From 0acff38b7f343cf26c58a20995b2675f8f42b634 Mon Sep 17 00:00:00 2001 From: Ted Hart <15467143+TedHartMS@users.noreply.github.com> Date: Thu, 6 Jun 2024 14:21:46 -0700 Subject: [PATCH] - Remove ConcurrencyControlMode.NoLock. Oh, and while I'm at it, just remove ConcurrencyControlMode. (#448) - Clean up ILockTable, OverflowBucketLockTable, InternalLock.cs, and LockableContext.cs to streamline the locking calls and clarify the current use of Transient vs. Manual Co-authored-by: Badrish Chandramouli --- .../cs/benchmark/FixedLenYcsbBenchmark.cs | 4 +- .../storage/Tsavorite/cs/benchmark/Options.cs | 8 +- .../cs/benchmark/SpanByteYcsbBenchmark.cs | 4 +- .../src/core/ClientSession/ClientSession.cs | 31 +- .../core/ClientSession/ILockableContext.cs | 6 - .../src/core/ClientSession/LockableContext.cs | 73 +- .../ClientSession/LockableUnsafeContext.cs | 11 +- .../ClientSession/SessionFunctionsWrapper.cs | 8 +- .../core/Index/Common/TsavoriteKVSettings.cs | 7 +- .../Index/Interfaces/ISessionFunctions.cs | 24 +- .../core/Index/Interfaces/ISessionLocker.cs | 72 +- .../Implementation/InternalDelete.cs | 2 +- .../Tsavorite/Implementation/InternalLock.cs | 102 +-- .../Tsavorite/Implementation/InternalRMW.cs | 76 +- .../Tsavorite/Implementation/InternalRead.cs | 6 +- .../Implementation/InternalUpsert.cs | 58 +- .../Implementation/Locking/ILockTable.cs | 61 +- .../Locking/OverflowBucketLockTable.cs | 144 +--- .../Locking/TransientLocking.cs | 20 +- .../Revivification/RevivificationManager.cs | 2 - .../cs/src/core/Index/Tsavorite/Tsavorite.cs | 9 +- .../core/Index/Tsavorite/TsavoriteIterator.cs | 2 +- .../cs/src/core/Utilities/LockType.cs | 16 - .../Tsavorite/cs/test/AdvancedLockTests.cs | 346 -------- .../Tsavorite/cs/test/BasicLockTests.cs | 2 +- .../cs/test/BlittableIterationTests.cs | 8 +- .../cs/test/BlittableLogCompactionTests.cs | 21 +- .../cs/test/BlittableLogScanTests.cs | 2 +- .../storage/Tsavorite/cs/test/DisposeTests.cs | 761 ------------------ .../cs/test/GenericDiskDeleteTests.cs | 3 +- .../cs/test/GenericIterationTests.cs | 13 +- .../Tsavorite/cs/test/GenericLogScanTests.cs | 11 +- .../cs/test/LockableUnsafeContextTests.cs | 7 +- libs/storage/Tsavorite/cs/test/MiscTests.cs | 6 +- .../Tsavorite/cs/test/ModifiedBitTests.cs | 2 +- .../cs/test/OverflowBucketLockTableTests.cs | 54 +- .../Tsavorite/cs/test/ReadAddressTests.cs | 77 +- .../Tsavorite/cs/test/ReadCacheChainTests.cs | 40 +- .../Tsavorite/cs/test/RecoveryChecks.cs | 4 +- .../Tsavorite/cs/test/ReproReadCacheTest.cs | 11 +- .../Tsavorite/cs/test/RevivificationTests.cs | 128 +-- .../cs/test/SpanByteIterationTests.cs | 3 +- .../Tsavorite/cs/test/SpanByteLogScanTests.cs | 4 +- .../Tsavorite/cs/test/SpanByteTests.cs | 3 +- 44 files changed, 348 insertions(+), 1904 deletions(-) delete mode 100644 libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs delete mode 100644 libs/storage/Tsavorite/cs/test/DisposeTests.cs diff --git a/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs b/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs index 99affa3475..40d4a0662f 100644 --- a/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs +++ b/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs @@ -79,11 +79,11 @@ internal Tsavorite_YcsbBenchmark(Key[] i_keys_, Key[] t_keys_, TestLoader testLo if (testLoader.Options.UseSmallMemoryLog) store = new TsavoriteKV (testLoader.GetHashTableSize(), new LogSettings { LogDevice = device, PreallocateLog = true, PageSizeBits = 25, SegmentSizeBits = 30, MemorySizeBits = 28 }, - new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, concurrencyControlMode: testLoader.Options.ConcurrencyControlMode, revivificationSettings: revivificationSettings); + new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, revivificationSettings: revivificationSettings); else store = new TsavoriteKV (testLoader.GetHashTableSize(), new LogSettings { LogDevice = device, PreallocateLog = true }, - new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, concurrencyControlMode: testLoader.Options.ConcurrencyControlMode, revivificationSettings: revivificationSettings); + new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, revivificationSettings: revivificationSettings); } internal void Dispose() diff --git a/libs/storage/Tsavorite/cs/benchmark/Options.cs b/libs/storage/Tsavorite/cs/benchmark/Options.cs index 70ecc2270d..375e2b6a54 100644 --- a/libs/storage/Tsavorite/cs/benchmark/Options.cs +++ b/libs/storage/Tsavorite/cs/benchmark/Options.cs @@ -33,12 +33,6 @@ class Options "\n (Checkpoints are stored in directories under " + TestLoader.DataPath + " in directories named by distribution, ycsb vs. synthetic data, and key counts)")] public bool BackupAndRestore { get; set; } - [Option('z', "locking", Required = false, Default = ConcurrencyControlMode.None, - HelpText = "Locking Implementation:" + - $"\n {nameof(ConcurrencyControlMode.None)} = No Locking (default)" + - $"\n {nameof(ConcurrencyControlMode.LockTable)} = Locking using main HashTable buckets")] - public ConcurrencyControlMode ConcurrencyControlMode { get; set; } - [Option('i', "iterations", Required = false, Default = 1, HelpText = "Number of iterations of the test to run")] public int IterationCount { get; set; } @@ -118,7 +112,7 @@ public string GetOptionsString() { static string boolStr(bool value) => value ? "y" : "n"; return $"b: {Benchmark}; d: {DistributionName.ToLower()}; n: {NumaStyle}; rumd: {string.Join(',', RumdPercents)}; reviv: {RevivificationLevel}; revivbinrecs: {RevivBinRecordCount};" - + $" revivfrac {RevivifiableFraction}; t: {ThreadCount}; z: {ConcurrencyControlMode}; i: {IterationCount}; hp: {HashPacking};" + + $" revivfrac {RevivifiableFraction}; t: {ThreadCount}; i: {IterationCount}; hp: {HashPacking};" + $" sd: {boolStr(UseSmallData)}; sm: {boolStr(UseSmallMemoryLog)}; sy: {boolStr(UseSyntheticData)}; safectx: {boolStr(UseSafeContext)};" + $" chkptms: {PeriodicCheckpointMilliseconds}; chkpttype: {(PeriodicCheckpointMilliseconds > 0 ? PeriodicCheckpointType.ToString() : "None")};" + $" chkptincr: {boolStr(PeriodicCheckpointTryIncremental)}"; diff --git a/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs b/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs index f79b6bc4b3..f56a423f81 100644 --- a/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs +++ b/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs @@ -88,11 +88,11 @@ internal SpanByteYcsbBenchmark(KeySpanByte[] i_keys_, KeySpanByte[] t_keys_, Tes if (testLoader.Options.UseSmallMemoryLog) store = new TsavoriteKV (testLoader.GetHashTableSize(), new LogSettings { LogDevice = device, PreallocateLog = true, PageSizeBits = 22, SegmentSizeBits = 26, MemorySizeBits = 26 }, - new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, concurrencyControlMode: testLoader.Options.ConcurrencyControlMode, revivificationSettings: revivificationSettings); + new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, revivificationSettings: revivificationSettings); else store = new TsavoriteKV (testLoader.GetHashTableSize(), new LogSettings { LogDevice = device, PreallocateLog = true, MemorySizeBits = 35 }, - new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, concurrencyControlMode: testLoader.Options.ConcurrencyControlMode, revivificationSettings: revivificationSettings); + new CheckpointSettings { CheckpointDir = testLoader.BackupPath }, revivificationSettings: revivificationSettings); } internal void Dispose() diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs index d3a2311cac..2d8133fb76 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs @@ -102,12 +102,8 @@ internal ClientSession( { bContext = new(this); uContext = new(this); - - if (store.LockTable.IsEnabled) - { - lContext = new(this); - luContext = new(this); - } + lContext = new(this); + luContext = new(this); this.loggerFactory = loggerFactory; logger = loggerFactory?.CreateLogger($"ClientSession-{GetHashCode():X8}"); @@ -146,28 +142,12 @@ public void Dispose() /// /// Return a new interface to Tsavorite operations that supports manual locking and epoch control. /// - public LockableUnsafeContext LockableUnsafeContext - { - get - { - if (!store.LockTable.IsEnabled) - throw new TsavoriteException($"LockableUnsafeContext requires {nameof(ConcurrencyControlMode.LockTable)}"); - return luContext; - } - } + public LockableUnsafeContext LockableUnsafeContext => luContext; /// /// Return a session wrapper that supports manual locking. /// - public LockableContext LockableContext - { - get - { - if (!store.LockTable.IsEnabled) - throw new TsavoriteException($"LockableContext requires {nameof(ConcurrencyControlMode.LockTable)}"); - return lContext; - } - } + public LockableContext LockableContext => lContext; /// /// Return a session wrapper struct that passes through to client session @@ -212,9 +192,6 @@ internal void ResetModified(TSessionFunctionsWrapper s } } - /// - public bool NeedKeyHash => store.LockTable.IsEnabled && store.LockTable.NeedKeyHash; - /// public int CompareKeyHashes(TLockableKey key1, TLockableKey key2) where TLockableKey : ILockableKey => store.LockTable.CompareKeyHashes(key1, key2); diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/ILockableContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/ILockableContext.cs index 0eaedfe123..07691f2ed8 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/ILockableContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/ILockableContext.cs @@ -22,12 +22,6 @@ public interface ILockableContext /// void EndLockable(); - /// - /// If true, then keys must use one of the overloads to obtain a code by which groups of keys will be sorted for manual locking, to avoid deadlocks. - /// - /// Whether this returns true depends on the on , or passed to the TsavoriteKV constructor. - bool NeedKeyHash { get; } - /// /// Compare two structures that implement ILockableKey. /// diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs index 3394264c98..61d2b5fa48 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs @@ -41,9 +41,6 @@ internal LockableContext(ClientSession - public bool NeedKeyHash => clientSession.NeedKeyHash; - /// public int CompareKeyHashes(TLockableKey key1, TLockableKey key2) where TLockableKey : ILockableKey => clientSession.CompareKeyHashes(ref key1, ref key2); @@ -57,14 +54,14 @@ internal LockableContext(ClientSession(TLockableKey[] keys, int start, int count) where TLockableKey : ILockableKey => clientSession.SortKeyHashes(keys, start, count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool DoInternalLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, + internal static bool DoManualLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, TLockableKey[] keys, int start, int count) where TSessionFunctionsWrapper : ISessionFunctionsWrapper where TLockableKey : ILockableKey { // The key codes are sorted, but there may be duplicates; the sorting is such that exclusive locks come first for each key code, // which of course allows the session to do shared operations as well, so we take the first occurrence of each key code. - // This is the same as DoInternalTryLock but without timeout; it will keep trying until it acquires all locks. + // This is the same as DoManualTryLock but without timeout; it will keep trying until it acquires all locks or the hardcoded retry limit is reached. var end = start + count - 1; int retryCount = 0; @@ -78,20 +75,18 @@ internal static bool DoInternalLock(TSes if (currBucketIndex != prevBucketIndex) { prevBucketIndex = currBucketIndex; - OperationStatus status = DoInternalLock(clientSession, key); + OperationStatus status = DoManualLock(clientSession, key); if (status == OperationStatus.SUCCESS) continue; // Success; continue to the next key. // Lock failure before we've completed all keys, and we did not lock the current key. Unlock anything we've locked. - DoInternalUnlock(clientSession, keys, start, keyIdx - 1); + DoManualUnlock(clientSession, keys, start, keyIdx - 1); // We've released our locks so this refresh will let other threads advance and release their locks, and we will retry with a full timeout. clientSession.store.HandleImmediateNonPendingRetryStatus(status, sessionFunctions); retryCount++; if (retryCount >= KeyLockMaxRetryAttempts) - { return false; - } goto Retry; } } @@ -101,14 +96,14 @@ internal static bool DoInternalLock(TSes } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool DoInternalTryLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, + internal static bool DoManualTryLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, TLockableKey[] keys, int start, int count, TimeSpan timeout, CancellationToken cancellationToken) where TSessionFunctionsWrapper : ISessionFunctionsWrapper where TLockableKey : ILockableKey { // The key codes are sorted, but there may be duplicates; the sorting is such that exclusive locks come first for each key code, // which of course allows the session to do shared operations as well, so we take the first occurrence of each key code. - // This is the same as DoInternalLock but with timeout. + // This is the same as DoManualLock but with timeout. var end = start + count - 1; // We can't start each retry with a full timeout because we might always fail if someone is not unlocking (e.g. another thread hangs @@ -132,13 +127,13 @@ internal static bool DoInternalTryLock(T status = OperationStatus.CANCELED; else { - status = DoInternalLock(clientSession, key); + status = DoManualLock(clientSession, key); if (status == OperationStatus.SUCCESS) continue; // Success; continue to the next key. } // Cancellation or lock failure before we've completed all keys; we have not locked the current key. Unlock anything we've locked. - DoInternalUnlock(clientSession, keys, start, keyIdx - 1); + DoManualUnlock(clientSession, keys, start, keyIdx - 1); // Lock failure is the only place we check the timeout. If we've exceeded that, or if we've had a cancellation, return false. if (cancellationToken.IsCancellationRequested || DateTime.UtcNow.Ticks - startTime.Ticks > timeout.Ticks) @@ -156,7 +151,7 @@ internal static bool DoInternalTryLock(T } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool DoInternalTryPromoteLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, + internal static bool DoManualTryPromoteLock(TSessionFunctionsWrapper sessionFunctions, ClientSession clientSession, TLockableKey key, TimeSpan timeout, CancellationToken cancellationToken) where TSessionFunctionsWrapper : ISessionFunctionsWrapper where TLockableKey : ILockableKey @@ -164,8 +159,7 @@ internal static bool DoInternalTryPromoteLock timeout.Ticks) break; // out of the retry loop - clientSession.store.HandleImmediateNonPendingRetryStatus(status, sessionFunctions); + // Lock failed, must retry + clientSession.store.HandleImmediateNonPendingRetryStatus(OperationStatus.RETRY_LATER, sessionFunctions); } // Failed to promote @@ -187,22 +182,26 @@ internal static bool DoInternalTryPromoteLock(ClientSession clientSession, TLockableKey key) + internal static OperationStatus DoManualLock(ClientSession clientSession, TLockableKey key) where TLockableKey : ILockableKey { - OperationStatus status = clientSession.store.InternalLock(key.KeyHash, key.LockType); - if (status == OperationStatus.SUCCESS) + if (key.LockType == LockType.Shared) { - if (key.LockType == LockType.Exclusive) - ++clientSession.exclusiveLockCount; - else if (key.LockType == LockType.Shared) - ++clientSession.sharedLockCount; + if (!clientSession.store.InternalTryLockShared(key.KeyHash)) + return OperationStatus.RETRY_LATER; + ++clientSession.sharedLockCount; + } + else + { + if (!clientSession.store.InternalTryLockExclusive(key.KeyHash)) + return OperationStatus.RETRY_LATER; + ++clientSession.exclusiveLockCount; } - return status; + return OperationStatus.SUCCESS; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static void DoInternalUnlock(ClientSession clientSession, + internal static void DoManualUnlock(ClientSession clientSession, TLockableKey[] keys, int start, int keyIdx) where TLockableKey : ILockableKey { @@ -213,15 +212,21 @@ internal static void DoInternalUnlock(ClientSession public void Lock(TLockableKey[] keys) where TLockableKey : ILockableKey => Lock(keys, 0, keys.Length); @@ -237,7 +242,7 @@ public void Lock(TLockableKey[] keys, int start, int count) clientSession.UnsafeResumeThread(sessionFunctions); try { - lockAquired = DoInternalLock(sessionFunctions, clientSession, keys, start, count); + lockAquired = DoManualLock(sessionFunctions, clientSession, keys, start, count); } finally { @@ -286,7 +291,7 @@ public bool TryLock(TLockableKey[] keys, int start, int count, Tim clientSession.UnsafeResumeThread(sessionFunctions); try { - return DoInternalTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); + return DoManualTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); } finally { @@ -319,7 +324,7 @@ public bool TryPromoteLock(TLockableKey key, TimeSpan timeout, Can clientSession.UnsafeResumeThread(sessionFunctions); try { - return DoInternalTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); + return DoManualTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); } finally { @@ -340,7 +345,7 @@ public void Unlock(TLockableKey[] keys, int start, int count) clientSession.UnsafeResumeThread(sessionFunctions); try { - DoInternalUnlock(clientSession, keys, start, start + count - 1); + DoManualUnlock(clientSession, keys, start, start + count - 1); } finally { diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs index ef3dc63993..8840616446 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs @@ -50,9 +50,6 @@ internal LockableUnsafeContext(ClientSession - public bool NeedKeyHash => clientSession.NeedKeyHash; - /// public int CompareKeyHashes(TLockableKey key1, TLockableKey key2) where TLockableKey : ILockableKey => clientSession.CompareKeyHashes(key1, key2); @@ -76,7 +73,7 @@ public void Lock(TLockableKey[] keys, int start, int count) Debug.Assert(clientSession.store.epoch.ThisInstanceProtected(), "Epoch protection required for LockableUnsafeContext.Lock()"); while (true) { - if (LockableContext.DoInternalLock(sessionFunctions, clientSession, keys, start, count)) + if (LockableContext.DoManualLock(sessionFunctions, clientSession, keys, start, count)) { break; } @@ -123,7 +120,7 @@ public bool TryLock(TLockableKey[] keys, int start, int count, Tim clientSession.CheckIsAcquiredLockable(); Debug.Assert(clientSession.store.epoch.ThisInstanceProtected(), "Epoch protection required for LockableUnsafeContext.Lock()"); - return LockableContext.DoInternalTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); + return LockableContext.DoManualTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); } /// @@ -148,7 +145,7 @@ public bool TryPromoteLock(TLockableKey key, TimeSpan timeout, Can clientSession.CheckIsAcquiredLockable(); Debug.Assert(clientSession.store.epoch.ThisInstanceProtected(), "Epoch protection required for LockableUnsafeContext.Lock()"); - return LockableContext.DoInternalTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); + return LockableContext.DoManualTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); } /// @@ -161,7 +158,7 @@ public void Unlock(TLockableKey[] keys, int start, int count) clientSession.CheckIsAcquiredLockable(); Debug.Assert(clientSession.store.epoch.ThisInstanceProtected(), "Epoch protection required for LockableUnsafeContext.Unlock()"); - LockableContext.DoInternalUnlock(clientSession, keys, start, start + count - 1); + LockableContext.DoManualUnlock(clientSession, keys, start, start + count - 1); } /// diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs index d9fac2751a..e524adb248 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs @@ -161,19 +161,19 @@ public void DisposeForRevivification(ref Key key, ref Value value, int newKeySiz [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) => - _sessionLocker.TryLockTransientExclusive(Store, ref key, ref stackCtx); + _sessionLocker.TryLockTransientExclusive(Store, ref stackCtx); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx) - => _sessionLocker.TryLockTransientShared(Store, ref key, ref stackCtx); + => _sessionLocker.TryLockTransientShared(Store, ref stackCtx); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) - => _sessionLocker.UnlockTransientExclusive(Store, ref key, ref stackCtx); + => _sessionLocker.UnlockTransientExclusive(Store, ref stackCtx); [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx) - => _sessionLocker.UnlockTransientShared(Store, ref key, ref stackCtx); + => _sessionLocker.UnlockTransientShared(Store, ref stackCtx); #endregion Transient locking #region Internal utilities diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Common/TsavoriteKVSettings.cs b/libs/storage/Tsavorite/cs/src/core/Index/Common/TsavoriteKVSettings.cs index 53f4d9ad0e..6a037372c3 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Common/TsavoriteKVSettings.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Common/TsavoriteKVSettings.cs @@ -21,11 +21,6 @@ public sealed class TsavoriteKVSettings : IDisposable /// public long IndexSize = 1L << 26; - /// - /// How Tsavorite should do record locking - /// - public ConcurrencyControlMode ConcurrencyControlMode; - /// /// Device used for main hybrid log /// @@ -191,7 +186,7 @@ public override string ToString() var retStr = $"index: {Utility.PrettySize(IndexSize)}; log memory: {Utility.PrettySize(MemorySize)}; log page: {Utility.PrettySize(PageSize)}; log segment: {Utility.PrettySize(SegmentSize)}"; retStr += $"; log device: {(LogDevice == null ? "null" : LogDevice.GetType().Name)}"; retStr += $"; obj log device: {(ObjectLogDevice == null ? "null" : ObjectLogDevice.GetType().Name)}"; - retStr += $"; mutable fraction: {MutableFraction}; locking mode: {ConcurrencyControlMode}"; + retStr += $"; mutable fraction: {MutableFraction};"; retStr += $"; read cache (rc): {(ReadCacheEnabled ? "yes" : "no")}"; retStr += $"; read copy options: {ReadCopyOptions}"; if (ReadCacheEnabled) diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs index a28bdbba0a..a9c0f0f072 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs @@ -33,8 +33,7 @@ public interface ISessionFunctions /// The value for the record being read /// The location where is to be copied /// Information about this read operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the value was available, else false (e.g. the value was expired) bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo); @@ -61,8 +60,7 @@ public interface ISessionFunctions /// The location where the result of the update may be placed /// Information about this update operation and its context /// The operation for which this write is being done - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the write was performed, else false (e.g. cancellation) bool SingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo); @@ -87,8 +85,7 @@ public interface ISessionFunctions /// The location where is to be copied; because this method is called only for in-place updates, there is a previous value there. /// The location where the result of the update may be placed /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the value was written, else false /// If the value is shrunk in-place, the caller must first zero the data that is no longer used, to ensure log-scan correctness. bool ConcurrentWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo); @@ -113,8 +110,7 @@ public interface ISessionFunctions /// The destination to be updated; because this is an insert, there is no previous value there. /// The location where the result of the operation on is to be copied /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the write was performed, else false (e.g. cancellation) bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); @@ -149,8 +145,7 @@ public interface ISessionFunctions /// The destination to be updated; because this is an copy to a new location, there is no previous value there. /// The location where is to be copied /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the write was performed, else false (e.g. cancellation) bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); @@ -178,8 +173,7 @@ public interface ISessionFunctions /// The destination to be updated; because this is an in-place update, there is a previous value there. /// The location where the result of the operation on is to be copied /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// True if the value was successfully updated, else false (e.g. the value was expired) /// If the value is shrunk in-place, the caller must first zero the data that is no longer used, to ensure log-scan correctness. bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); @@ -216,8 +210,7 @@ public interface ISessionFunctions /// The key for the record to be deleted /// The value for the record being deleted; because this method is called only for in-place updates, there is a previous value there. Usually this is ignored or assigned 'default'. /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// For Object Value types, Dispose() can be called here. If recordInfo.Invalid is true, this is called after the record was allocated and populated, but could not be appended at the end of the log. /// True if the deleted record should be added, else false (e.g. cancellation) bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); @@ -237,8 +230,7 @@ public interface ISessionFunctions /// The key for the record to be deleted /// The value for the record being deleted; because this method is called only for in-place updates, there is a previous value there. Usually this is ignored or assigned 'default'. /// Information about this update operation and its context - /// A reference to the RecordInfo for the record; used for locking if - /// is used, or for variable-length record length modification + /// A reference to the RecordInfo for the record; used for variable-length record length modification /// For Object Value types, Dispose() can be called here. If recordInfo.Invalid is true, this is called after the record was allocated and populated, but could not be appended at the end of the log. /// True if the value was successfully deleted, else false (e.g. the record was sealed) bool ConcurrentDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs index e13ffe361f..5164042814 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs @@ -13,10 +13,10 @@ public interface ISessionLocker { bool IsManualLocking { get; } - bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx); - bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx); - void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx); - void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx); + bool TryLockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx); + bool TryLockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx); + void UnlockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx); + void UnlockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx); } /// @@ -29,39 +29,31 @@ internal struct BasicSessionLocker : ISessionLocker { public bool IsManualLocking => false; - public bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public bool TryLockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx) { - if (!store.LockTable.IsEnabled) - return true; - if (!store.LockTable.TryLockTransientExclusive(ref key, ref stackCtx.hei)) + if (!store.LockTable.TryLockExclusive(ref stackCtx.hei)) return false; stackCtx.recSrc.SetHasTransientXLock(); return true; } - public bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public bool TryLockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx) { - if (!store.LockTable.IsEnabled) - return true; - if (!store.LockTable.TryLockTransientShared(ref key, ref stackCtx.hei)) + if (!store.LockTable.TryLockShared(ref stackCtx.hei)) return false; stackCtx.recSrc.SetHasTransientSLock(); return true; } - public void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public void UnlockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx) { - if (!store.LockTable.IsEnabled) - return; - store.LockTable.UnlockExclusive(ref key, ref stackCtx.hei); + store.LockTable.UnlockExclusive(ref stackCtx.hei); stackCtx.recSrc.ClearHasTransientXLock(); } - public void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public void UnlockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx) { - if (!store.LockTable.IsEnabled) - return; - store.LockTable.UnlockShared(ref key, ref stackCtx.hei); + store.LockTable.UnlockShared(ref stackCtx.hei); stackCtx.recSrc.ClearHasTransientSLock(); } } @@ -73,38 +65,46 @@ internal struct LockableSessionLocker : ISessionLocker true; - public bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public bool TryLockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx) { - Debug.Assert(store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei), + Debug.Assert(store.LockTable.IsLockedExclusive(ref stackCtx.hei), $"Attempting to use a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); + + $" XLocked {store.LockTable.IsLockedExclusive(ref stackCtx.hei)}," + + $" Slocked {store.LockTable.IsLockedShared(ref stackCtx.hei)}"); return true; } - public bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) => throw new System.NotImplementedException(); + + public bool TryLockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx) { - Debug.Assert(store.LockTable.IsLocked(ref key, ref stackCtx.hei), + Debug.Assert(store.LockTable.IsLocked(ref stackCtx.hei), $"Attempting to use a non-Locked (S or X) key in a Lockable context (requesting SLock):" - + $" XLocked {store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); + + $" XLocked {store.LockTable.IsLockedExclusive(ref stackCtx.hei)}," + + $" Slocked {store.LockTable.IsLockedShared(ref stackCtx.hei)}"); return true; } - public void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) => throw new System.NotImplementedException(); + + public void UnlockTransientExclusive(TsavoriteKV store, ref OperationStackContext stackCtx) { - Debug.Assert(store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei), + Debug.Assert(store.LockTable.IsLockedExclusive(ref stackCtx.hei), $"Attempting to unlock a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); + + $" XLocked {store.LockTable.IsLockedExclusive(ref stackCtx.hei)}," + + $" Slocked {store.LockTable.IsLockedShared(ref stackCtx.hei)}"); } - public void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + public void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) => throw new System.NotImplementedException(); + + public void UnlockTransientShared(TsavoriteKV store, ref OperationStackContext stackCtx) { - Debug.Assert(store.LockTable.IsLockedShared(ref key, ref stackCtx.hei), + Debug.Assert(store.LockTable.IsLockedShared(ref stackCtx.hei), $"Attempting to use a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); + + $" XLocked {store.LockTable.IsLockedExclusive(ref stackCtx.hei)}," + + $" Slocked {store.LockTable.IsLockedShared(ref stackCtx.hei)}"); } + + public void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) => throw new System.NotImplementedException(); } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalDelete.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalDelete.cs index 756bff641f..425f6450ac 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalDelete.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalDelete.cs @@ -184,7 +184,7 @@ internal OperationStatus InternalDelete : TsavoriteBase { - /// - /// Manual Lock operation for key-based locking. Locks the record corresponding to 'key'. - /// - /// key of the record. - /// Whether the lock is shared or exclusive [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal OperationStatus InternalLock(ref Key key, LockType lockType) + internal bool InternalTryLockShared(long keyHash) { - Debug.Assert(epoch.ThisInstanceProtected(), "InternalLock must have protected epoch"); - Debug.Assert(LockTable.IsEnabled, "ManualLockTable must be enabled for InternalLock"); - - OperationStackContext stackCtx = new(comparer.GetHashCode64(ref key)); - FindTag(ref stackCtx.hei); - stackCtx.SetRecordSourceToHashEntry(hlog); - - if (!LockTable.TryLockManual(ref key, ref stackCtx.hei, lockType)) - return OperationStatus.RETRY_LATER; - return OperationStatus.SUCCESS; + HashEntryInfo hei = new(keyHash); + FindTag(ref hei); + return InternalTryLockShared(ref hei); } - /// - /// Manual Lock operation for key-based locking. Unlocks the record corresponding to 'key'. - /// - /// key of the record. - /// Whether the lock is shared or exclusive [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void InternalUnlock(ref Key key, LockType lockType) + internal bool InternalTryLockShared(ref HashEntryInfo hei) { - Debug.Assert(epoch.ThisInstanceProtected(), "InternalLock must have protected epoch"); - Debug.Assert(LockTable.IsEnabled, "ManualLockTable must be enabled for InternalLock"); + Debug.Assert(epoch.ThisInstanceProtected(), "InternalLockShared must have protected epoch"); + return LockTable.TryLockShared(ref hei); + } - OperationStackContext stackCtx = new(comparer.GetHashCode64(ref key)); - FindTag(ref stackCtx.hei); - stackCtx.SetRecordSourceToHashEntry(hlog); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool InternalTryLockExclusive(long keyHash) + { + HashEntryInfo hei = new(keyHash); + FindTag(ref hei); + return InternalTryLockExclusive(ref hei); + } - LockTable.Unlock(ref key, ref stackCtx.hei, lockType); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal bool InternalTryLockExclusive(ref HashEntryInfo hei) + { + Debug.Assert(epoch.ThisInstanceProtected(), "InternalLockExclusive must have protected epoch"); + return LockTable.TryLockExclusive(ref hei); } - /// - /// Manual Lock operation for locking. Locks the buckets corresponding to 'keys'. - /// - /// Hash code of the key to be locked or unlocked. - /// Whether the lock is shared or exclusive [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal OperationStatus InternalLock(long keyHash, LockType lockType) + internal void InternalUnlockShared(long keyHash) { - Debug.Assert(epoch.ThisInstanceProtected(), "InternalLock must have protected epoch"); - Debug.Assert(LockTable.IsEnabled, "ManualLockTable must be enabled for InternalLock"); + HashEntryInfo hei = new(keyHash); + FindTag(ref hei); + InternalUnlockShared(ref hei); + } - if (!LockTable.TryLockManual(keyHash, lockType)) - return OperationStatus.RETRY_LATER; - return OperationStatus.SUCCESS; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void InternalUnlockShared(ref HashEntryInfo hei) + { + Debug.Assert(epoch.ThisInstanceProtected(), "InternalUnlockShared must have protected epoch"); + LockTable.UnlockShared(ref hei); } - /// - /// Manual Lock operation for locking. Locks the buckets corresponding to 'keys'. - /// - /// Hash code of the key to be locked or unlocked. - /// Whether the lock is shared or exclusive [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void InternalUnlock(long keyHash, LockType lockType) + internal void InternalUnlockExclusive(long keyHash) { - Debug.Assert(epoch.ThisInstanceProtected(), "InternalLock must have protected epoch"); - Debug.Assert(LockTable.IsEnabled, "ManualLockTable must be enabled for InternalLock"); + HashEntryInfo hei = new(keyHash); + FindTag(ref hei); + InternalUnlockExclusive(ref hei); + } - LockTable.Unlock(keyHash, lockType); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal void InternalUnlockExclusive(ref HashEntryInfo hei) + { + Debug.Assert(epoch.ThisInstanceProtected(), "InternalUnlockExclusive must have protected epoch"); + LockTable.UnlockExclusive(ref hei); } - /// - /// Manual Lock promotion for locking. Promotes the lock for 'key' from Shared to Exclusive. - /// - /// Hash code of the key to be locked or unlocked. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal OperationStatus InternalPromoteLock(long keyHash) + internal bool InternalPromoteLock(long keyHash) { Debug.Assert(epoch.ThisInstanceProtected(), "InternalLock must have protected epoch"); - Debug.Assert(LockTable.IsEnabled, "ManualLockTable must be enabled for InternalLock"); - - if (!LockTable.TryPromoteLockManual(keyHash)) - return OperationStatus.RETRY_LATER; - return OperationStatus.SUCCESS; + HashEntryInfo hei = new(keyHash); + FindTag(ref hei); + return LockTable.TryPromoteLock(ref hei); } } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRMW.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRMW.cs index 57d6ddba86..574dfce92c 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRMW.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRMW.cs @@ -175,7 +175,7 @@ internal OperationStatus InternalRMW stackCtx, ref OperationStatus status, ref LatchOperation latchOperation) - { - if (!IsLocking) - return AcquireCPRLatchRMW(phase, ref stackCtx, ref status, ref latchOperation); - - // This is AcquireCPRLatchRMW without the bucket latching, since we already have a latch on either the bucket or the recordInfo. - // See additional comments in AcquireCPRLatchRMW. - - switch (phase) - { - case Phase.PREPARE: // Thread is in V - if (!IsEntryVersionNew(ref stackCtx.hei.entry)) - break; // Normal Processing; thread is in V, record is in V - - status = OperationStatus.CPR_SHIFT_DETECTED; - return LatchDestination.Retry; // Pivot Thread for retry (do not operate on v+1 record when thread is in V) - - case Phase.IN_PROGRESS: // Thread is in v+1 - case Phase.WAIT_INDEX_CHECKPOINT: - case Phase.WAIT_FLUSH: - if (IsRecordVersionNew(stackCtx.recSrc.LogicalAddress)) - break; // Normal Processing; V+1 thread encountered a record in V+1 - - if (stackCtx.recSrc.LogicalAddress >= hlog.HeadAddress) - return LatchDestination.CreateNewRecord; // Record is in memory so force creation of a (V+1) record - break; // Normal Processing; the record is below HeadAddress so the operation will go pending - - default: - break; - } - return LatchDestination.NormalProcessing; - } - - private LatchDestination AcquireCPRLatchRMW(Phase phase, ref OperationStackContext stackCtx, ref OperationStatus status, ref LatchOperation latchOperation) { // The idea of CPR is that if a thread in version V tries to perform an operation and notices a record in V+1, it needs to back off and run CPR_SHIFT_DETECTED. // Similarly, a V+1 thread cannot update a V record; it needs to do a read-copy-update (or upsert at tail) instead of an in-place update. + // For background info: Prior to HashBucket-based locking, we had to lock the bucket in the following way: // 1. V threads take shared lock on bucket // 2. V+1 threads take exclusive lock on bucket, refreshing until they can // 3. If V thread cannot take shared lock, that means the system is in V+1 so we can immediately refresh and go to V+1 (do CPR_SHIFT_DETECTED) // 4. If V thread manages to get shared lock, but encounters a V+1 record, it knows the system is in V+1 so it will do CPR_SHIFT_DETECTED + // Now we no longer need to do the bucket latching, since we already have a latch on the bucket. switch (phase) { case Phase.PREPARE: // Thread is in V - if (HashBucket.TryAcquireSharedLatch(ref stackCtx.hei)) - { - // Set to release shared latch (default) - latchOperation = LatchOperation.Shared; - - // Here (and in InternalRead, AcquireLatchUpsert, and AcquireLatchDelete) we still check the tail record of the bucket (entry.Address) - // rather than the traced record (logicalAddress), because allowing in-place updates for version V when the bucket has arrived at V+1 may have - // complications we haven't investigated yet. This is safer but potentially unnecessary, and this case is so rare that the potential - // inefficiency is not a concern. - if (IsEntryVersionNew(ref stackCtx.hei.entry)) - { - status = OperationStatus.CPR_SHIFT_DETECTED; - return LatchDestination.Retry; // Pivot Thread for retry (do not operate on v+1 record when thread is in V) - } + if (!IsEntryVersionNew(ref stackCtx.hei.entry)) break; // Normal Processing; thread is in V, record is in V - } - // Could not acquire Shared latch; system must be in V+1 (or we have too many shared latches). status = OperationStatus.CPR_SHIFT_DETECTED; - return LatchDestination.Retry; // Pivot Thread for retry + return LatchDestination.Retry; // Pivot Thread for retry (do not operate on v+1 record when thread is in V) case Phase.IN_PROGRESS: // Thread is in v+1 - if (IsRecordVersionNew(stackCtx.recSrc.LogicalAddress)) - break; // Normal Processing; V+1 thread encountered a record in V+1 - - if (HashBucket.TryAcquireExclusiveLatch(ref stackCtx.hei)) - { - // Set to release exclusive latch (default) - latchOperation = LatchOperation.Exclusive; - if (stackCtx.recSrc.LogicalAddress >= hlog.HeadAddress) - return LatchDestination.CreateNewRecord; // Record is in memory so force creation of a (V+1) record - break; // Normal Processing; the record is below HeadAddress so the operation will go pending - } - - // Could not acquire exclusive latch; likely a conflict on the bucket. - status = OperationStatus.RETRY_LATER; - return LatchDestination.Retry; // Refresh and retry - - case Phase.WAIT_INDEX_CHECKPOINT: // Thread is in V+1 + case Phase.WAIT_INDEX_CHECKPOINT: case Phase.WAIT_FLUSH: if (IsRecordVersionNew(stackCtx.recSrc.LogicalAddress)) break; // Normal Processing; V+1 thread encountered a record in V+1 if (stackCtx.recSrc.LogicalAddress >= hlog.HeadAddress) - return LatchDestination.CreateNewRecord; // Record is in memory so force creation of a (V+1) record + return LatchDestination.CreateNewRecord; // Record is in memory so force creation of a (V+1) record break; // Normal Processing; the record is below HeadAddress so the operation will go pending default: diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRead.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRead.cs index 5d1c2f0dc2..42aab0911c 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRead.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalRead.cs @@ -148,7 +148,7 @@ internal OperationStatus InternalRead= hlog.BeginAddress) { // On-Disk Region - Debug.Assert(!sessionFunctions.IsManualLocking || LockTable.IsLocked(ref key, ref stackCtx.hei), "A Lockable-session Read() of an on-disk key requires a LockTable lock"); + Debug.Assert(!sessionFunctions.IsManualLocking || LockTable.IsLocked(ref stackCtx.hei), "A Lockable-session Read() of an on-disk key requires a LockTable lock"); // Note: we do not lock here; we wait until reading from disk, then lock in the ContinuePendingRead chain. if (hlog.IsNullDevice) @@ -158,7 +158,7 @@ internal OperationStatus InternalRead(ref Key key, ref Input input, Output output, Context userContext, ref PendingContext pendingContext, TSessionFunctionsWrapper sessionFunctions, long logicalAddress) where TSessionFunctionsWrapper : ISessionFunctionsWrapper diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalUpsert.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalUpsert.cs index b83a91202d..575993a1f8 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalUpsert.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/InternalUpsert.cs @@ -139,7 +139,7 @@ internal OperationStatus InternalUpsert stackCtx, ref OperationStatus status, ref LatchOperation latchOperation) { - if (!IsLocking) - return AcquireCPRLatchUpsert(phase, ref stackCtx, ref status, ref latchOperation); - - // This is AcquireCPRLatchUpsert without the bucket latching, since we already have a latch on either the bucket or the recordInfo. - // See additional comments in AcquireCPRLatchRMW. + // See explanatory comments in CheckCPRConsistencyRMW. switch (phase) { @@ -282,56 +278,6 @@ private LatchDestination CheckCPRConsistencyUpsert(Phase phase, ref OperationSta return LatchDestination.NormalProcessing; } - private LatchDestination AcquireCPRLatchUpsert(Phase phase, ref OperationStackContext stackCtx, ref OperationStatus status, ref LatchOperation latchOperation) - { - // See additional comments in AcquireCPRLatchRMW. - - switch (phase) - { - case Phase.PREPARE: // Thread is in V - if (HashBucket.TryAcquireSharedLatch(ref stackCtx.hei)) - { - // Set to release shared latch (default) - latchOperation = LatchOperation.Shared; - if (IsEntryVersionNew(ref stackCtx.hei.entry)) - { - status = OperationStatus.CPR_SHIFT_DETECTED; - return LatchDestination.Retry; // Pivot Thread for retry (do not operate on V+1 record when thread is in V) - } - break; // Normal Processing; thread is in V, record is in V - } - - // Could not acquire Shared latch; system must be in V+1 (or we have too many shared latches). - status = OperationStatus.CPR_SHIFT_DETECTED; - return LatchDestination.Retry; // Pivot Thread for retry - - case Phase.IN_PROGRESS: // Thread is in V+1 - if (IsRecordVersionNew(stackCtx.recSrc.LogicalAddress)) - break; // Normal Processing; V+1 thread encountered a record in V+1 - - if (HashBucket.TryAcquireExclusiveLatch(ref stackCtx.hei)) - { - // Set to release exclusive latch (default) - latchOperation = LatchOperation.Exclusive; - return LatchDestination.CreateNewRecord; // Upsert never goes pending; always force creation of a (v+1) record - } - - // Could not acquire exclusive latch; likely a conflict on the bucket. - status = OperationStatus.RETRY_LATER; - return LatchDestination.Retry; // Retry after refresh - - case Phase.WAIT_INDEX_CHECKPOINT: // Thread is in v+1 - case Phase.WAIT_FLUSH: - if (IsRecordVersionNew(stackCtx.recSrc.LogicalAddress)) - break; // Normal Processing; V+1 thread encountered a record in V+1 - return LatchDestination.CreateNewRecord; // Upsert never goes pending; always force creation of a (V+1) record - - default: - break; - } - return LatchDestination.NormalProcessing; - } - /// /// Create a new record for Upsert /// diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/ILockTable.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/ILockTable.cs index 0a8c41b52e..ec3b7c9df8 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/ILockTable.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/ILockTable.cs @@ -17,80 +17,47 @@ public interface ILockTable : IDisposable public bool IsEnabled { get; } /// - /// Try to acquire a manual lock for the key. - /// - /// The key to lock - /// The hash table entry info of the key to lock - /// The lock type to acquire - /// True if the lock was acquired; false if lock acquisition failed - /// There are no variations of this call specific to Shared vs. Exclusive, because this is - /// called only from InternalLock, which takes the argument. - public bool TryLockManual(ref TKey key, ref HashEntryInfo hei, LockType lockType); - - /// - /// Try to acquire a transient lock for the key. - /// - /// The key to lock - /// The hash table entry info of the key to lock - /// The lock type to acquire--shared or exclusive - public bool TryLockTransient(ref TKey key, ref HashEntryInfo hei, LockType lockType); - - /// - /// Try to acquire a shared transient lock for the key. + /// Try to acquire a shared lock for . /// - /// The key to lock /// The hash table entry info of the key to lock - public bool TryLockTransientShared(ref TKey key, ref HashEntryInfo hei); + public bool TryLockShared(ref HashEntryInfo hei); /// - /// Try to acquire an exclusive transient lock for the key. + /// Try to acquire an exclusive lock for . /// - /// The key to lock /// The hash table entry info of the key to lock - public bool TryLockTransientExclusive(ref TKey key, ref HashEntryInfo hei); + public bool TryLockExclusive(ref HashEntryInfo hei); /// - /// Release the lock on the key. + /// Release a shared lock on the . /// - /// The key to unlock /// The hash table entry info of the key to lock - /// The lock type to release--shared or exclusive - public void Unlock(ref TKey key, ref HashEntryInfo hei, LockType lockType); + public void UnlockShared(ref HashEntryInfo hei); /// - /// Release a shared lock on the key. + /// Release an exclusive lock on . /// - /// The key to unlock /// The hash table entry info of the key to lock - public void UnlockShared(ref TKey key, ref HashEntryInfo hei); + public void UnlockExclusive(ref HashEntryInfo hei); /// - /// Release an exclusive lock on the key. + /// Return whether the is S locked /// - /// The key to unlock - /// The hash table entry info of the key to lock - public void UnlockExclusive(ref TKey key, ref HashEntryInfo hei); + public bool IsLockedShared(ref HashEntryInfo hei); /// - /// Return whether the key is S locked + /// Return whether the is X locked /// - public bool IsLockedShared(ref TKey key, ref HashEntryInfo hei); - - /// - /// Return whether the keyrecord is X locked - /// - public bool IsLockedExclusive(ref TKey key, ref HashEntryInfo hei); + public bool IsLockedExclusive(ref HashEntryInfo hei); /// /// Return whether an the key is S or X locked /// - public bool IsLocked(ref TKey key, ref HashEntryInfo hei); + public bool IsLocked(ref HashEntryInfo hei); /// /// Return the Lock state of the key. /// - public LockState GetLockState(ref TKey key, ref HashEntryInfo hei); - - public bool NeedKeyHash { get; } + public LockState GetLockState(ref HashEntryInfo hei); } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/OverflowBucketLockTable.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/OverflowBucketLockTable.cs index af5e9ac8a7..7df3c76a01 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/OverflowBucketLockTable.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/OverflowBucketLockTable.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Runtime.CompilerServices; namespace Tsavorite.core @@ -12,38 +11,18 @@ internal struct OverflowBucketLockTable : ILockTable { private readonly TsavoriteKV store; - internal readonly long NumBuckets => IsEnabled ? store.state[store.resizeInfo.version].size_mask + 1 : 0; + internal readonly long NumBuckets => store.state[store.resizeInfo.version].size_mask + 1; public readonly bool IsEnabled => store is not null; - internal OverflowBucketLockTable(TsavoriteKV f) => store = f; - - [Conditional("DEBUG")] - private readonly void AssertLockAllowed() => Debug.Assert(IsEnabled, $"Attempt to do Manual locking when not using {nameof(ConcurrencyControlMode)}.{ConcurrencyControlMode.LockTable}"); - - [Conditional("DEBUG")] - private readonly void AssertUnlockAllowed() => Debug.Assert(IsEnabled, $"Attempt to do Manual unlocking when not using {nameof(ConcurrencyControlMode)}.{ConcurrencyControlMode.LockTable}"); - - [Conditional("DEBUG")] - private readonly void AssertQueryAllowed() => Debug.Assert(IsEnabled, $"Attempt to do Manual locking query when not using {nameof(ConcurrencyControlMode)}.{ConcurrencyControlMode.LockTable}"); + internal OverflowBucketLockTable(TsavoriteKV tkv) => store = tkv; internal readonly long GetSize() => store.state[store.resizeInfo.version].size_mask; - public readonly bool NeedKeyHash => IsEnabled; - - static OverflowBucketLockTable() - { - Debug.Assert(LockType.Exclusive < LockType.Shared, "LockType.Exclusive must be < LockType.Shared, or KeyHashComparer must be changed accordingly"); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static long GetBucketIndex(long keyHash, long size_mask) => keyHash & size_mask; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static long GetBucketIndex(long keyHash, TsavoriteKV store) - => GetBucketIndex(keyHash, store.state[store.resizeInfo.version].size_mask); - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal long GetBucketIndex(long keyHash) => GetBucketIndex(keyHash, store.state[store.resizeInfo.version].size_mask); @@ -54,136 +33,53 @@ internal long GetBucketIndex(long keyHash) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryLockManual(ref TKey key, ref HashEntryInfo hei, LockType lockType) - => TryLockManual(hei.firstBucket, lockType); + public unsafe bool TryLockShared(ref HashEntryInfo hei) + => HashBucket.TryAcquireSharedLatch(hei.firstBucket); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryLockManual(long keyHash, LockType lockType) - => TryLockManual(GetBucket(keyHash), lockType); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private unsafe bool TryLockManual(HashBucket* bucket, LockType lockType) - { - AssertLockAllowed(); - return lockType switch - { - LockType.Shared => HashBucket.TryAcquireSharedLatch(bucket), - LockType.Exclusive => HashBucket.TryAcquireExclusiveLatch(bucket), - _ => throw new TsavoriteException("Attempt to lock with unknown LockType") - }; - } + public unsafe bool TryLockExclusive(ref HashEntryInfo hei) + => HashBucket.TryAcquireExclusiveLatch(hei.firstBucket); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryPromoteLockManual(long keyHash) - { - AssertLockAllowed(); - return HashBucket.TryPromoteLatch(GetBucket(keyHash)); - } + public unsafe bool TryPromoteLock(ref HashEntryInfo hei) + => HashBucket.TryPromoteLatch(hei.firstBucket); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryLockTransient(ref TKey key, ref HashEntryInfo hei, LockType lockType) - => lockType == LockType.Shared ? TryLockTransientShared(ref key, ref hei) : TryLockTransientExclusive(ref key, ref hei); + public unsafe void UnlockShared(ref HashEntryInfo hei) + => HashBucket.ReleaseSharedLatch(ref hei); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryLockTransientShared(ref TKey key, ref HashEntryInfo hei) - { - AssertLockAllowed(); - return HashBucket.TryAcquireSharedLatch(hei.firstBucket); - } + public unsafe void UnlockExclusive(ref HashEntryInfo hei) + => HashBucket.ReleaseExclusiveLatch(ref hei); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool TryLockTransientExclusive(ref TKey key, ref HashEntryInfo hei) - { - AssertLockAllowed(); - return HashBucket.TryAcquireExclusiveLatch(hei.firstBucket); - } + public unsafe bool IsLockedShared(ref HashEntryInfo hei) + => HashBucket.NumLatchedShared(hei.firstBucket) > 0; /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Unlock(ref TKey key, ref HashEntryInfo hei, LockType lockType) - { - AssertUnlockAllowed(); - if (lockType == LockType.Shared) - UnlockShared(ref key, ref hei); - else - { - Debug.Assert(lockType == LockType.Exclusive, "Attempt to unlock with unknown LockType"); - UnlockExclusive(ref key, ref hei); - } - } + public unsafe bool IsLockedExclusive(ref HashEntryInfo hei) + => HashBucket.IsLatchedExclusive(hei.firstBucket); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void Unlock(long keyHash, LockType lockType) - { - AssertUnlockAllowed(); - HashBucket* bucket = GetBucket(keyHash); - if (lockType == LockType.Shared) - HashBucket.ReleaseSharedLatch(bucket); - else - { - Debug.Assert(lockType == LockType.Exclusive, "Attempt to unlock with unknown LockType"); - HashBucket.ReleaseExclusiveLatch(bucket); - } - } + public unsafe bool IsLocked(ref HashEntryInfo hei) + => HashBucket.IsLatched(hei.firstBucket); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void UnlockShared(ref TKey key, ref HashEntryInfo hei) - { - AssertUnlockAllowed(); - HashBucket.ReleaseSharedLatch(ref hei); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe void UnlockExclusive(ref TKey key, ref HashEntryInfo hei) - { - AssertUnlockAllowed(); - HashBucket.ReleaseExclusiveLatch(ref hei); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool IsLockedShared(ref TKey key, ref HashEntryInfo hei) - { - AssertQueryAllowed(); - return HashBucket.NumLatchedShared(hei.firstBucket) > 0; - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool IsLockedExclusive(ref TKey key, ref HashEntryInfo hei) - { - AssertQueryAllowed(); - return HashBucket.IsLatchedExclusive(hei.firstBucket); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe bool IsLocked(ref TKey key, ref HashEntryInfo hei) - { - AssertQueryAllowed(); - return HashBucket.IsLatched(hei.firstBucket); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe LockState GetLockState(ref TKey key, ref HashEntryInfo hei) - { - AssertQueryAllowed(); - return new() + public unsafe LockState GetLockState(ref HashEntryInfo hei) + => new() { IsFound = true, // Always true for OverflowBucketLockTable NumLockedShared = HashBucket.NumLatchedShared(hei.firstBucket), IsLockedExclusive = HashBucket.IsLatchedExclusive(hei.firstBucket) }; - } private static int KeyHashComparer(TLockableKey key1, TLockableKey key2, long size_mask) where TLockableKey : ILockableKey diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/TransientLocking.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/TransientLocking.cs index 1678fa0629..20224f0d57 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/TransientLocking.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Locking/TransientLocking.cs @@ -56,17 +56,15 @@ internal static void TransientSUnlock stackCtx, ref Key key, ref RecordInfo recordInfo) { Debug.Assert(!stackCtx.recSrc.HasLock, $"Should not call LockForScan if recSrc already has a lock ({stackCtx.recSrc.LockStateString()})"); - if (IsLocking) - { - // This will always be a transient lock as it is not session-based - stackCtx = new(comparer.GetHashCode64(ref key)); - FindTag(ref stackCtx.hei); - stackCtx.SetRecordSourceToHashEntry(hlog); - while (!LockTable.TryLockTransientShared(ref key, ref stackCtx.hei)) - epoch.ProtectAndDrain(); - stackCtx.recSrc.SetHasTransientSLock(); - } + // This will always be a transient lock as it is not session-based + stackCtx = new(comparer.GetHashCode64(ref key)); + FindTag(ref stackCtx.hei); + stackCtx.SetRecordSourceToHashEntry(hlog); + + while (!LockTable.TryLockShared(ref stackCtx.hei)) + epoch.ProtectAndDrain(); + stackCtx.recSrc.SetHasTransientSLock(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -74,7 +72,7 @@ internal void UnlockForScan(ref OperationStackContext stackCtx, ref { if (stackCtx.recSrc.HasTransientSLock) { - LockTable.UnlockShared(ref key, ref stackCtx.hei); + LockTable.UnlockShared(ref stackCtx.hei); stackCtx.recSrc.ClearHasTransientSLock(); } } diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RevivificationManager.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RevivificationManager.cs index ad28f9f8ff..ecfac8cedc 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RevivificationManager.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RevivificationManager.cs @@ -30,8 +30,6 @@ public RevivificationManager(TsavoriteKV store, bool isFixedLen, Rev if (revivSettings is null) return; - if (revivSettings.EnableRevivification && !store.IsLocking) - throw new TsavoriteException("Revivification cannot be used with ConcurrencyControlMode.None"); revivSettings.Verify(IsFixedLength, logSettings.MutableFraction); if (!revivSettings.EnableRevivification) diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs index 0e8c58f306..bb322539d5 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs @@ -66,7 +66,6 @@ public partial class TsavoriteKV : TsavoriteBase, IDisposable int maxSessionID; - internal readonly bool IsLocking; // uses LockTable internal readonly bool CheckpointVersionSwitchBarrier; // version switch barrier internal readonly OverflowBucketLockTable LockTable; @@ -89,7 +88,7 @@ public TsavoriteKV(TsavoriteKVSettings tsavoriteKVSettings) : this( tsavoriteKVSettings.GetIndexSizeCacheLines(), tsavoriteKVSettings.GetLogSettings(), tsavoriteKVSettings.GetCheckpointSettings(), tsavoriteKVSettings.GetSerializerSettings(), - tsavoriteKVSettings.EqualityComparer, tsavoriteKVSettings.TryRecoverLatest, tsavoriteKVSettings.ConcurrencyControlMode, + tsavoriteKVSettings.EqualityComparer, tsavoriteKVSettings.TryRecoverLatest, null, revivificationSettings: tsavoriteKVSettings.RevivificationSettings) { } @@ -102,14 +101,12 @@ public TsavoriteKV(TsavoriteKVSettings tsavoriteKVSettings) : /// Serializer settings /// Tsavorite equality comparer for key /// Try to recover from latest checkpoint, if any - /// How Tsavorite should do record locking /// Logger factory to create an ILogger, if one is not passed in (e.g. from ). /// Logger to use. /// Settings for recycling deleted records on the log. public TsavoriteKV(long size, LogSettings logSettings, CheckpointSettings checkpointSettings = null, SerializerSettings serializerSettings = null, ITsavoriteEqualityComparer comparer = null, bool tryRecoverLatest = false, - ConcurrencyControlMode concurrencyControlMode = ConcurrencyControlMode.LockTable, ILoggerFactory loggerFactory = null, ILogger logger = null, RevivificationSettings revivificationSettings = null) { this.loggerFactory = loggerFactory; @@ -136,8 +133,6 @@ public TsavoriteKV(long size, LogSettings logSettings, } } - IsLocking = concurrencyControlMode == ConcurrencyControlMode.LockTable; - checkpointSettings ??= new CheckpointSettings(); CheckpointVersionSwitchBarrier = checkpointSettings.CheckpointVersionSwitchBarrier; @@ -234,7 +229,7 @@ public TsavoriteKV(long size, LogSettings logSettings, sectorSize = (int)logSettings.LogDevice.SectorSize; Initialize(size, sectorSize); - LockTable = new OverflowBucketLockTable(concurrencyControlMode == ConcurrencyControlMode.LockTable ? this : null); + LockTable = new OverflowBucketLockTable(this); RevivificationManager = new(this, isFixedLenReviv, revivificationSettings, logSettings); systemState = SystemState.Make(Phase.REST, 1); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs index 61b891f52f..f9e594f721 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs @@ -96,7 +96,7 @@ public TsavoriteKVIterator(TsavoriteKV store, Functions functions, l iterationPhase = IterationPhase.MainKv; tempKv = new TsavoriteKV(store.IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice(), MutableFraction = 1 }, comparer: store.Comparer, - loggerFactory: loggerFactory, concurrencyControlMode: ConcurrencyControlMode.None); + loggerFactory: loggerFactory); tempKvSession = tempKv.NewSession(functions); tempbContext = tempKvSession.BasicContext; mainKvIter = store.Log.Scan(store.Log.BeginAddress, untilAddress); diff --git a/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs b/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs index 2fd4df3fc0..18335efb67 100644 --- a/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs +++ b/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs @@ -24,22 +24,6 @@ public enum LockType : byte Shared } - /// - /// How Tsavorite should do concurrency control - /// - public enum ConcurrencyControlMode : byte - { - /// - /// Keys are locked using a LockTable. Currently the implementation latches the hash index buckets. Supports manual and transient locking, based on the session type. - /// - LockTable, - - /// - /// No Locking is done in Tsavorite. - /// - None - } - /// /// Interface that must be implemented to participate in keyHash-based locking. /// diff --git a/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs b/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs deleted file mode 100644 index c3bd56c203..0000000000 --- a/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs +++ /dev/null @@ -1,346 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -using System; -using System.Diagnostics; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using NUnit.Framework; -using Tsavorite.core; -using static Tsavorite.test.TestUtils; - -#pragma warning disable IDE0060 // Remove unused parameter: used by Setup - -namespace Tsavorite.test.LockTests -{ - [TestFixture] - internal class AdvancedLockTests - { - const int numKeys = 500; - const int valueAdd = 1000000; - const int mod = 100; - - public struct Input - { - internal bool doTest; // For Populate() we don't want to do this - internal bool expectSingleReader; - internal int updatedValue; - - public override readonly string ToString() => $"doTest {doTest}, expectSingleReader {expectSingleReader}, updatedValue {updatedValue}"; - } - - [Flags] - internal enum LockTestMode - { - UpdateAfterCTT = 1, - CTTAfterUpdate = 2, - } - - internal class Functions : SessionFunctionsBase, IDisposable - { - // The update event is manual reset because it will retry and thus needs to remain set. - internal readonly ManualResetEvent updateEvent = new(initialState: false); - internal readonly AutoResetEvent readEvent = new(initialState: false); - - public override bool SingleWriter(ref int key, ref Input input, ref int src, ref int dst, ref int output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) - { - // SingleWriter is shared with Read so needs the Reason - if (input.doTest && reason == WriteReason.Upsert) - updateEvent.WaitOne(); - output = dst = src; - return true; - } - - public override void PostSingleWriter(ref int key, ref Input input, ref int src, ref int dst, ref int output, ref UpsertInfo upsertInfo, WriteReason reason) - { - // SingleWriter is shared with Read so needs the Reason - base.PostSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); - if (!input.doTest) - return; - if (reason == WriteReason.Upsert) - readEvent.Set(); - else // CopyToTail or CopyToReadCache - updateEvent.Set(); - } - - public override bool ConcurrentWriter(ref int key, ref Input input, ref int src, ref int dst, ref int output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo) - { - // No Wait or Set operations are done here; that's all been done by the time we get to this point - output = dst = src; - return true; - } - - public override bool CopyUpdater(ref int key, ref Input input, ref int oldValue, ref int newValue, ref int output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - if (input.doTest) - updateEvent.WaitOne(); - output = newValue = input.updatedValue; - return true; - } - - public override bool PostCopyUpdater(ref int key, ref Input input, ref int oldValue, ref int newValue, ref int output, ref RMWInfo rmwInfo) - { - base.PostCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); - if (input.doTest) - readEvent.Set(); - return true; - } - - public override bool InPlaceUpdater(ref int key, ref Input input, ref int value, ref int output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - // No Wait or Set operations are done here; that's all been done by the time we get to this point - output = value = input.updatedValue; - return true; - } - - public override bool SingleReader(ref int key, ref Input input, ref int value, ref int output, ref ReadInfo readInfo) - { - // We are here if we are doing the initial read, before Upsert's insert-at-tail has taken place; this means we are testing that Insert waits for the - // Read to Read and then CTT, and then signal the Insert to proceed (and update the newly-CTT'd record). - Assert.IsTrue(input.doTest && input.expectSingleReader, $"SingleReader: Key = {key}"); - output = value; - return true; - } - - public override bool ConcurrentReader(ref int key, ref Input input, ref int value, ref int output, ref ReadInfo readInfo, ref RecordInfo recordInfo) - { - // We are here if we are doing the read of the Upsert-inserted updated value; this means we are testing that Read waits for the - // Upsert to complete and then signal the Read to proceed (and read the newly-inserted record). - Assert.IsFalse(input.doTest && input.expectSingleReader, $"ConcurrentReader: Key = {key}"); - output = value; - return true; - } - - public void Dispose() - { - updateEvent.Dispose(); - readEvent.Dispose(); - } - } - - internal class ChainComparer : ITsavoriteEqualityComparer - { - readonly int mod; - - internal ChainComparer(int mod) => this.mod = mod; - - public bool Equals(ref int k1, ref int k2) => k1 == k2; - - public long GetHashCode64(ref int k) => k % mod; - } - - private TsavoriteKV store; - private ClientSession session; - private IDevice log; - - [SetUp] - public void Setup() - { - DeleteDirectory(MethodTestDir, wait: true); - log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "GenericStringTests.log"), deleteOnClose: true); - var readCacheSettings = new ReadCacheSettings { MemorySizeBits = 15, PageSizeBits = 9 }; - - var concurrencyControlMode = ConcurrencyControlMode.None; - foreach (var arg in TestContext.CurrentContext.Test.Arguments) - { - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } - } - - store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, ReadCacheSettings = readCacheSettings }, - comparer: new ChainComparer(mod), concurrencyControlMode: concurrencyControlMode); - session = store.NewSession(new Functions()); - } - - [TearDown] - public void TearDown() - { - session?.Dispose(); - session = null; - store?.Dispose(); - store = null; - log?.Dispose(); - log = null; - - DeleteDirectory(MethodTestDir); - } - - void Populate(bool evict = false) - { - using var session = store.NewSession(new Functions()); - var bContext = session.BasicContext; - - for (int key = 0; key < numKeys; key++) - bContext.Upsert(key, key + valueAdd); - bContext.CompletePending(true); - if (evict) - store.Log.FlushAndEvict(wait: true); - } - - [Test] - [Category(TsavoriteKVTestCategory)] - [Category(LockTestCategory)] - //[Repeat(100)] - public async ValueTask SameKeyInsertAndCTTTest([Values(ConcurrencyControlMode.None /* Do not use LockTable; it will hang */)] ConcurrencyControlMode concurrencyControlMode, - [Values(ReadCopyTo.ReadCache, ReadCopyTo.MainLog)] ReadCopyTo readCopyTo, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) - { - if (TestContext.CurrentContext.CurrentRepeatCount > 0) - Debug.WriteLine($"*** Current test iteration: {TestContext.CurrentContext.CurrentRepeatCount + 1} ***"); - Populate(evict: true); - using Functions functions = new(); - using var readSession = store.NewSession(functions); - var readbContext = readSession.BasicContext; - using var updateSession = store.NewSession(functions); - var updatebContext = updateSession.BasicContext; - var iter = 0; - - // This test ensures that we handle the two cases, which are timing-dependent so we use events to force the sequence. "Update" refers to either - // Upsert ((Post)SingleWriter) or RMW ((Post)CopyUpdater). Note that "CTT" is loosely used here to refer to both copies to MainLog and to ReadCache. - // UpdateAfterCTT: an Update is in-flight when a Read is CTT'd to the tail or readcache; the Update's insert should fail CAS and retry successfully, - // and the updated value should be returned in its output; the Read completed first, so should still have the old value. - // - Wait on the updateEvent in Upsert/SingleWriter or CopyUpdater; Set the updateEvent in Read/PostSingleWriter as part of CopyToTail. - // - In this case, ContinuePendingRead calls SingleReader (on the record in the request, before calling CopyToTail to add it) so returns the old value. - // CTTAfterUpdate: a ReadCTT is in-flight when an Insert is added to the tail; the updated value should be returned in output from both Upsert/RMW and Read. - // Read must go pending before Upsert/RMW starts, then wait until Upsert/RMW is complete, so it will find the newly inserted record rather than doing CTT. - // - Stage 0: Wait on the updateEvent in Upsert/SingleWriter or CopyUpdater; the updateEvent is Set in "second action" after calling Read but before calling CompletePending. - // - Stage 1: Wait on the readEvent in the "second action"; the readEvent is Set in Upsert/PostSingleWriter or PostCopyUpdater after Upsert/RMW write to tail. - // - In this case, ContinuePendingRead calls ConcurrentReader (on the newly inserted record; it does *not* call SingleReader) - // So both of these resolve to having the Functions Wait in Upsert/SingleWriter or CopyUpdater, and Set in (Upsert_or_RMW or Read, depending on testMode)/PostSingleWriter. - - Input createInput(int key, out LockTestMode testMode) - { - testMode = (key & 1) == 0 ? LockTestMode.UpdateAfterCTT : LockTestMode.CTTAfterUpdate; - return new() - { - doTest = true, - expectSingleReader = testMode == LockTestMode.UpdateAfterCTT, - updatedValue = key + valueAdd * 2 - }; - } - - await DoTwoThreadRandomKeyTest(numKeys, doRandom: false, - key => - { - int output = -1; - Input input = createInput(key, out var testMode); - Status status; - - bool expectedFound; - if (updateOp == UpdateOp.Upsert) - { - // Upsert of a record not in mutable log returns NOTFOUND because it creates a new record. - expectedFound = readCopyTo == ReadCopyTo.MainLog && testMode == LockTestMode.UpdateAfterCTT; - status = updatebContext.Upsert(ref key, ref input, ref input.updatedValue, ref output); - } - else - { - // RMW will always find and update or copyupdate. - expectedFound = true; - status = updatebContext.RMW(ref key, ref input, ref output); - - // Unlike Upsert, RMW may go pending (depending whether CopyToTail|ReadCache completes first). We don't care about that here; - // the test is that we properly handle either the CAS at the tail by retrying, or invalidate the readcache record. - if (status.IsPending) - { - updatebContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - } - } - - // Insert that replaces a ReadCache record returns NOTFOUND because it creates a new record - Assert.AreEqual(expectedFound, status.Found, $"First action: Key = {key}, status = {status}, testMode {testMode}"); - Assert.AreEqual(input.updatedValue, output, $"First action: Key = {key}, iter = {iter}, testMode {testMode}"); - }, - key => - { - int output = -2; - Input input = createInput(key, out var testMode); - ReadOptions readOptions = new() { CopyOptions = new(ReadCopyFrom.Device, readCopyTo) }; - - // This will copy to ReadCache or MainLog tail, and the test is trying to cause a race with the above Upsert. - var status = readbContext.Read(ref key, ref input, ref output, ref readOptions, out _); - - // For this test to work deterministically, the read must go pending - Assert.IsTrue(status.IsPending, $"Second action: Key = {key}, status = {status}, testMode {testMode}"); - - var expectedOutput = key + valueAdd; - if (testMode == LockTestMode.CTTAfterUpdate) - { - // We've gone pending on Read; now tell Update to continue and write at tail. - functions.updateEvent.Set(); - - // We must wait here until Update is complete, so ContinuePendingRead finds the newly-inserted record. - functions.readEvent.WaitOne(); - - // We will do ConcurrentReader of the newly-inserted (and therefore updated) value. - expectedOutput += valueAdd; - } - - readbContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.AreEqual(expectedOutput, output, $"Second action: Key = {key}, iter = {iter}, testMode {testMode}"); - }, - key => - { - int output = -3; - var status = readbContext.Read(ref key, ref output); - - // This should not have gone pending, since the Insert or Read updated at the log tail. There should be no valid readcache - // record; if there is, the test for updated value will fail. - Assert.IsTrue(status.Found, $"Verification: Key = {key}, status = {status}"); - Assert.AreEqual(key + valueAdd * 2, output, $"Verification: Key = {key}, iter = {iter}"); - functions.updateEvent.Reset(); - functions.readEvent.Reset(); - ++iter; - } - ); - } - - [TestFixture] - class LockRecoveryTests - { - const int numKeys = 5000; - - string checkpointDir; - - private TsavoriteKV store1; - private TsavoriteKV store2; - private IDevice log; - - [SetUp] - public void Setup() - { - DeleteDirectory(MethodTestDir, wait: true); - checkpointDir = Path.Join(MethodTestDir, "checkpoints"); - log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "test.log"), deleteOnClose: true); - - store1 = new TsavoriteKV(128, - logSettings: new LogSettings { LogDevice = log, MutableFraction = 0.1, MemorySizeBits = 29 }, - checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir } - ); - - store2 = new TsavoriteKV(128, - logSettings: new LogSettings { LogDevice = log, MutableFraction = 0.1, MemorySizeBits = 29 }, - checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir } - ); - } - - [TearDown] - public void TearDown() - { - store1?.Dispose(); - store1 = null; - store2?.Dispose(); - store2 = null; - log?.Dispose(); - log = null; - - DeleteDirectory(MethodTestDir); - } - } - } -} \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/test/BasicLockTests.cs b/libs/storage/Tsavorite/cs/test/BasicLockTests.cs index a03fc4b0a8..1d3610c5a5 100644 --- a/libs/storage/Tsavorite/cs/test/BasicLockTests.cs +++ b/libs/storage/Tsavorite/cs/test/BasicLockTests.cs @@ -82,7 +82,7 @@ public void Setup() { DeleteDirectory(MethodTestDir, wait: true); log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "GenericStringTests.log"), deleteOnClose: true); - store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null }, comparer: new LocalComparer(), concurrencyControlMode: ConcurrencyControlMode.LockTable); + store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null }, comparer: new LocalComparer()); session = store.NewSession(new Functions()); bContext = session.BasicContext; } diff --git a/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs b/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs index 96123d3902..fe8ba9738c 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs @@ -62,8 +62,7 @@ public void BlittableIterationBasicTest([Values] DeviceType deviceType, [Values] { log = CreateTestDevice(deviceType, Path.Join(MethodTestDir, $"{deviceType}.log")); store = new TsavoriteKV - (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }, - concurrencyControlMode: scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable); + (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }); using var session = store.NewSession(new FunctionsCompaction()); var bContext = session.BasicContext; @@ -183,13 +182,12 @@ void scanAndVerify(int stopAt, bool useScan) [Test] [Category(TsavoriteKVTestCategory)] [Category(SmokeTestCategory)] - public unsafe void BlittableIterationPushLockTest([Values(1, 4)] int scanThreads, [Values(1, 4)] int updateThreads, [Values] ConcurrencyControlMode concurrencyControlMode, [Values] ScanMode scanMode) + public unsafe void BlittableIterationPushLockTest([Values(1, 4)] int scanThreads, [Values(1, 4)] int updateThreads, [Values] ScanMode scanMode) { log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "lock_test.log")); // Must be large enough to contain all records in memory to exercise locking store = new TsavoriteKV(1L << 20, - new LogSettings { LogDevice = log, MemorySizeBits = 25, PageSizeBits = 20, SegmentSizeBits = 22 }, - concurrencyControlMode: concurrencyControlMode); + new LogSettings { LogDevice = log, MemorySizeBits = 25, PageSizeBits = 20, SegmentSizeBits = 22 }); const int totalRecords = 2000; var start = store.Log.TailAddress; diff --git a/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs index 2ac297a5dd..778859a966 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs @@ -40,15 +40,9 @@ public void Setup() DeleteDirectory(MethodTestDir, wait: true); log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "BlittableLogCompactionTests.log"), deleteOnClose: true); - var concurrencyControlMode = ConcurrencyControlMode.LockTable; var hashMod = HashModulo.NoMod; foreach (var arg in TestContext.CurrentContext.Test.Arguments) { - if (arg is ConcurrencyControlMode locking_mode) - { - concurrencyControlMode = locking_mode; - continue; - } if (arg is HashModulo mod) { hashMod = mod; @@ -57,7 +51,7 @@ public void Setup() } store = new TsavoriteKV - (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9 }, comparer: new HashModuloComparer(hashMod), concurrencyControlMode: concurrencyControlMode); ; + (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9 }, comparer: new HashModuloComparer(hashMod)); ; } [TearDown] @@ -127,7 +121,7 @@ void drainPending() [Category("Compaction")] [Category("Smoke")] - public void BlittableLogCompactionTest1([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void BlittableLogCompactionTest1([Values] CompactionType compactionType) { using var session = store.NewSession(new FunctionsCompaction()); var bContext = session.BasicContext; @@ -159,8 +153,7 @@ public void BlittableLogCompactionTest1([Values] CompactionType compactionType, [Test] [Category("TsavoriteKV")] [Category("Compaction")] - public void BlittableLogCompactionTest2([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode, - [Values(HashModulo.NoMod, HashModulo.Hundred)] HashModulo hashMod) + public void BlittableLogCompactionTest2([Values] CompactionType compactionType, [Values(HashModulo.NoMod, HashModulo.Hundred)] HashModulo hashMod) { using var session = store.NewSession(new FunctionsCompaction()); var bContext = session.BasicContext; @@ -208,7 +201,7 @@ public void BlittableLogCompactionTest2([Values] CompactionType compactionType, [Test] [Category("TsavoriteKV")] [Category("Compaction")] - public void BlittableLogCompactionTest3([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void BlittableLogCompactionTest3([Values] CompactionType compactionType) { using var session = store.NewSession(new FunctionsCompaction()); var bContext = session.BasicContext; @@ -248,7 +241,7 @@ public void BlittableLogCompactionTest3([Values] CompactionType compactionType, [Category("Compaction")] [Category("Smoke")] - public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType compactionType) { using var session = store.NewSession(new FunctionsCompaction()); var bContext = session.BasicContext; @@ -308,9 +301,7 @@ public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType c [Test] [Category("TsavoriteKV")] [Category("Compaction")] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void BlittableLogCompactionCustomFunctionsTest2([Values] CompactionType compactionType, [Values] bool flushAndEvict, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void BlittableLogCompactionCustomFunctionsTest2([Values] CompactionType compactionType, [Values] bool flushAndEvict) { // Update: irrelevant as session compaction no longer uses Copy/CopyInPlace // This test checks if CopyInPlace returning false triggers call to Copy diff --git a/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs b/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs index 0ecc1d24be..b4963d7493 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs @@ -53,7 +53,7 @@ public void Setup() log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "test.log"), deleteOnClose: true); store = new TsavoriteKV(1L << 20, - new LogSettings { LogDevice = log, MemorySizeBits = 24, PageSizeBits = PageSizeBits }, concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); + new LogSettings { LogDevice = log, MemorySizeBits = 24, PageSizeBits = PageSizeBits }, comparer: comparer); } [TearDown] diff --git a/libs/storage/Tsavorite/cs/test/DisposeTests.cs b/libs/storage/Tsavorite/cs/test/DisposeTests.cs deleted file mode 100644 index f38bf644d9..0000000000 --- a/libs/storage/Tsavorite/cs/test/DisposeTests.cs +++ /dev/null @@ -1,761 +0,0 @@ -#pragma warning disable IDE0055 -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -#if false - -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using NUnit.Framework; -using Tsavorite.core; -using static Tsavorite.core.Utility; -using static Tsavorite.test.TestUtils; - -#pragma warning disable IDE0060 // Remove unused parameter; used for Setup only - -namespace Tsavorite.test.Dispose -{ - [TestFixture] - internal class DisposeTests - { - // MyKey and MyValue are classes; we want to be sure we are getting the right Keys and Values to Dispose(). - private TsavoriteKV store; - private IDevice log, objlog; - - // Events to coordinate forcing CAS failure (by appending a new item), etc. - private SemaphoreSlim sutGate; // Session Under Test - private SemaphoreSlim otherGate; // Other session that inserts a colliding value - - [SetUp] - public void Setup() - { - DeleteDirectory(MethodTestDir, wait: true); - - sutGate = new(0); - otherGate = new(0); - - log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "ObjectTests.log"), deleteOnClose: true); - objlog = Devices.CreateLogDevice(Path.Join(MethodTestDir, "ObjectTests.obj.log"), deleteOnClose: true); - - LogSettings logSettings = new() { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 15, PageSizeBits = 10 }; - var concurrencyControlMode = ConcurrencyControlMode.None; - foreach (var arg in TestContext.CurrentContext.Test.Arguments) - { - if (arg is ReadCopyDestination dest) - { - if (dest == ReadCopyDestination.ReadCache) - logSettings.ReadCacheSettings = new() { PageSizeBits = 12, MemorySizeBits = 22 }; - continue; - } - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } - } - - store = new TsavoriteKV(128, logSettings: logSettings, comparer: new MyKeyComparer(), - serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: concurrencyControlMode // Warning: ConcurrencyControlMode.LockTable will deadlock with X locks as both keys map to the same keyHash - ); - } - - [TearDown] - public void TearDown() - { - store?.Dispose(); - store = null; - log?.Dispose(); - log = null; - objlog?.Dispose(); - objlog = null; - DeleteDirectory(MethodTestDir); - } - - // This is passed to the TsavoriteKV ctor to override the default one. This lets us use a different key for the colliding - // CAS; we can't use the same key because Readonly-region handling in the first session Seals the to-be-transferred record, - // so the second session would loop forever while the first session waits for the collision to be written. - class MyKeyComparer : ITsavoriteEqualityComparer - { - public long GetHashCode64(ref MyKey key) => Utility.GetHashCode(key.key % TestKey); - - public bool Equals(ref MyKey k1, ref MyKey k2) => k1.key == k2.key; - } - - const int TestKey = 111; - const int TestCollidingKey = TestKey * 2; - const int TestCollidingKey2 = TestKey * 3; - const int TestInitialValue = 3333; - const int TestUpdatedValue = 5555; - const int TestCollidingValue = 7777; - const int TestCollidingValue2 = 9999; - - internal enum DisposeHandler - { - None, - SingleWriter, - CopyUpdater, - InitialUpdater, - SingleDeleter, - DeserializedFromDisk, - } - - public class DisposeFunctions : FunctionsBase - { - private readonly DisposeTests tester; - internal readonly bool isSUT; // IsSessionUnderTest - internal Queue handlerQueue = new(); - private bool isRetry; - private bool isSplice; - - internal DisposeFunctions(DisposeTests tester, bool sut, bool splice = false) - { - this.tester = tester; - isSUT = sut; - isSplice = splice; - } - - void WaitForEvent() - { - Assert.IsTrue(tester.store.epoch.ThisInstanceProtected(), "This should only be called from ISessionFunctions methods, which are under epoch protection"); - if (isSUT) - { - MyKey key = new() { key = TestKey }; - tester.store.FindHashBucketEntryForKey(ref key, out var entry); - var address = entry.Address; - if (isSplice) - { - // Get the tail entry for this key's hash chain; there should be exactly one readcache entry for this test. - Assert.IsTrue(entry.ReadCache, "Expected readcache entry in WaitForEvent pt 1"); - Assert.GreaterOrEqual(entry.AbsoluteAddress, tester.store.ReadCache.HeadAddress); - var physicalAddress = tester.store.readcache.GetPhysicalAddress(entry.AbsoluteAddress); - ref RecordInfo recordInfo = ref tester.store.readcache.GetInfo(physicalAddress); - address = recordInfo.PreviousAddress; - - // There should be only one readcache entry for this test. The address we just got may have been kTempInvalidAddress, - // and if not then it should have been a pre-FlushAndEvict()ed record. - Assert.IsFalse(IsReadCache(address)); - - // Retry will have already inserted something post-FlushAndEvict. - Assert.IsTrue(isRetry || address < tester.store.hlog.HeadAddress); - } - tester.otherGate.Release(); - tester.sutGate.Wait(); - - tester.store.FindHashBucketEntryForKey(ref key, out entry); - - // There's a little race where the SUT session could still beat the other session to the CAS - if (!isRetry) - { - if (isSplice) - { - // If this is not Standard locking, then we use detach-and-reattach logic on the hash chain. That happens after SingleWriter, - // so 'other' thread may still be in progress . Wait for it. - while (!entry.ReadCache) - { - Assert.IsFalse(tester.store.LockTable.IsEnabled, "Standard locking should have spliced directly"); - Thread.Yield(); - tester.store.FindHashBucketEntryForKey(ref key, out entry); - } - - // We're the thread awaiting the splice, so wait until the address in the last readcache record changes. - Assert.IsTrue(entry.ReadCache, "Expected readcache entry in WaitForEvent pt 2"); - Assert.GreaterOrEqual(entry.AbsoluteAddress, tester.store.ReadCache.HeadAddress); - var physicalAddress = tester.store.readcache.GetPhysicalAddress(entry.AbsoluteAddress); - ref RecordInfo recordInfo = ref tester.store.readcache.GetInfo(physicalAddress); - while (recordInfo.PreviousAddress == address) - { - // Wait for the splice to happen - Thread.Yield(); - } - Assert.IsFalse(IsReadCache(recordInfo.PreviousAddress)); - Assert.IsTrue(recordInfo.PreviousAddress >= tester.store.hlog.HeadAddress); - } - else - { - // We're not the splice thread, so wait until the address in the hash entry changes. - while (entry.Address == address) - { - Thread.Yield(); - tester.store.FindHashBucketEntryForKey(ref key, out entry); - } - } - } - isRetry = true; // the next call will be from RETRY_NOW - } - } - - void SignalEvent() - { - // Let the SUT proceed, which will trigger a RETRY_NOW due to the failed CAS, so we need to release for the second wait as well. - // Release with a count of 2 to handle the attempt it's currently blocked on and the subsequent retry. - if (!isSUT) - tester.sutGate.Release(2); - } - - public override bool SingleWriter(ref MyKey key, ref MyInput input, ref MyValue src, ref MyValue dst, ref MyOutput output, ref UpsertInfo upsertInfo, WriteReason reason) - { - WaitForEvent(); - dst = src; - SignalEvent(); - return true; - } - - public override bool InitialUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo) - { - WaitForEvent(); - value = new MyValue { value = input.value }; - SignalEvent(); - return true; - } - - public override bool CopyUpdater(ref MyKey key, ref MyInput input, ref MyValue oldValue, ref MyValue newValue, ref MyOutput output, ref RMWInfo rmwInfo) - { - WaitForEvent(); - newValue = new MyValue { value = oldValue.value + input.value }; - SignalEvent(); - return true; - } - - public override bool SingleDeleter(ref MyKey key, ref MyValue value, ref DeleteInfo deleteInfo) - { - WaitForEvent(); - base.SingleDeleter(ref key, ref value, ref deleteInfo); - SignalEvent(); - return true; - } - - public override bool InPlaceUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo) - { - value.value += input.value; - return true; - } - - public override bool NeedCopyUpdate(ref MyKey key, ref MyInput input, ref MyValue oldValue, ref MyOutput output, ref RMWInfo rmwInfo) => true; - - public override bool ConcurrentReader(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput dst, ref ReadInfo readInfo) - { - Assert.Fail("ConcurrentReader should not be called for this test"); - return true; - } - - public override bool ConcurrentWriter(ref MyKey key, ref MyInput input, ref MyValue src, ref MyValue dst, ref MyOutput output, ref UpsertInfo upsertInfo) - { - dst.value = src.value; - return true; - } - - public override void RMWCompletionCallback(ref MyKey key, ref MyInput input, ref MyOutput output, Empty ctx, Status status, RecordMetadata recordMetadata) - { - if (isSUT) - { - Assert.IsTrue(status.Found, status.ToString()); - Assert.IsTrue(status.Record.CopyUpdated, status.ToString()); // InPlace due to RETRY_NOW after CAS failure - } - else - { - Assert.IsTrue(status.NotFound, status.ToString()); - Assert.IsTrue(status.Record.Created, status.ToString()); - } - } - - public override bool SingleReader(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput dst, ref ReadInfo readInfo) - { - dst.value = value; - return true; - } - - public override void DisposeSingleWriter(ref MyKey key, ref MyInput input, ref MyValue src, ref MyValue dst, ref MyOutput output, ref UpsertInfo upsertInfo, WriteReason reason) - { - Assert.AreEqual(TestKey, key.key); - Assert.AreEqual(TestInitialValue, src.value); - Assert.AreEqual(TestInitialValue, dst.value); // dst has been populated - handlerQueue.Enqueue(DisposeHandler.SingleWriter); - } - - public override void DisposeCopyUpdater(ref MyKey key, ref MyInput input, ref MyValue oldValue, ref MyValue newValue, ref MyOutput output, ref RMWInfo rmwInfo) - { - Assert.AreEqual(TestKey, key.key); - Assert.AreEqual(TestInitialValue, oldValue.value); - Assert.AreEqual(TestInitialValue + TestUpdatedValue, newValue.value); - handlerQueue.Enqueue(DisposeHandler.CopyUpdater); - } - - public override void DisposeInitialUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo) - { - Assert.AreEqual(TestKey, key.key); - Assert.AreEqual(TestInitialValue, value.value); - handlerQueue.Enqueue(DisposeHandler.InitialUpdater); - } - - public override void DisposeSingleDeleter(ref MyKey key, ref MyValue value, ref DeleteInfo deleteInfo) - { - Assert.AreEqual(TestKey, key.key); - Assert.IsNull(value); // This is the default value inserted for the Tombstoned record - handlerQueue.Enqueue(DisposeHandler.SingleDeleter); - } - - public override void DisposeDeserializedFromDisk(ref MyKey key, ref MyValue value) - { - VerifyKeyValueCombo(ref key, ref value); - handlerQueue.Enqueue(DisposeHandler.DeserializedFromDisk); - } - } - - static void VerifyKeyValueCombo(ref MyKey key, ref MyValue value) - { - switch (key.key) - { - case TestKey: - Assert.AreEqual(TestInitialValue, value.value); - break; - case TestCollidingKey: - Assert.AreEqual(TestCollidingValue, value.value); - break; - case TestCollidingKey2: - Assert.AreEqual(TestCollidingValue2, value.value); - break; - default: - Assert.Fail($"Unexpected key: {key.key}"); - break; - } - } - - // Override some things from MyFunctions for our purposes here - class DisposeFunctionsNoSync : MyFunctions - { - internal Queue handlerQueue = new(); - - public override bool CopyUpdater(ref MyKey key, ref MyInput input, ref MyValue oldValue, ref MyValue newValue, ref MyOutput output, ref RMWInfo rmwInfo) - { - newValue = new MyValue { value = oldValue.value + input.value }; - output.value = newValue; - return true; - } - - public override void ReadCompletionCallback(ref MyKey key, ref MyInput input, ref MyOutput output, Empty ctx, Status status, RecordMetadata recordMetadata) - { - Assert.IsTrue(status.Found); - } - - public override void DisposeDeserializedFromDisk(ref MyKey key, ref MyValue value) - { - VerifyKeyValueCombo(ref key, ref value); - handlerQueue.Enqueue(DisposeHandler.DeserializedFromDisk); - } - } - - void DoFlush(FlushMode flushMode) - { - switch (flushMode) - { - case FlushMode.NoFlush: - return; - case FlushMode.ReadOnly: - store.Log.ShiftReadOnlyAddress(store.Log.TailAddress, wait: true); - return; - case FlushMode.OnDisk: - store.Log.FlushAndEvict(wait: true); - return; - } - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposeSingleWriter2Threads([Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions1 = new DisposeFunctions(this, sut: true); - var functions2 = new DisposeFunctions(this, sut: false); - - MyKey key = new() { key = TestKey }; - MyKey collidingKey = new() { key = TestCollidingKey }; - MyValue value = new() { value = TestInitialValue }; - MyValue collidingValue = new() { value = TestCollidingValue }; - - void DoUpsert(DisposeFunctions functions) - { - using var innerSession = store.NewSession(functions); - if (functions.isSUT) - innerSession.Upsert(ref key, ref value); - else - { - otherGate.Wait(); - innerSession.Upsert(ref collidingKey, ref collidingValue); - } - } - - var tasks = new[] - { - Task.Factory.StartNew(() => DoUpsert(functions1)), - Task.Factory.StartNew(() => DoUpsert(functions2)) - }; - - Task.WaitAll(tasks); - - Assert.AreEqual(DisposeHandler.SingleWriter, functions1.handlerQueue.Dequeue()); - Assert.IsEmpty(functions1.handlerQueue); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposeInitialUpdater2Threads([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushMode flushMode, - [Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions1 = new DisposeFunctions(this, sut: true); - var functions2 = new DisposeFunctions(this, sut: false); - - MyKey key = new() { key = TestKey }; - MyKey collidingKey = new() { key = TestCollidingKey }; - MyInput input = new() { value = TestInitialValue }; - MyInput collidingInput = new() { value = TestCollidingValue }; - - DoFlush(flushMode); - - void DoInsert(DisposeFunctions functions) - { - using var session = store.NewSession(functions); - if (functions.isSUT) - session.RMW(ref key, ref input); - else - { - otherGate.Wait(); - session.RMW(ref collidingKey, ref collidingInput); - } - } - - var tasks = new[] - { - Task.Factory.StartNew(() => DoInsert(functions1)), - Task.Factory.StartNew(() => DoInsert(functions2)) - }; - Task.WaitAll(tasks); - - Assert.AreEqual(DisposeHandler.InitialUpdater, functions1.handlerQueue.Dequeue()); - Assert.IsEmpty(functions1.handlerQueue); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposeCopyUpdater2Threads([Values(FlushMode.ReadOnly, FlushMode.OnDisk)] FlushMode flushMode, - [Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions1 = new DisposeFunctions(this, sut: true); - var functions2 = new DisposeFunctions(this, sut: false); - - MyKey key = new() { key = TestKey }; - MyKey collidingKey = new() { key = TestCollidingKey }; - { - using var session = store.NewSession(new DisposeFunctionsNoSync()); - MyValue value = new() { value = TestInitialValue }; - session.Upsert(ref key, ref value); - } - - // Make it immutable so CopyUpdater is called. - DoFlush(flushMode); - - void DoUpdate(DisposeFunctions functions) - { - using var session = store.NewSession(functions); - MyInput input = new() { value = functions.isSUT ? TestUpdatedValue : TestCollidingValue }; - if (functions.isSUT) - session.RMW(ref key, ref input); - else - { - otherGate.Wait(); - session.RMW(ref collidingKey, ref input); - } - session.CompletePending(true); - } - - var tasks = new[] - { - Task.Factory.StartNew(() => DoUpdate(functions1)), - Task.Factory.StartNew(() => DoUpdate(functions2)) - }; - Task.WaitAll(tasks); - - // The way this works for OnDisk is: - // SUT sees that the address in the hash entry is below HeadAddress (because everything has been flushed to disk) - // SUT records InitialEntryAddress with the original hash entry address - // SUT goes pending, gets to InternalContinuePendingRMW, calls CreateNewRecordRMW, which calls CopyUpdater - // SUT (in CopyUpdater) signals Other, then blocks - // Other calls InternalRMW and also sees that the address in the hash entry is below HeadAddress, so it goes pending - // Other gets to InternalContinuePendingRMW, sees its key does not exist, and calls InitialUpdater, which signals SUT - // Other returns from InternalContinuePendingRMW, which enqueues DeserializedFromDisk into functions2.handlerQueue - // SUT is now unblocked and returns from CopyUpdater. CAS fails due to Other's insertion - // SUT does the RETRY loop in InternalContinuePendingRMW - // This second loop iteration searches for the record in-memory down to InitialEntryAddress and does not find it. - // It verifies that the lower bound of the search guarantees we searched all in-memory records. - // Therefore SUT calls CreateNewRecordRMW again, which succeeds. - // SUT returns from InternalContinuePendingRMW, which enqueues DeserializedFromDisk into functions1.handlerQueue - Assert.AreEqual(DisposeHandler.CopyUpdater, functions1.handlerQueue.Dequeue()); - if (flushMode == FlushMode.OnDisk) - { - Assert.AreEqual(DisposeHandler.DeserializedFromDisk, functions1.handlerQueue.Dequeue()); - Assert.AreEqual(DisposeHandler.DeserializedFromDisk, functions2.handlerQueue.Dequeue()); - } - Assert.IsEmpty(functions1.handlerQueue); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposeSingleDeleter2Threads([Values(FlushMode.ReadOnly, FlushMode.OnDisk)] FlushMode flushMode, - [Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions1 = new DisposeFunctions(this, sut: true); - var functions2 = new DisposeFunctions(this, sut: false); - - MyKey key = new() { key = TestKey }; - MyKey collidingKey = new() { key = TestCollidingKey }; - - { - using var session = store.NewSession(new DisposeFunctionsNoSync()); - MyValue value = new() { value = TestInitialValue }; - session.Upsert(ref key, ref value); - MyValue collidingValue = new() { value = TestCollidingValue }; - session.Upsert(ref collidingKey, ref collidingValue); - } - - // Make it immutable so we don't simply set Tombstone. - DoFlush(flushMode); - - void DoDelete(DisposeFunctions functions) - { - using var innerSession = store.NewSession(functions); - if (functions.isSUT) - innerSession.Delete(ref key); - else - { - otherGate.Wait(); - innerSession.Delete(ref collidingKey); - } - } - - var tasks = new[] - { - Task.Factory.StartNew(() => DoDelete(functions1)), - Task.Factory.StartNew(() => DoDelete(functions2)) - }; - - Task.WaitAll(tasks); - - Assert.AreEqual(DisposeHandler.SingleDeleter, functions1.handlerQueue.Dequeue()); - Assert.IsEmpty(functions1.handlerQueue); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void PendingRead([Values] ReadCopyDestination copyDest, [Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - - { - DoPendingReadInsertTest(copyDest, initialReadCacheInsert: false); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void CopyToTailWithInitialReadCache([Values(ReadCopyDestination.ReadCache)] ReadCopyDestination copyDest, - [Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - // We use the ReadCopyDestination.ReadCache parameter so Setup() knows to set up the readcache, but - // for the actual test it is used only for setup; we execute CopyToTail. - DoPendingReadInsertTest(ReadCopyDestination.Tail, initialReadCacheInsert: true); - } - - void DoPendingReadInsertTest(ReadCopyDestination copyDest, bool initialReadCacheInsert) - { - MyKey key = new() { key = TestKey }; - MyKey collidingKey2 = new() { key = TestCollidingKey2 }; - MyValue collidingValue2 = new() { value = TestCollidingValue2 }; - - using var session = store.NewSession(new DisposeFunctionsNoSync()); - - // Do initial insert(s) to set things up - { - MyValue value = new() { value = TestInitialValue }; - session.Upsert(ref key, ref value); - if (initialReadCacheInsert) - session.Upsert(ref collidingKey2, ref collidingValue2); - } - - // FlushAndEvict so we go pending - DoFlush(FlushMode.OnDisk); - - MyOutput output = new(); - MyInput input = new(); - - if (initialReadCacheInsert) - { - Assert.IsTrue(session.Read(ref collidingKey2, ref output).IsPending); - session.CompletePending(wait: true); - } - - ReadOptions readOptions = new() { CopyFrom = ReadCopyFrom.Device }; - if (copyDest == ReadCopyDestination.Tail) - readOptions.CopyOptions.CopyTo = ReadCopyTo.MainLog; - var status = session.Read(ref key, ref input, ref output, ref readOptions, out _); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.AreEqual(TestInitialValue, output.value.value); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposePendingRead2Threads([Values] ReadCopyDestination copyDest, [Values] ConcurrencyControlMode concurrencyControlMode) - { - DoDisposePendingReadInsertTest2Threads(copyDest, initialReadCacheInsert: false); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposeCopyToTailWithInitialReadCache2Threads([Values(ReadCopyDestination.ReadCache)] ReadCopyDestination copyDest, [Values] ConcurrencyControlMode concurrencyControlMode) - { - // We use the ReadCopyDestination.ReadCache parameter so Setup() knows to set up the readcache, but - // for the actual test it is used only for setup; we execute CopyToTail. - DoDisposePendingReadInsertTest2Threads(ReadCopyDestination.Tail, initialReadCacheInsert: true); - } - - void DoDisposePendingReadInsertTest2Threads(ReadCopyDestination copyDest, bool initialReadCacheInsert) - { - var functions1 = new DisposeFunctions(this, sut: true, splice: initialReadCacheInsert); - var functions2 = new DisposeFunctions(this, sut: false); - - MyKey key = new() { key = TestKey }; - MyKey collidingKey = new() { key = TestCollidingKey }; - MyValue collidingValue = new() { value = TestCollidingValue }; - MyKey collidingKey2 = new() { key = TestCollidingKey2 }; - MyValue collidingValue2 = new() { value = TestCollidingValue2 }; - - // Do initial insert(s) to set things up - { - using var session = store.NewSession(new DisposeFunctionsNoSync()); - MyValue value = new() { value = TestInitialValue }; - session.Upsert(ref key, ref value); - session.Upsert(ref collidingKey, ref collidingValue); - if (initialReadCacheInsert) - session.Upsert(ref collidingKey2, ref collidingValue2); - } - - // FlushAndEvict so we go pending - DoFlush(FlushMode.OnDisk); - - if (initialReadCacheInsert) - { - using var session = store.NewSession(new DisposeFunctionsNoSync()); - MyOutput output = new(); - var status = session.Read(ref collidingKey2, ref output); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(wait: true); - } - - // We use Read() only here (not Upsert()), so we have only read locks and thus do not self-deadlock with an XLock on the colliding bucket. - void DoRead(DisposeFunctions functions) - { - MyOutput output = new(); - MyInput input = new(); - ReadOptions readOptions = new() { CopyFrom = ReadCopyFrom.Device }; - if (copyDest == ReadCopyDestination.Tail) - readOptions.CopyOptions.CopyTo = ReadCopyTo.MainLog; - - using var session = store.NewSession(functions); - if (functions.isSUT) - { - var status = session.Read(ref key, ref input, ref output, ref readOptions, out _); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.IsTrue(status.Found, status.ToString()); - Assert.AreEqual(TestInitialValue, output.value.value); - } - else - { - otherGate.Wait(); - var status = session.Read(ref collidingKey, ref input, ref output, ref readOptions, out _); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.IsTrue(status.Found, status.ToString()); - Assert.AreEqual(TestCollidingValue, output.value.value); - } - } - - var tasks = new[] - { - Task.Factory.StartNew(() => DoRead(functions1)), - Task.Factory.StartNew(() => DoRead(functions2)) - }; - Task.WaitAll(tasks); - - if (store.LockTable.IsEnabled || !initialReadCacheInsert) // This allows true splice, so we generated a conflict. - Assert.AreEqual(DisposeHandler.SingleWriter, functions1.handlerQueue.Dequeue()); - Assert.AreEqual(DisposeHandler.DeserializedFromDisk, functions1.handlerQueue.Dequeue()); - Assert.IsEmpty(functions1.handlerQueue); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposePendingReadWithNoInsertTest([Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions = new DisposeFunctionsNoSync(); - - MyKey key = new() { key = TestKey }; - MyValue value = new() { value = TestInitialValue }; - - // Do initial insert - using var session = store.NewSession(functions); - session.Upsert(ref key, ref value); - - // FlushAndEvict so we go pending - DoFlush(FlushMode.OnDisk); - - MyOutput output = new(); - var status = session.Read(ref key, ref output); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.AreEqual(TestInitialValue, output.value.value); - - Assert.AreEqual(DisposeHandler.DeserializedFromDisk, functions.handlerQueue.Dequeue()); - } - - [Test] - [Category("TsavoriteKV")] - [Category("Smoke")] - public void DisposePendingRmwWithNoConflictTest([Values(ConcurrencyControlMode.None)] ConcurrencyControlMode concurrencyControlMode) - { - var functions = new DisposeFunctionsNoSync(); - - MyKey key = new() { key = TestKey }; - MyValue value = new() { value = TestInitialValue }; - - // Do initial insert - using var session = store.NewSession(functions); - session.Upsert(ref key, ref value); - - // FlushAndEvict so we go pending - DoFlush(FlushMode.OnDisk); - - MyInput input = new() { value = TestUpdatedValue }; - MyOutput output = new(); - var status = session.RMW(ref key, ref input, ref output); - Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); - (status, output) = GetSinglePendingResult(completedOutputs); - Assert.AreEqual(TestInitialValue + TestUpdatedValue, output.value.value); - - Assert.AreEqual(DisposeHandler.DeserializedFromDisk, functions.handlerQueue.Dequeue()); - } - } -} -#endif -#pragma warning restore IDE0055 \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs b/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs index 6639f87911..1f89d60725 100644 --- a/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs @@ -26,8 +26,7 @@ public void Setup() store = new TsavoriteKV (128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 14, PageSizeBits = 9 }, - serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: ConcurrencyControlMode.None); + serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }); session = store.NewSession(new MyFunctionsDelete()); bContext = session.BasicContext; } diff --git a/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs b/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs index c5cdfc94a4..befba6d496 100644 --- a/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs @@ -28,12 +28,10 @@ public void Setup() private void InternalSetup(ScanIteratorType scanIteratorType, bool largeMemory) { - // Default ConcurrencyControlMode for this iterator type. - var concurrencyControlMode = scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable; - InternalSetup(concurrencyControlMode, largeMemory); + InternalSetup(largeMemory); } - private void InternalSetup(ConcurrencyControlMode concurrencyControlMode, bool largeMemory) + private void InternalSetup(bool largeMemory) { // Broke this out as we have different requirements by test. log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "GenericIterationTests.log"), deleteOnClose: true); @@ -42,8 +40,7 @@ private void InternalSetup(ConcurrencyControlMode concurrencyControlMode, bool l store = new TsavoriteKV (128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = largeMemory ? 25 : 14, PageSizeBits = largeMemory ? 20 : 9 }, - serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: concurrencyControlMode); + serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }); session = store.NewSession(new MyFunctionsDelete()); bContext = session.BasicContext; } @@ -202,9 +199,9 @@ void scanAndVerify(int stopAt, bool useScan) [Test] [Category(TsavoriteKVTestCategory)] [Category(SmokeTestCategory)] - public unsafe void GenericIterationPushLockTest([Values(1, 4)] int scanThreads, [Values(1, 4)] int updateThreads, [Values] ConcurrencyControlMode concurrencyControlMode, [Values] ScanMode scanMode) + public unsafe void GenericIterationPushLockTest([Values(1, 4)] int scanThreads, [Values(1, 4)] int updateThreads, [Values] ScanMode scanMode) { - InternalSetup(concurrencyControlMode, largeMemory: true); + InternalSetup(largeMemory: true); const int totalRecords = 2000; var start = store.Log.TailAddress; diff --git a/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs b/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs index fc746b43b5..25d3287eb5 100644 --- a/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs @@ -97,9 +97,7 @@ public void DiskWriteScanBasicTest([Values] DeviceType deviceType, [Values] Scan objlog = CreateTestDevice(deviceType, Path.Join(MethodTestDir, $"DiskWriteScanBasicTest_{deviceType}.obj.log")); store = new(128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }, - serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable - ); + serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }); using var session = store.NewSession(new MyFunctions()); var bContext = session.BasicContext; @@ -173,8 +171,7 @@ public void BlittableScanJumpToBeginAddressTest() objlog = Devices.CreateLogDevice(Path.Join(MethodTestDir, "test.obj.log")); store = new(128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 20, PageSizeBits = 15, SegmentSizeBits = 18 }, - serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: ConcurrencyControlMode.None); + serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }); using var session = store.NewSession(new MyFunctions()); var bContext = session.BasicContext; @@ -244,7 +241,7 @@ public void GenericScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred)] store = new(128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 20, PageSizeBits = 15, SegmentSizeBits = 18 }, serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); + comparer: comparer); using var session = store.NewSession(new ScanFunctions()); var bContext = session.BasicContext; @@ -336,7 +333,7 @@ public void GenericScanCursorFilterTest([Values(HashModulo.NoMod, HashModulo.Hun store = new(128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 20, PageSizeBits = 15, SegmentSizeBits = 18 }, serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); + comparer: comparer); using var session = store.NewSession(new ScanFunctions()); var bContext = session.BasicContext; diff --git a/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs b/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs index de753bd64e..faa1a26c66 100644 --- a/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs +++ b/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs @@ -182,8 +182,7 @@ public void Setup(bool forRecovery) functions = new LockableUnsafeFunctions(); store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 22, ReadCacheSettings = readCacheSettings }, - checkpointSettings: checkpointSettings, comparer: comparer, - concurrencyControlMode: ConcurrencyControlMode.LockTable); + checkpointSettings: checkpointSettings, comparer: comparer); session = store.NewSession(functions); bContext = session.BasicContext; } @@ -879,7 +878,7 @@ FixedLengthLockableKeyStruct AddLockTableEntry(LockableUnsafeC HashEntryInfo hei = new(comparer.GetHashCode64(ref key)); PopulateHei(ref hei); - var lockState = store.LockTable.GetLockState(ref key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.IsTrue(lockState.IsFound); Assert.IsTrue(lockState.IsLockedExclusive); @@ -1158,7 +1157,7 @@ public void LockAndUnlockInLockTableOnlyTest() ref var key = ref keyVec[idx]; HashEntryInfo hei = new(key.KeyHash); PopulateHei(ref hei); - var lockState = store.LockTable.GetLockState(ref key.Key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.IsTrue(lockState.IsFound); Assert.AreEqual(key.LockType == LockType.Exclusive, lockState.IsLockedExclusive); if (key.LockType == LockType.Shared) diff --git a/libs/storage/Tsavorite/cs/test/MiscTests.cs b/libs/storage/Tsavorite/cs/test/MiscTests.cs index cf7361559e..074a9bab38 100644 --- a/libs/storage/Tsavorite/cs/test/MiscTests.cs +++ b/libs/storage/Tsavorite/cs/test/MiscTests.cs @@ -121,8 +121,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog1.log"), deleteOnClose: true); store = new TsavoriteKV (128, new LogSettings { LogDevice = log, MemorySizeBits = 29 }, - checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir }, - concurrencyControlMode: ConcurrencyControlMode.None); + checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir }); session = store.NewSession(copyOnWrite); var bContext = session.BasicContext; @@ -171,8 +170,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update store = new TsavoriteKV (128, new LogSettings { LogDevice = log, MemorySizeBits = 29 }, - checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir }, - concurrencyControlMode: ConcurrencyControlMode.None); + checkpointSettings: new CheckpointSettings { CheckpointDir = checkpointDir }); store.Recover(token); session = store.NewSession(copyOnWrite); diff --git a/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs b/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs index 318b8c2d4f..19af9d173d 100644 --- a/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs +++ b/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs @@ -36,7 +36,7 @@ public void Setup() { log = Devices.CreateLogDevice(Path.Combine(MethodTestDir, "test.log"), deleteOnClose: false); comparer = new ModifiedBitTestComparer(); - store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 22 }, comparer: comparer, concurrencyControlMode: ConcurrencyControlMode.LockTable); + store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 22 }, comparer: comparer); session = store.NewSession>(new SimpleSimpleFunctions()); bContext = session.BasicContext; } diff --git a/libs/storage/Tsavorite/cs/test/OverflowBucketLockTableTests.cs b/libs/storage/Tsavorite/cs/test/OverflowBucketLockTableTests.cs index cbec4ec440..96fb6cb047 100644 --- a/libs/storage/Tsavorite/cs/test/OverflowBucketLockTableTests.cs +++ b/libs/storage/Tsavorite/cs/test/OverflowBucketLockTableTests.cs @@ -50,7 +50,7 @@ public void Setup() comparer ??= new LongTsavoriteEqualityComparer(); store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 22 }, - comparer: comparer, concurrencyControlMode: ConcurrencyControlMode.LockTable); + comparer: comparer); } [TearDown] @@ -64,26 +64,29 @@ public void TearDown() DeleteDirectory(MethodTestDir); } - void TryLock(long key, LockType lockType, bool transient, int expectedCurrentReadLocks, bool expectedLockResult) + void TryLock(long key, LockType lockType, int expectedCurrentReadLocks, bool expectedLockResult) { HashEntryInfo hei = new(comparer.GetHashCode64(ref key)); PopulateHei(ref hei); // Check for existing lock - var lockState = store.LockTable.GetLockState(ref key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.AreEqual(expectedCurrentReadLocks, lockState.NumLockedShared); - if (transient) - Assert.AreEqual(expectedLockResult, store.LockTable.TryLockTransient(ref key, ref hei, lockType)); + if (lockType == LockType.Shared) + Assert.AreEqual(expectedLockResult, store.LockTable.TryLockShared(ref hei)); else - Assert.AreEqual(expectedLockResult, store.LockTable.TryLockManual(ref key, ref hei, lockType)); + Assert.AreEqual(expectedLockResult, store.LockTable.TryLockExclusive(ref hei)); } void Unlock(long key, LockType lockType) { HashEntryInfo hei = new(comparer.GetHashCode64(ref key)); PopulateHei(ref hei); - store.LockTable.Unlock(ref key, ref hei, lockType); + if (lockType == LockType.Shared) + store.LockTable.UnlockShared(ref hei); + else + store.LockTable.UnlockExclusive(ref hei); } internal void PopulateHei(ref HashEntryInfo hei) => PopulateHei(store, ref hei); @@ -92,7 +95,7 @@ void Unlock(long key, LockType lockType) internal void AssertLockCounts(ref HashEntryInfo hei, bool expectedX, long expectedS) { - var lockState = store.LockTable.GetLockState(ref SingleBucketKey, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.AreEqual(expectedX, lockState.IsLockedExclusive); Assert.AreEqual(expectedS, lockState.NumLockedShared); } @@ -104,7 +107,7 @@ internal static void AssertLockCounts(TsavoriteKV st { HashEntryInfo hei = new(store.comparer.GetHashCode64(ref key)); PopulateHei(store, ref hei); - var lockState = store.LockTable.GetLockState(ref key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.AreEqual(expectedX, lockState.IsLockedExclusive, "XLock mismatch"); Assert.AreEqual(expectedS, lockState.NumLockedShared, "SLock mismatch"); } @@ -126,7 +129,7 @@ internal static void AssertLockCounts(TsavoriteKV st { HashEntryInfo hei = new(key.KeyHash); PopulateHei(store, ref hei); - var lockState = store.LockTable.GetLockState(ref key.Key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.AreEqual(expectedX, lockState.IsLockedExclusive, "XLock mismatch"); Assert.AreEqual(expectedS, lockState.NumLockedShared > 0, "SLock mismatch"); } @@ -169,17 +172,17 @@ public void SingleKeyTest([Values] UseSingleBucketComparer /* justToSignalSetup // No entries long key = 1; - TryLock(key, LockType.Shared, transient: true, expectedCurrentReadLocks: 0, expectedLockResult: true); + TryLock(key, LockType.Shared, expectedCurrentReadLocks: 0, expectedLockResult: true); AssertLockCounts(ref hei, false, 1); // Add a non-transient lock - TryLock(key, LockType.Shared, transient: false, expectedCurrentReadLocks: 1, expectedLockResult: true); + TryLock(key, LockType.Shared, expectedCurrentReadLocks: 1, expectedLockResult: true); AssertLockCounts(ref hei, false, 2); // Now both transient and manual x locks with the same key should fail - TryLock(key, LockType.Exclusive, transient: true, expectedCurrentReadLocks: 2, expectedLockResult: false); + TryLock(key, LockType.Exclusive, expectedCurrentReadLocks: 2, expectedLockResult: false); AssertLockCounts(ref hei, false, 2); - TryLock(key, LockType.Exclusive, transient: false, expectedCurrentReadLocks: 2, expectedLockResult: false); + TryLock(key, LockType.Exclusive, expectedCurrentReadLocks: 2, expectedLockResult: false); AssertLockCounts(ref hei, false, 2); // Now unlock @@ -189,7 +192,7 @@ public void SingleKeyTest([Values] UseSingleBucketComparer /* justToSignalSetup AssertLockCounts(ref hei, false, 0); // Now exclusive should succeed - TryLock(key, LockType.Exclusive, transient: false, expectedCurrentReadLocks: 0, expectedLockResult: true); + TryLock(key, LockType.Exclusive, expectedCurrentReadLocks: 0, expectedLockResult: true); AssertLockCounts(ref hei, true, 0); Unlock(key, LockType.Exclusive); AssertLockCounts(ref hei, false, 0); @@ -203,17 +206,17 @@ public void ThreeKeyTest([Values] UseSingleBucketComparer /* justToSignalSetup * PopulateHei(ref hei); AssertLockCounts(ref hei, false, 0); - TryLock(1, LockType.Shared, transient: false, expectedCurrentReadLocks: 0, expectedLockResult: true); + TryLock(1, LockType.Shared, expectedCurrentReadLocks: 0, expectedLockResult: true); AssertLockCounts(ref hei, false, 1); - TryLock(2, LockType.Shared, transient: false, expectedCurrentReadLocks: 1, expectedLockResult: true); + TryLock(2, LockType.Shared, expectedCurrentReadLocks: 1, expectedLockResult: true); AssertLockCounts(ref hei, false, 2); - TryLock(3, LockType.Shared, transient: false, expectedCurrentReadLocks: 2, expectedLockResult: true); + TryLock(3, LockType.Shared, expectedCurrentReadLocks: 2, expectedLockResult: true); AssertLockCounts(ref hei, false, 3); // Exclusive lock should fail - TryLock(4, LockType.Exclusive, transient: false, expectedCurrentReadLocks: 3, expectedLockResult: false); + TryLock(4, LockType.Exclusive, expectedCurrentReadLocks: 3, expectedLockResult: false); AssertLockCounts(ref hei, false, 3); // Now unlock @@ -225,7 +228,7 @@ public void ThreeKeyTest([Values] UseSingleBucketComparer /* justToSignalSetup * AssertLockCounts(ref hei, false, 0); // Now exclusive should succeed - TryLock(4, LockType.Exclusive, transient: false, expectedCurrentReadLocks: 0, expectedLockResult: true); + TryLock(4, LockType.Exclusive, expectedCurrentReadLocks: 0, expectedLockResult: true); AssertLockCounts(ref hei, true, 0); Unlock(4, LockType.Exclusive); AssertLockCounts(ref hei, false, 0); @@ -402,8 +405,10 @@ long getNextKey() { HashEntryInfo hei = new(threadStructs[ii].KeyHash); PopulateHei(ref hei); - while (!store.LockTable.TryLockManual(ref threadStructs[ii].Key, ref hei, threadStructs[ii].LockType)) - ; + if (threadStructs[ii].LockType == LockType.Shared) + while (!store.LockTable.TryLockShared(ref hei)) { } + else + while (!store.LockTable.TryLockExclusive(ref hei)) { } } // Pretend to do work @@ -414,7 +419,10 @@ long getNextKey() { HashEntryInfo hei = new(threadStructs[ii].KeyHash); PopulateHei(ref hei); - store.LockTable.Unlock(ref threadStructs[ii].Key, ref hei, threadStructs[ii].LockType); + if (threadStructs[ii].LockType == LockType.Shared) + store.LockTable.UnlockShared(ref hei); + else + store.LockTable.UnlockExclusive(ref hei); } Array.Clear(threadStructs); } diff --git a/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs b/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs index ea1c96b8b6..d3dad8bcca 100644 --- a/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs +++ b/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs @@ -149,7 +149,7 @@ private class TestStore : IDisposable internal long[] InsertAddresses = new long[numKeys]; - internal TestStore(bool useReadCache, ReadCopyOptions readCopyOptions, bool flush, ConcurrencyControlMode concurrencyControlMode) + internal TestStore(bool useReadCache, ReadCopyOptions readCopyOptions, bool flush) { DeleteDirectory(MethodTestDir, wait: true); logDevice = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog.log")); @@ -171,8 +171,7 @@ internal TestStore(bool useReadCache, ReadCopyOptions readCopyOptions, bool flus logSettings: logSettings, checkpointSettings: new CheckpointSettings { CheckpointDir = Path.Join(MethodTestDir, "chkpt") }, serializerSettings: null, - comparer: new Key.Comparer(), - concurrencyControlMode: concurrencyControlMode + comparer: new Key.Comparer() ); } @@ -262,18 +261,18 @@ public void Dispose() } // readCache and copyReadsToTail are mutually exclusive and orthogonal to populating by RMW vs. Upsert. - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk)] [Category("TsavoriteKV"), Category("Read")] - public void VersionedReadTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode, [Values] ConcurrencyControlMode concurrencyControlMode) + public void VersionedReadTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode) { var useReadCache = urc == UseReadCache.ReadCache; var readCopyOptions = new ReadCopyOptions(readCopyFrom, readCopyTo); - using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); + using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk); testStore.Populate(updateOp == UpdateOp.RMW).GetAwaiter().GetResult(); using var session = testStore.store.NewSession(new Functions()); var bContext = session.BasicContext; @@ -337,13 +336,13 @@ public void OnStop(bool completed, long numberOfRecords) { } } [Test, Category(TsavoriteKVTestCategory), Category(ReadTestCategory)] - public void IterateKeyTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushMode flushMode, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] ConcurrencyControlMode concurrencyControlMode) + public void IterateKeyTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushMode flushMode, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { // Upsert does not provide the preserveCopyUpdaterSource option, so we cannot check it in NoFlush; the record will always be elided. if (flushMode == FlushMode.NoFlush && updateOp == UpdateOp.Upsert) Assert.Ignore("Cannot test NoFlush with Upsert"); - using var testStore = new TestStore(useReadCache: false, ReadCopyOptions.None, flushMode != FlushMode.NoFlush, concurrencyControlMode); + using var testStore = new TestStore(useReadCache: false, ReadCopyOptions.None, flushMode != FlushMode.NoFlush); testStore.Populate(useRMW: updateOp == UpdateOp.RMW, preserveCopyUpdaterSource: true).GetAwaiter().GetResult(); for (int iteration = 0; iteration < 2; ++iteration) @@ -356,13 +355,13 @@ public void IterateKeyTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushM } [Test, Category(TsavoriteKVTestCategory), Category(ReadTestCategory)] - public void IterateKeyStopTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushMode flushMode, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] ConcurrencyControlMode concurrencyControlMode) + public void IterateKeyStopTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] FlushMode flushMode, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { // Upsert does not provide the preserveCopyUpdaterSource option, so we cannot check it in NoFlush; the record will always be elided. if (flushMode == FlushMode.NoFlush && updateOp == UpdateOp.Upsert) Assert.Ignore("Cannot test NoFlush with Upsert"); - using var testStore = new TestStore(useReadCache: false, ReadCopyOptions.None, flushMode != FlushMode.NoFlush, concurrencyControlMode); + using var testStore = new TestStore(useReadCache: false, ReadCopyOptions.None, flushMode != FlushMode.NoFlush); testStore.Populate(updateOp == UpdateOp.RMW, preserveCopyUpdaterSource: true).GetAwaiter().GetResult(); for (int iteration = 0; iteration < 2; ++iteration) @@ -375,18 +374,18 @@ public void IterateKeyStopTests([Values(FlushMode.NoFlush, FlushMode.OnDisk)] Fl } // readCache and copyReadsToTail are mutually exclusive and orthogonal to populating by RMW vs. Upsert. - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk)] [Category("TsavoriteKV"), Category("Read")] - public void ReadAtAddressTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode, [Values] ConcurrencyControlMode concurrencyControlMode) + public void ReadAtAddressTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode) { var useReadCache = urc == UseReadCache.ReadCache; var readCopyOptions = new ReadCopyOptions(readCopyFrom, readCopyTo); - using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); + using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk); testStore.Populate(updateOp == UpdateOp.RMW).GetAwaiter().GetResult(); using var session = testStore.store.NewSession(new Functions()); var bContext = session.BasicContext; @@ -421,18 +420,18 @@ public void ReadAtAddressTests(UseReadCache urc, ReadCopyFrom readCopyFrom, Read } // Test is similar to others but tests the Overload where RadCopy*.None is set -- probably don't need all combinations of test but doesn't hurt - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk)] [Category("TsavoriteKV"), Category("Read")] - public async Task ReadAtAddressCopyOptNoRcTest(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode, [Values] ConcurrencyControlMode concurrencyControlMode) + public async Task ReadAtAddressCopyOptNoRcTest(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode) { var useReadCache = urc == UseReadCache.ReadCache; var readCopyOptions = new ReadCopyOptions(readCopyFrom, readCopyTo); - using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); + using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk); await testStore.Populate(updateOp == UpdateOp.RMW); using var session = testStore.store.NewSession(new Functions()); var bContext = session.BasicContext; @@ -463,18 +462,18 @@ public async Task ReadAtAddressCopyOptNoRcTest(UseReadCache urc, ReadCopyFrom re } // readCache and copyReadsToTail are mutually exclusive and orthogonal to populating by RMW vs. Upsert. - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush, ConcurrencyControlMode.None)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] - [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk, ConcurrencyControlMode.LockTable)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.NoFlush)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.NoReadCache, ReadCopyFrom.Device, ReadCopyTo.MainLog, UpdateOp.RMW, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.Upsert, FlushMode.OnDisk)] + [TestCase(UseReadCache.ReadCache, ReadCopyFrom.None, ReadCopyTo.None, UpdateOp.RMW, FlushMode.OnDisk)] [Category("TsavoriteKV"), Category("Read")] - public async ValueTask ReadNoKeyTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode, [Values] ConcurrencyControlMode concurrencyControlMode) + public async ValueTask ReadNoKeyTests(UseReadCache urc, ReadCopyFrom readCopyFrom, ReadCopyTo readCopyTo, UpdateOp updateOp, FlushMode flushMode) { var useReadCache = urc == UseReadCache.ReadCache; var readCopyOptions = new ReadCopyOptions(readCopyFrom, readCopyTo); - using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); + using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk); await testStore.Populate(updateOp == UpdateOp.RMW); using var session = testStore.store.NewSession(new Functions()); var bContext = session.BasicContext; diff --git a/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs b/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs index 234d28aa71..7dc82ac10f 100644 --- a/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs +++ b/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs @@ -63,19 +63,9 @@ public void Setup() var readCacheSettings = new ReadCacheSettings { MemorySizeBits = 15, PageSizeBits = 9 }; log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "NativeReadCacheTests.log"), deleteOnClose: true); - var concurrencyControlMode = ConcurrencyControlMode.None; - foreach (var arg in TestContext.CurrentContext.Test.Arguments) - { - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } - } - store = new TsavoriteKV (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 10, ReadCacheSettings = readCacheSettings }, - comparer: new ChainComparer(mod), concurrencyControlMode: concurrencyControlMode); + comparer: new ChainComparer(mod)); } [TearDown] @@ -300,7 +290,7 @@ public void ChainVerificationTest() [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void DeleteCacheRecordTest([Values] ConcurrencyControlMode concurrencyControlMode) + public void DeleteCacheRecordTest() { PopulateAndEvict(); CreateChain(); @@ -329,7 +319,7 @@ void doTest(long key) [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void DeleteHalfOfAllReadCacheRecordsTest([Values] ConcurrencyControlMode concurrencyControlMode) + public void DeleteHalfOfAllReadCacheRecordsTest() { PopulateAndEvict(); CreateChain(); @@ -382,7 +372,7 @@ void doTest(long key) [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void UpsertCacheRecordTest([Values] ConcurrencyControlMode concurrencyControlMode) + public void UpsertCacheRecordTest() { DoUpdateTest(useRMW: false); } @@ -391,7 +381,7 @@ public void UpsertCacheRecordTest([Values] ConcurrencyControlMode concurrencyCon [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void RMWCacheRecordTest([Values] ConcurrencyControlMode concurrencyControlMode) + public void RMWCacheRecordTest() { DoUpdateTest(useRMW: true); } @@ -441,7 +431,7 @@ void doTest(long key) [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void SpliceInFromCTTTest([Values] ConcurrencyControlMode concurrencyControlMode) + public void SpliceInFromCTTTest() { PopulateAndEvict(); CreateChain(); @@ -463,7 +453,7 @@ public void SpliceInFromCTTTest([Values] ConcurrencyControlMode concurrencyContr [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void SpliceInFromUpsertTest([Values] RecordRegion recordRegion, [Values] ConcurrencyControlMode concurrencyControlMode) + public void SpliceInFromUpsertTest([Values] RecordRegion recordRegion) { PopulateAndEvict(recordRegion); CreateChain(recordRegion); @@ -493,7 +483,7 @@ public void SpliceInFromUpsertTest([Values] RecordRegion recordRegion, [Values] [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void SpliceInFromRMWTest([Values] RecordRegion recordRegion, [Values] ConcurrencyControlMode concurrencyControlMode) + public void SpliceInFromRMWTest([Values] RecordRegion recordRegion) { PopulateAndEvict(recordRegion); CreateChain(recordRegion); @@ -541,7 +531,7 @@ public void SpliceInFromRMWTest([Values] RecordRegion recordRegion, [Values] Con [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void SpliceInFromDeleteTest([Values] RecordRegion recordRegion, [Values] ConcurrencyControlMode concurrencyControlMode) + public void SpliceInFromDeleteTest([Values] RecordRegion recordRegion) { PopulateAndEvict(recordRegion); CreateChain(recordRegion); @@ -570,7 +560,7 @@ public void SpliceInFromDeleteTest([Values] RecordRegion recordRegion, [Values] [Category(TsavoriteKVTestCategory)] [Category(ReadCacheTestCategory)] [Category(SmokeTestCategory)] - public void VerifyLockCountsAfterReadCacheEvict([Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void VerifyLockCountsAfterReadCacheEvict() { PopulateAndEvict(); CreateChain(); @@ -613,13 +603,13 @@ public void VerifyLockCountsAfterReadCacheEvict([Values(ConcurrencyControlMode.L HashEntryInfo hei = new(store.comparer.GetHashCode64(ref key.Key)); OverflowBucketLockTableTests.PopulateHei(store, ref hei); - var lockState = store.LockTable.GetLockState(ref key.Key, ref hei); + var lockState = store.LockTable.GetLockState(ref hei); Assert.IsTrue(lockState.IsFound); Assert.AreEqual(key.LockType == LockType.Exclusive, lockState.IsLockedExclusive); Assert.AreEqual(key.LockType != LockType.Exclusive, lockState.NumLockedShared > 0); luContext.Unlock(keys, idx, 1); - lockState = store.LockTable.GetLockState(ref key.Key, ref hei); + lockState = store.LockTable.GetLockState(ref hei); Assert.IsFalse(lockState.IsLockedExclusive); Assert.AreEqual(0, lockState.NumLockedShared); } @@ -694,8 +684,7 @@ public void Setup() } } - store = new TsavoriteKV(1L << 20, logSettings, comparer: new LongComparerModulo(modRange), - concurrencyControlMode: ConcurrencyControlMode.LockTable); + store = new TsavoriteKV(1L << 20, logSettings, comparer: new LongComparerModulo(modRange)); } [TearDown] @@ -944,8 +933,7 @@ public void Setup() } } - store = new TsavoriteKV(1L << 20, logSettings, comparer: new SpanByteComparerModulo(modRange), - concurrencyControlMode: ConcurrencyControlMode.LockTable); + store = new TsavoriteKV(1L << 20, logSettings, comparer: new SpanByteComparerModulo(modRange)); } [TearDown] diff --git a/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs b/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs index 905c4cc7a8..a65a9fe03e 100644 --- a/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs +++ b/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs @@ -614,9 +614,7 @@ public async ValueTask RecoveryCheck5([Values] CheckpointType checkpointType, [V using var store1 = new TsavoriteKV (size, logSettings: new LogSettings { LogDevice = log, MutableFraction = 1, PageSizeBits = 10, MemorySizeBits = 14, ReadCacheSettings = useReadCache ? new ReadCacheSettings() : null }, - checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir }, - concurrencyControlMode: ConcurrencyControlMode.LockTable - ); + checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir }); using var s1 = store1.NewSession(new MyFunctions()); var bc1 = s1.BasicContext; diff --git a/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs b/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs index 9083a57765..bedadb84af 100644 --- a/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs +++ b/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs @@ -59,7 +59,6 @@ public void Setup() ReadCacheSettings readCacheSettings = default; string filename = Path.Join(MethodTestDir, "BasicTests.log"); - var concurrencyControlMode = ConcurrencyControlMode.None; foreach (var arg in TestContext.CurrentContext.Test.Arguments) { if (arg is ReadCacheMode rcm) @@ -73,11 +72,6 @@ public void Setup() }; continue; } - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } if (arg is DeviceType deviceType) { log = CreateTestDevice(deviceType, filename, deleteOnClose: true); @@ -94,7 +88,7 @@ public void Setup() MemorySizeBits = 15, PageSizeBits = 12, ReadCacheSettings = readCacheSettings, - }, concurrencyControlMode: concurrencyControlMode); + }); } [TearDown] @@ -112,8 +106,7 @@ public void TearDown() [Category(ReadCacheTestCategory)] [Category(StressTestCategory)] //[Repeat(300)] - public unsafe void RandomReadCacheTest([Values(1, 2, 8)] int numThreads, [Values] KeyContentionMode keyContentionMode, - [Values] ConcurrencyControlMode concurrencyControlMode, [Values] ReadCacheMode readCacheMode) + public unsafe void RandomReadCacheTest([Values(1, 2, 8)] int numThreads, [Values] KeyContentionMode keyContentionMode, [Values] ReadCacheMode readCacheMode) { if (numThreads == 1 && keyContentionMode == KeyContentionMode.Contention) Assert.Ignore("Skipped because 1 thread cannot have contention"); diff --git a/libs/storage/Tsavorite/cs/test/RevivificationTests.cs b/libs/storage/Tsavorite/cs/test/RevivificationTests.cs index 6d43443a9c..2dc5efc9ce 100644 --- a/libs/storage/Tsavorite/cs/test/RevivificationTests.cs +++ b/libs/storage/Tsavorite/cs/test/RevivificationTests.cs @@ -190,16 +190,10 @@ public void Setup() DeleteDirectory(MethodTestDir, wait: true); log = Devices.CreateLogDevice(Path.Combine(MethodTestDir, "test.log"), deleteOnClose: true); - var concurrencyControlMode = ConcurrencyControlMode.LockTable; double? revivifiableFraction = default; RecordElision? recordElision = default; foreach (var arg in TestContext.CurrentContext.Test.Arguments) { - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } if (arg is RevivifiableFraction frac) { revivifiableFraction = RevivificationTestUtils.GetRevivifiableFraction(frac); @@ -218,7 +212,7 @@ public void Setup() if (recordElision.HasValue) revivificationSettings.RestoreDeletedRecordsIfBinIsFull = recordElision.Value == RecordElision.NoElide; store = new TsavoriteKV(1L << 18, new LogSettings { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 12, MemorySizeBits = 20 }, - concurrencyControlMode: concurrencyControlMode, revivificationSettings: revivificationSettings); + revivificationSettings: revivificationSettings); functions = new RevivificationFixedLenFunctions(); session = store.NewSession(functions); bContext = session.BasicContext; @@ -249,9 +243,7 @@ void Populate() [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SimpleFixedLenTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleFixedLenTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -289,9 +281,7 @@ public void SimpleFixedLenTest([Values] DeleteDest deleteDest, [Values(UpdateOp. [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void UnelideTest([Values] RecordElision elision, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void UnelideTest([Values] RecordElision elision, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -333,9 +323,7 @@ public void UnelideTest([Values] RecordElision elision, [Values(UpdateOp.Upsert, [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "revivifiableFraction and concurrencyControlMode are used by Setup")] - public void SimpleMinAddressAddTest([Values] RevivifiableFraction revivifiableFraction, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleMinAddressAddTest([Values] RevivifiableFraction revivifiableFraction) { Populate(); @@ -351,9 +339,7 @@ public void SimpleMinAddressAddTest([Values] RevivifiableFraction revivifiableFr [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "revivifiableFraction and concurrencyControlMode are used by Setup")] - public void SimpleMinAddressTakeTest([Values] RevivifiableFraction revivifiableFraction, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleMinAddressTakeTest([Values] RevivifiableFraction revivifiableFraction, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -621,7 +607,6 @@ public void Setup() CollisionRange collisionRange = CollisionRange.None; LogSettings logSettings = new() { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 17, MemorySizeBits = 20 }; - var concurrencyControlMode = ConcurrencyControlMode.LockTable; var revivificationSettings = RevivificationSettings.PowerOf2Bins; foreach (var arg in TestContext.CurrentContext.Test.Arguments) { @@ -630,11 +615,6 @@ public void Setup() collisionRange = cr; continue; } - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } if (arg is PendingOp) { logSettings.ReadCopyOptions = new(ReadCopyFrom.Device, ReadCopyTo.MainLog); @@ -649,7 +629,7 @@ public void Setup() } comparer = new RevivificationSpanByteComparer(collisionRange); - store = new TsavoriteKV(1L << 16, logSettings, comparer: comparer, concurrencyControlMode: concurrencyControlMode, revivificationSettings: revivificationSettings); + store = new TsavoriteKV(1L << 16, logSettings, comparer: comparer, revivificationSettings: revivificationSettings); functions = new RevivificationSpanByteFunctions(store); session = store.NewSession(functions); @@ -700,9 +680,7 @@ public enum Growth { None, Grow, Shrink }; [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SpanByteNoRevivLengthTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] Growth growth, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SpanByteNoRevivLengthTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] Growth growth) { Populate(); @@ -769,9 +747,7 @@ public void SpanByteNoRevivLengthTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] Up [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SpanByteSimpleTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SpanByteSimpleTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -812,9 +788,7 @@ public void SpanByteSimpleTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SpanByteIPUGrowAndRevivifyTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SpanByteIPUGrowAndRevivifyTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -889,9 +863,7 @@ public void SpanByteIPUGrowAndRevivifyTest([Values(UpdateOp.Upsert, UpdateOp.RMW [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SpanByteReadOnlyMinAddressTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SpanByteReadOnlyMinAddressTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -996,7 +968,6 @@ private long PrepareDeletes(bool stayInChain, byte delAboveRO, FlushMode flushMo [Category(RevivificationCategory)] [Category(SmokeTestCategory)] //[Repeat(300)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] public void SpanByteUpdateRevivifyTest([Values] DeleteDest deleteDest, [Values] UpdateKey updateKey, [Values] CollisionRange collisionRange, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { @@ -1075,9 +1046,7 @@ public void SpanByteUpdateRevivifyTest([Values] DeleteDest deleteDest, [Values] [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SimpleRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -1121,10 +1090,7 @@ public void SimpleRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp. [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void DeleteEntireChainAndRevivifyTest([Values(CollisionRange.Ten)] CollisionRange collisionRange, - [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void DeleteEntireChainAndRevivifyTest([Values(CollisionRange.Ten)] CollisionRange collisionRange, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -1179,10 +1145,7 @@ public void DeleteEntireChainAndRevivifyTest([Values(CollisionRange.Ten)] Collis [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] CollisionRange collisionRange, - [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] CollisionRange collisionRange, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -1254,8 +1217,7 @@ public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] Collis [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void DeleteAllRecordsAndTakeSnapshotTest([Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void DeleteAllRecordsAndTakeSnapshotTest() { Populate(); @@ -1279,8 +1241,7 @@ public void DeleteAllRecordsAndTakeSnapshotTest([Values(ConcurrencyControlMode.L [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void DeleteAllRecordsAndIterateTest([Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void DeleteAllRecordsAndIterateTest() { Populate(); @@ -1308,8 +1269,7 @@ public void DeleteAllRecordsAndIterateTest([Values(ConcurrencyControlMode.LockTa [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void BinSelectionTest([Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void BinSelectionTest() { FreeRecordPool pool = store.RevivificationManager.FreeRecordPool; int expectedBin = 0, recordSize = RevivificationTestUtils.GetMaxRecordSize(pool, expectedBin); @@ -1336,8 +1296,7 @@ public void BinSelectionTest([Values(ConcurrencyControlMode.LockTable)] Concurre [Category(RevivificationCategory)] [Category(SmokeTestCategory)] //[Repeat(30)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public unsafe void ArtificialBinWrappingTest([Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public unsafe void ArtificialBinWrappingTest() { FreeRecordPool pool = store.RevivificationManager.FreeRecordPool; @@ -1387,9 +1346,7 @@ public unsafe void ArtificialBinWrappingTest([Values(ConcurrencyControlMode.Lock [Category(RevivificationCategory)] [Category(SmokeTestCategory)] //[Repeat(3000)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public unsafe void LiveBinWrappingTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] WaitMode waitMode, [Values] DeleteDest deleteDest, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public unsafe void LiveBinWrappingTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values] WaitMode waitMode, [Values] DeleteDest deleteDest) { if (TestContext.CurrentContext.CurrentRepeatCount > 0) Debug.WriteLine($"*** Current test iteration: {TestContext.CurrentContext.CurrentRepeatCount + 1} ***"); @@ -1476,9 +1433,7 @@ public unsafe void LiveBinWrappingTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void LiveBinWrappingNoRevivTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values(RevivificationEnabled.NoReviv)] RevivificationEnabled revivEnabled, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void LiveBinWrappingNoRevivTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, [Values(RevivificationEnabled.NoReviv)] RevivificationEnabled revivEnabled) { // For a comparison to the reviv version above. Populate(); @@ -1523,9 +1478,7 @@ public void LiveBinWrappingNoRevivTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SimpleOversizeRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleOversizeRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -1584,9 +1537,7 @@ public enum PendingOp { Read, RMW }; [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SimplePendingOpsRevivifyTest([Values(CollisionRange.None)] CollisionRange collisionRange, [Values] PendingOp pendingOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimplePendingOpsRevivifyTest([Values(CollisionRange.None)] CollisionRange collisionRange, [Values] PendingOp pendingOp) { byte delAboveRO = numRecords - 2; // Will be sent to free list byte targetRO = numRecords / 2 - 15; @@ -1667,21 +1618,11 @@ public void Setup() log = Devices.CreateLogDevice(Path.Combine(MethodTestDir, "test.log"), deleteOnClose: true); objlog = Devices.CreateLogDevice(Path.Combine(MethodTestDir, "test.obj.log"), deleteOnClose: true); - var concurrencyControlMode = ConcurrencyControlMode.LockTable; - foreach (var arg in TestContext.CurrentContext.Test.Arguments) - { - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } - } - store = new TsavoriteKV (128, logSettings: new LogSettings { LogDevice = log, ObjectLogDevice = objlog, MutableFraction = 0.1, MemorySizeBits = 22, PageSizeBits = 12 }, serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, - concurrencyControlMode: concurrencyControlMode, revivificationSettings: RevivificationSettings.DefaultFixedLength); + revivificationSettings: RevivificationSettings.DefaultFixedLength); functions = new MyFunctions(); session = store.NewSession(functions); @@ -1717,9 +1658,7 @@ void Populate() [Test] [Category(RevivificationCategory)] [Category(SmokeTestCategory)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void SimpleObjectTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void SimpleObjectTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { Populate(); @@ -1843,7 +1782,6 @@ public void Setup() CollisionRange collisionRange = CollisionRange.None; LogSettings logSettings = new() { LogDevice = log, ObjectLogDevice = null, PageSizeBits = 17, MemorySizeBits = 20 }; - var concurrencyControlMode = ConcurrencyControlMode.LockTable; foreach (var arg in TestContext.CurrentContext.Test.Arguments) { if (arg is CollisionRange cr) @@ -1851,15 +1789,10 @@ public void Setup() collisionRange = cr; continue; } - if (arg is ConcurrencyControlMode ccm) - { - concurrencyControlMode = ccm; - continue; - } } comparer = new RevivificationSpanByteComparer(collisionRange); - store = new TsavoriteKV(1L << 16, logSettings, comparer: comparer, concurrencyControlMode: concurrencyControlMode, revivificationSettings: RevivificationSettings.PowerOf2Bins); + store = new TsavoriteKV(1L << 16, logSettings, comparer: comparer, revivificationSettings: RevivificationSettings.PowerOf2Bins); functions = new RevivificationStressFunctions(keyComparer: null); session = store.NewSession(functions); @@ -2217,9 +2150,7 @@ unsafe void runThread(int tid) [Test] [Category(RevivificationCategory)] //[Repeat(3000)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void LiveThreadContentionOnOneRecordTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void LiveThreadContentionOnOneRecordTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { if (TestContext.CurrentContext.CurrentRepeatCount > 0) Debug.WriteLine($"*** Current test iteration: {TestContext.CurrentContext.CurrentRepeatCount + 1} ***"); @@ -2305,10 +2236,8 @@ public enum ThreadingPattern { SameKeys, RandomKeys }; [Test] [Category(RevivificationCategory)] //[Repeat(3000)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] public void LiveFreeListThreadStressTest([Values] CollisionRange collisionRange, - [Values] ThreadingPattern threadingPattern, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + [Values] ThreadingPattern threadingPattern, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { if (TestContext.CurrentContext.CurrentRepeatCount > 0) Debug.WriteLine($"*** Current test iteration: {TestContext.CurrentContext.CurrentRepeatCount + 1} ***"); @@ -2391,10 +2320,7 @@ unsafe void runUpdateThread(int tid) [Test] [Category(RevivificationCategory)] //[Repeat(30)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0060:Remove unused parameter", Justification = "concurrencyControlMode is used by Setup")] - public void LiveInChainThreadStressTest([Values(CollisionRange.Ten)] CollisionRange collisionRange, - [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp, - [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) + public void LiveInChainThreadStressTest([Values(CollisionRange.Ten)] CollisionRange collisionRange, [Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp updateOp) { if (TestContext.CurrentContext.CurrentRepeatCount > 0) Debug.WriteLine($"*** Current test iteration: {TestContext.CurrentContext.CurrentRepeatCount + 1} ***"); diff --git a/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs index c548edc0cf..04ff89e26c 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs @@ -70,8 +70,7 @@ public unsafe void SpanByteIterationBasicTest([Values] DeviceType deviceType, [V { log = CreateTestDevice(deviceType, $"{MethodTestDir}{deviceType}.log"); store = new TsavoriteKV - (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }, - concurrencyControlMode: scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable); + (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }); using var session = store.NewSession(new VLVectorFunctions()); var bContext = session.BasicContext; diff --git a/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs index c2034a9b04..cf5412b3cc 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs @@ -52,7 +52,7 @@ public void Setup() DeleteDirectory(MethodTestDir, wait: true); log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "test.log"), deleteOnClose: true); store = new TsavoriteKV - (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 25, PageSizeBits = PageSizeBits }, concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); + (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 25, PageSizeBits = PageSizeBits }, comparer: comparer); } [TearDown] @@ -373,7 +373,7 @@ public unsafe void SpanByteJumpToBeginAddressTest() DeleteDirectory(MethodTestDir, wait: true); using var log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "test.log"), deleteOnClose: true); using var store = new TsavoriteKV - (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 20, PageSizeBits = 15 }, concurrencyControlMode: ConcurrencyControlMode.None); + (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 20, PageSizeBits = 15 }); using var session = store.NewSession>(new SpanByteFunctions()); var bContext = session.BasicContext; diff --git a/libs/storage/Tsavorite/cs/test/SpanByteTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteTests.cs index f88531674d..3b15d03ad9 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteTests.cs @@ -201,8 +201,7 @@ public unsafe void ShouldSkipEmptySpaceAtEndOfPage() using var log = Devices.CreateLogDevice(Path.Join(TestUtils.MethodTestDir, "vl-iter.log"), deleteOnClose: true); using var store = new TsavoriteKV (128, - new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 10 }, // 1KB page - null, null, null, concurrencyControlMode: ConcurrencyControlMode.None); + new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 10 }); // 1KB page using var session = store.NewSession(new VLVectorFunctions()); var bContext = session.BasicContext;