FASTER/cs/test/ModifiedBitTests.cs

414 строки
15 KiB
C#

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using System;
using System.IO;
using FASTER.core;
using FASTER.test.LockTable;
using NUnit.Framework;
using static FASTER.test.TestUtils;
namespace FASTER.test.ModifiedBit
{
internal class ModifiedBitTestComparer : IFasterEqualityComparer<int>
{
public bool Equals(ref int k1, ref int k2) => k1 == k2;
public long GetHashCode64(ref int k) => Utility.GetHashCode(k);
}
[TestFixture]
class ModifiedBitTests
{
const int numRecords = 1000;
const int valueMult = 1_000_000;
ModifiedBitTestComparer comparer;
private FasterKV<int, int> fht;
private ClientSession<int, int, int, int, Empty, SimpleFunctions<int, int>> session;
private IDevice log;
[SetUp]
public void Setup()
{
log = Devices.CreateLogDevice(Path.Combine(MethodTestDir, "test.log"), deleteOnClose: false);
comparer = new ModifiedBitTestComparer();
fht = new FasterKV<int, int>(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 22 }, comparer: comparer, concurrencyControlMode: ConcurrencyControlMode.LockTable);
session = fht.For(new SimpleFunctions<int, int>()).NewSession<SimpleFunctions<int, int>>();
}
[TearDown]
public void TearDown()
{
session?.Dispose();
session = null;
fht?.Dispose();
fht = null;
log?.Dispose();
log = null;
}
void Populate()
{
for (int key = 0; key < numRecords; key++)
Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending);
}
void AssertLockandModified(LockableUnsafeContext<int, int, int, int, Empty, SimpleFunctions<int, int>> luContext, int key, bool xlock, bool slock, bool modified = false)
{
OverflowBucketLockTableTests.AssertLockCounts(fht, ref key, xlock, slock);
var isM = luContext.IsModified(key);
Assert.AreEqual(modified, isM, "modified mismatch");
}
void AssertLockandModified(LockableContext<int, int, int, int, Empty, SimpleFunctions<int, int>> luContext, int key, bool xlock, bool slock, bool modified = false)
{
OverflowBucketLockTableTests.AssertLockCounts(fht, ref key, xlock, slock);
var isM = luContext.IsModified(key);
Assert.AreEqual(modified, isM, "modified mismatch");
}
void AssertLockandModified(ClientSession<int, int, int, int, Empty, SimpleFunctions<int, int>> session, int key, bool xlock, bool slock, bool modified = false)
{
var luContext = session.LockableUnsafeContext;
luContext.BeginUnsafe();
OverflowBucketLockTableTests.AssertLockCounts(fht, ref key, xlock, slock);
var isM = luContext.IsModified(key);
Assert.AreEqual(modified, isM, "Modified mismatch");
luContext.EndUnsafe();
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void LockAndNotModify()
{
Populate();
Random r = new(100);
int key = r.Next(numRecords);
session.ResetModified(key);
var lContext = session.LockableContext;
lContext.BeginLockable();
AssertLockandModified(lContext, key, xlock: false, slock: false, modified: false);
var keyVec = new[] { new FixedLengthLockableKeyStruct<int>(key, LockType.Exclusive, lContext) };
lContext.Lock(keyVec);
AssertLockandModified(lContext, key, xlock: true, slock: false, modified: false);
lContext.Unlock(keyVec);
AssertLockandModified(lContext, key, xlock: false, slock: false, modified: false);
keyVec[0].LockType = LockType.Shared;
lContext.Lock(keyVec);
AssertLockandModified(lContext, key, xlock: false, slock: true, modified: false);
lContext.Unlock(keyVec);
AssertLockandModified(lContext, key, xlock: false, slock: false, modified: false);
lContext.EndLockable();
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void ResetModifyForNonExistingKey()
{
Populate();
int key = numRecords + 100;
session.ResetModified(key);
AssertLockandModified(session, key, xlock: false, slock: false, modified: false);
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void ModifyClientSession([Values(true, false)] bool flushToDisk, [Values] UpdateOp updateOp)
{
Populate();
int key = numRecords - 500;
int value = 14;
session.ResetModified(key);
AssertLockandModified(session, key, xlock: false, slock: false, modified: false);
if (flushToDisk)
this.fht.Log.FlushAndEvict(wait: true);
Status status = default;
switch (updateOp)
{
case UpdateOp.Upsert:
status = session.Upsert(key, value);
break;
case UpdateOp.RMW:
status = session.RMW(key, value);
break;
case UpdateOp.Delete:
status = session.Delete(key);
break;
default:
break;
}
if (flushToDisk)
{
switch (updateOp)
{
case UpdateOp.RMW:
Assert.IsTrue(status.IsPending, status.ToString());
session.CompletePending(wait: true);
break;
default:
Assert.IsTrue(status.NotFound);
break;
}
(status, var _) = session.Read(key);
Assert.IsTrue(status.Found || updateOp == UpdateOp.Delete);
}
if (updateOp == UpdateOp.Delete)
AssertLockandModified(session, key, xlock: false, slock: false, modified: false);
else
AssertLockandModified(session, key, xlock: false, slock: false, modified: true);
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void ModifyLUC([Values(true, false)] bool flushToDisk, [Values] UpdateOp updateOp)
{
Populate();
int key = numRecords - 500;
int value = 14;
session.ResetModified(key);
var luContext = session.LockableUnsafeContext;
luContext.BeginUnsafe();
luContext.BeginLockable();
AssertLockandModified(luContext, key, xlock: false, slock: false, modified: false);
luContext.EndLockable();
luContext.EndUnsafe();
if (flushToDisk)
this.fht.Log.FlushAndEvict(wait: true);
Status status = default;
luContext.BeginUnsafe();
luContext.BeginLockable();
var keyVec = new[] { new FixedLengthLockableKeyStruct<int>(key, LockType.Exclusive, luContext) };
luContext.Lock(keyVec);
switch (updateOp)
{
case UpdateOp.Upsert:
status = luContext.Upsert(key, value);
break;
case UpdateOp.RMW:
status = luContext.RMW(key, value);
break;
case UpdateOp.Delete:
status = luContext.Delete(key);
break;
default:
break;
}
if (flushToDisk)
{
switch (updateOp)
{
case UpdateOp.RMW:
Assert.IsTrue(status.IsPending, status.ToString());
luContext.CompletePending(wait: true);
break;
default:
Assert.IsTrue(status.NotFound);
break;
}
}
luContext.Unlock(keyVec);
if (flushToDisk)
{
keyVec[0].LockType = LockType.Shared;
luContext.Lock(keyVec);
(status, var _) = luContext.Read(key);
Assert.AreEqual(updateOp != UpdateOp.Delete, status.Found, status.ToString());
luContext.Unlock(keyVec);
}
AssertLockandModified(luContext, key, xlock: false, slock: false, modified: updateOp != UpdateOp.Delete);
luContext.EndLockable();
luContext.EndUnsafe();
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void ModifyUC([Values(true, false)] bool flushToDisk, [Values] UpdateOp updateOp)
{
Populate();
int key = numRecords - 500;
int value = 14;
session.ResetModified(key);
AssertLockandModified(session, key, xlock: false, slock: false, modified: false);
if (flushToDisk)
this.fht.Log.FlushAndEvict(wait: true);
Status status = default;
var unsafeContext = session.UnsafeContext;
unsafeContext.BeginUnsafe();
switch (updateOp)
{
case UpdateOp.Upsert:
status = unsafeContext.Upsert(key, value);
break;
case UpdateOp.RMW:
status = unsafeContext.RMW(key, value);
break;
case UpdateOp.Delete:
status = unsafeContext.Delete(key);
break;
default:
break;
}
if (flushToDisk)
{
switch (updateOp)
{
case UpdateOp.RMW:
Assert.IsTrue(status.IsPending, status.ToString());
unsafeContext.CompletePending(wait: true);
break;
default:
Assert.IsTrue(status.NotFound);
break;
}
(status, var _) = unsafeContext.Read(key);
Assert.IsTrue(status.Found || updateOp == UpdateOp.Delete);
}
unsafeContext.EndUnsafe();
AssertLockandModified(session, key, xlock: false, slock: false, modified: updateOp != UpdateOp.Delete);
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void ModifyLC([Values(true, false)] bool flushToDisk, [Values] UpdateOp updateOp)
{
Populate();
int key = numRecords - 500;
int value = 14;
session.ResetModified(key);
var lContext = session.LockableContext;
lContext.BeginLockable();
AssertLockandModified(lContext, key, xlock: false, slock: false, modified: false);
var keyVec = new[] { new FixedLengthLockableKeyStruct<int>(key, LockType.Exclusive, lContext) };
lContext.Lock(keyVec);
if (flushToDisk)
this.fht.Log.FlushAndEvict(wait: true);
Status status = default;
switch (updateOp)
{
case UpdateOp.Upsert:
status = lContext.Upsert(key, value);
break;
case UpdateOp.RMW:
status = lContext.RMW(key, value);
break;
case UpdateOp.Delete:
status = lContext.Delete(key);
break;
default:
break;
}
if (flushToDisk)
{
switch (updateOp)
{
case UpdateOp.RMW:
Assert.IsTrue(status.IsPending, status.ToString());
lContext.CompletePending(wait: true);
break;
default:
Assert.IsTrue(status.NotFound);
break;
}
}
lContext.Unlock(keyVec);
if (flushToDisk)
{
keyVec[0].LockType = LockType.Shared;
lContext.Lock(keyVec);
(status, var _) = lContext.Read(key);
Assert.AreEqual(updateOp != UpdateOp.Delete, status.Found, status.ToString());
lContext.Unlock(keyVec);
}
AssertLockandModified(lContext, key, xlock: false, slock: false, modified: updateOp != UpdateOp.Delete);
lContext.EndLockable();
}
[Test]
[Category(ModifiedBitTestCategory), Category(SmokeTestCategory)]
public void CopyToTailTest()
{
Populate();
fht.Log.FlushAndEvict(wait: true);
var luContext = session.LockableUnsafeContext;
int input = 0, output = 0, key = 200;
ReadOptions readOptions = new() { CopyOptions = new(ReadCopyFrom.AllImmutable, ReadCopyTo.MainLog) };
luContext.BeginUnsafe();
luContext.BeginLockable();
AssertLockandModified(luContext, key, xlock: false, slock: false, modified: true);
var keyVec = new[] { new FixedLengthLockableKeyStruct<int>(key, LockType.Shared, luContext) };
luContext.Lock(keyVec);
AssertLockandModified(luContext, key, xlock: false, slock: true, modified: true);
// Check Read Copy to Tail resets the modified
var status = luContext.Read(ref key, ref input, ref output, ref readOptions, out _);
Assert.IsTrue(status.IsPending, status.ToString());
luContext.CompletePending(wait: true);
luContext.Unlock(keyVec);
AssertLockandModified(luContext, key, xlock: false, slock: false, modified: true);
// Check Read Copy to Tail resets the modified on locked key
key += 10;
keyVec[0] = new(key, LockType.Exclusive, luContext);
luContext.Lock(keyVec);
status = luContext.Read(ref key, ref input, ref output, ref readOptions, out _);
Assert.IsTrue(status.IsPending, status.ToString());
luContext.CompletePending(wait: true);
AssertLockandModified(luContext, key, xlock: true, slock: false, modified: true);
luContext.Unlock(keyVec);
AssertLockandModified(luContext, key, xlock: false, slock: false, modified: true);
luContext.EndLockable();
luContext.EndUnsafe();
}
}
}