From 198d3297e9640057af54e300bdb20a9f65c78527 Mon Sep 17 00:00:00 2001 From: TedHartMS <15467143+TedHartMS@users.noreply.github.com> Date: Fri, 31 May 2024 13:45:19 -0700 Subject: [PATCH] - Rename IFunctions to ISessionFunctions (in prep for StoreFunctions) - Clean up ITsavoriteSession to SessionFunctionsWrapper, minimizing the per-Context code to just the locking portion - remove RUMD (Read/Upsert/rMw/Delete) functionality from ClientSession; users should use one of the 4 Contexts (Basic, Lockable, Unsafe, LockableUnsafe) for all such actions. This changed a lot of tests --- .../MigrationKeyIterationFunctions.cs | 2 +- libs/server/AOF/AofProcessor.cs | 45 +- .../Providers/TsavoriteKVProviderBase.cs | 2 +- .../Functions/MainStore/CallbackMethods.cs | 2 +- .../Functions/MainStore/DeleteMethods.cs | 2 +- .../Functions/MainStore/DisposeMethods.cs | 2 +- .../Functions/MainStore/MainStoreFunctions.cs | 2 +- .../Functions/MainStore/PrivateMethods.cs | 2 +- .../Storage/Functions/MainStore/RMWMethods.cs | 2 +- .../Functions/MainStore/ReadMethods.cs | 2 +- .../Functions/MainStore/UpsertMethods.cs | 2 +- .../Functions/MainStore/VarLenInputMethods.cs | 2 +- .../Functions/ObjectStore/CallbackMethods.cs | 2 +- .../Functions/ObjectStore/DeleteMethods.cs | 2 +- .../Functions/ObjectStore/DisposeMethods.cs | 2 +- .../ObjectStore/ObjectStoreFunctions.cs | 2 +- .../Functions/ObjectStore/PrivateMethods.cs | 2 +- .../Functions/ObjectStore/RMWMethods.cs | 2 +- .../Functions/ObjectStore/ReadMethods.cs | 2 +- .../Functions/ObjectStore/UpsertMethods.cs | 2 +- .../ObjectStore/VarLenInputMethods.cs | 2 +- .../Storage/Session/MainStore/BitmapOps.cs | 2 +- libs/server/StoreWrapper.cs | 8 +- libs/server/Transaction/TransactionManager.cs | 20 +- .../cs/benchmark/FixedLenYcsbBenchmark.cs | 32 +- .../Tsavorite/cs/benchmark/Functions.cs | 2 +- .../cs/benchmark/SpanByteYcsbBenchmark.cs | 37 +- .../cs/src/core/Allocator/AllocatorScan.cs | 13 +- .../src/core/Async/AsyncOperationInternal.cs | 12 +- .../cs/src/core/Async/CompletePendingAsync.cs | 2 +- .../cs/src/core/Async/DeleteAsync.cs | 10 +- .../Tsavorite/cs/src/core/Async/RMWAsync.cs | 14 +- .../Tsavorite/cs/src/core/Async/ReadAsync.cs | 16 +- .../cs/src/core/Async/UpsertAsync.cs | 12 +- .../cs/src/core/ClientSession/BasicContext.cs | 415 ++++++-- .../src/core/ClientSession/ClientSession.cs | 890 ++---------------- .../ClientSession/ClientSessionBuilder.cs | 2 +- .../core/ClientSession/ITsavoriteContext.cs | 38 +- .../src/core/ClientSession/LockableContext.cs | 306 +----- .../ClientSession/LockableUnsafeContext.cs | 72 +- .../ClientSession/SessionFunctionsWrapper.cs | 196 ++++ .../src/core/ClientSession/UnsafeContext.cs | 64 +- .../core/Compaction/ICompactionFunctions.cs | 2 +- .../core/Compaction/LogCompactionFunctions.cs | 4 +- .../core/Compaction/TsavoriteCompaction.cs | 37 +- .../core/Index/Interfaces/CallbackInfos.cs | 24 +- .../{IFunctions.cs => ISessionFunctions.cs} | 6 +- .../Interfaces/ISessionFunctionsWrapper.cs | 75 ++ .../core/Index/Interfaces/ISessionLocker.cs | 110 +++ .../Index/Interfaces/ITsavoriteSession.cs | 70 -- ...nctionsBase.cs => SessionFunctionsBase.cs} | 16 +- .../core/Index/Interfaces/TryAddFunctions.cs | 2 +- .../cs/src/core/Index/Recovery/Checkpoint.cs | 2 +- .../Tsavorite/Implementation/BlockAllocate.cs | 4 +- .../Implementation/ConditionalCopyToTail.cs | 6 +- .../Implementation/ContainsKeyInMemory.cs | 2 +- .../Implementation/ContinuePending.cs | 8 +- .../Implementation/EpochOperations.cs | 2 +- .../Tsavorite/Implementation/FindRecord.cs | 2 +- .../Implementation/HandleOperationStatus.cs | 6 +- .../Tsavorite/Implementation/HashEntryInfo.cs | 2 +- .../Index/Tsavorite/Implementation/Helpers.cs | 14 +- .../Implementation/InternalDelete.cs | 6 +- .../Tsavorite/Implementation/InternalRMW.cs | 12 +- .../Tsavorite/Implementation/InternalRead.cs | 10 +- .../Implementation/InternalUpsert.cs | 10 +- .../Implementation/Locking/ILockTable.cs | 7 +- .../Locking/OverflowBucketLockTable.cs | 2 +- .../Locking/TransientLocking.cs | 8 +- .../Implementation/OperationStackContext.cs | 2 +- .../Revivification/RecordLengths.cs | 4 +- .../Implementation/TryCopyToReadCache.cs | 2 +- .../Tsavorite/Implementation/TryCopyToTail.cs | 2 +- .../src/core/Index/Tsavorite/LogAccessor.cs | 8 +- .../cs/src/core/Index/Tsavorite/Tsavorite.cs | 18 +- .../core/Index/Tsavorite/TsavoriteIterator.cs | 18 +- .../core/Index/Tsavorite/TsavoriteThread.cs | 10 +- .../cs/src/core/Utilities/LockType.cs | 2 +- .../cs/src/core/Utilities/StatusCode.cs | 2 +- .../cs/src/core/VarLen/SpanByteFunctions.cs | 2 +- .../Tsavorite/cs/test/AdvancedLockTests.cs | 21 +- .../cs/test/AsyncLargeObjectTests.cs | 8 +- .../Tsavorite/cs/test/BasicLockTests.cs | 32 +- .../Tsavorite/cs/test/BasicStorageTests.cs | 13 +- libs/storage/Tsavorite/cs/test/BasicTests.cs | 190 ++-- .../cs/test/BlittableIterationTests.cs | 23 +- .../cs/test/BlittableLogCompactionTests.cs | 38 +- .../cs/test/BlittableLogScanTests.cs | 16 +- .../Tsavorite/cs/test/CancellationTests.cs | 20 +- .../cs/test/CheckpointManagerTests.cs | 5 +- .../Tsavorite/cs/test/CompletePendingTests.cs | 21 +- .../storage/Tsavorite/cs/test/DisposeTests.cs | 2 +- .../Tsavorite/cs/test/ExpirationTests.cs | 16 +- .../cs/test/FunctionPerSessionTests.cs | 20 +- .../cs/test/GenericByteArrayTests.cs | 12 +- .../cs/test/GenericDiskDeleteTests.cs | 46 +- .../cs/test/GenericIterationTests.cs | 20 +- .../cs/test/GenericLogCompactionTests.cs | 38 +- .../Tsavorite/cs/test/GenericLogScanTests.cs | 17 +- .../Tsavorite/cs/test/GenericStringTests.cs | 11 +- .../cs/test/InputOutputParameterTests.cs | 14 +- .../Tsavorite/cs/test/LargeObjectTests.cs | 10 +- .../cs/test/LockableUnsafeContextTests.cs | 56 +- .../Tsavorite/cs/test/LowMemAsyncTests.cs | 19 +- libs/storage/Tsavorite/cs/test/MiscTests.cs | 31 +- .../Tsavorite/cs/test/ModifiedBitTests.cs | 36 +- .../cs/test/MoreLogCompactionTests.cs | 14 +- .../Tsavorite/cs/test/NativeReadCacheTests.cs | 42 +- .../Tsavorite/cs/test/NeedCopyUpdateTests.cs | 34 +- .../Tsavorite/cs/test/ObjectReadCacheTests.cs | 42 +- .../Tsavorite/cs/test/ObjectRecoveryTest.cs | 12 +- .../Tsavorite/cs/test/ObjectRecoveryTest2.cs | 18 +- .../Tsavorite/cs/test/ObjectRecoveryTest3.cs | 16 +- .../cs/test/ObjectRecoveryTestTypes.cs | 2 +- .../Tsavorite/cs/test/ObjectTestTypes.cs | 10 +- libs/storage/Tsavorite/cs/test/ObjectTests.cs | 48 +- .../Tsavorite/cs/test/PostOperationsTests.cs | 28 +- .../Tsavorite/cs/test/ReadAddressTests.cs | 50 +- .../Tsavorite/cs/test/ReadCacheChainTests.cs | 136 +-- .../Tsavorite/cs/test/RecoveryChecks.cs | 145 +-- .../Tsavorite/cs/test/RecoveryTestTypes.cs | 2 +- .../Tsavorite/cs/test/RecoveryTests.cs | 46 +- .../Tsavorite/cs/test/ReproReadCacheTest.cs | 3 +- .../Tsavorite/cs/test/RevivificationTests.cs | 170 ++-- .../storage/Tsavorite/cs/test/SessionTests.cs | 63 +- .../Tsavorite/cs/test/SharedDirectoryTests.cs | 13 +- .../Tsavorite/cs/test/SimpleAsyncTests.cs | 117 ++- .../Tsavorite/cs/test/SimpleRecoveryTest.cs | 46 +- .../Tsavorite/cs/test/SingleWriterTests.cs | 24 +- .../cs/test/SpanByteIterationTests.cs | 23 +- .../Tsavorite/cs/test/SpanByteLogScanTests.cs | 19 +- .../Tsavorite/cs/test/SpanByteTests.cs | 21 +- .../cs/test/SpanByteVLVectorTests.cs | 22 +- .../cs/test/StateMachineBarrierTests.cs | 3 +- .../Tsavorite/cs/test/StateMachineTests.cs | 11 +- libs/storage/Tsavorite/cs/test/TestTypes.cs | 8 +- .../Tsavorite/cs/test/ThreadSession.cs | 10 +- test/Garnet.test/GarnetObjectTests.cs | 23 +- 138 files changed, 2295 insertions(+), 2428 deletions(-) create mode 100644 libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs rename libs/storage/Tsavorite/cs/src/core/Index/Interfaces/{IFunctions.cs => ISessionFunctions.cs} (98%) create mode 100644 libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctionsWrapper.cs create mode 100644 libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs rename libs/storage/Tsavorite/cs/src/core/Index/Interfaces/{FunctionsBase.cs => SessionFunctionsBase.cs} (91%) diff --git a/libs/cluster/Server/Migration/MigrationKeyIterationFunctions.cs b/libs/cluster/Server/Migration/MigrationKeyIterationFunctions.cs index fa2d94ccb6..d169b79a21 100644 --- a/libs/cluster/Server/Migration/MigrationKeyIterationFunctions.cs +++ b/libs/cluster/Server/Migration/MigrationKeyIterationFunctions.cs @@ -87,7 +87,7 @@ public bool SingleReader(ref SpanByte key, ref SpanByte value, RecordMetadata re cursorRecordResult = CursorRecordResult.Accept; // default; not used here var s = HashSlotUtils.HashSlot(key.ToPointer(), key.Length); if (slots.Contains(s)) - session.Delete(key); + session.BasicContext.Delete(key); return true; } diff --git a/libs/server/AOF/AofProcessor.cs b/libs/server/AOF/AofProcessor.cs index 8ff00b0431..3ce797c86f 100644 --- a/libs/server/AOF/AofProcessor.cs +++ b/libs/server/AOF/AofProcessor.cs @@ -31,11 +31,13 @@ public sealed unsafe partial class AofProcessor /// Session for main store /// readonly ClientSession session = null; + readonly BasicContext sessionBasicContext; /// /// Session for object store /// readonly ClientSession objectStoreSession = null; + readonly BasicContext objectStoreSessionBasicContext; readonly Dictionary> inflightTxns; readonly byte[] buffer; @@ -76,7 +78,10 @@ public AofProcessor( this.respServerSession = new RespServerSession(null, replayAofStoreWrapper, null); session = respServerSession.storageSession.session; + sessionBasicContext = session.BasicContext; objectStoreSession = respServerSession.storageSession.objectStoreSession; + if (objectStoreSession is not null) + objectStoreSessionBasicContext = objectStoreSession.BasicContext; inflightTxns = new Dictionary>(); buffer = new byte[BufferSizeUtils.ServerBufferSize(new MaxSizeSettings())]; @@ -232,22 +237,22 @@ private unsafe bool ReplayOp(byte* entryPtr) switch (header.opType) { case AofEntryType.StoreUpsert: - StoreUpsert(session, entryPtr); + StoreUpsert(sessionBasicContext, entryPtr); break; case AofEntryType.StoreRMW: - StoreRMW(session, entryPtr); + StoreRMW(sessionBasicContext, entryPtr); break; case AofEntryType.StoreDelete: - StoreDelete(session, entryPtr); + StoreDelete(sessionBasicContext, entryPtr); break; case AofEntryType.ObjectStoreRMW: - ObjectStoreRMW(objectStoreSession, entryPtr, bufferPtr, buffer.Length); + ObjectStoreRMW(objectStoreSessionBasicContext, entryPtr, bufferPtr, buffer.Length); break; case AofEntryType.ObjectStoreUpsert: - ObjectStoreUpsert(objectStoreSession, storeWrapper.GarnetObjectSerializer, entryPtr, bufferPtr, buffer.Length); + ObjectStoreUpsert(objectStoreSessionBasicContext, storeWrapper.GarnetObjectSerializer, entryPtr, bufferPtr, buffer.Length); break; case AofEntryType.ObjectStoreDelete: - ObjectStoreDelete(objectStoreSession, entryPtr); + ObjectStoreDelete(objectStoreSessionBasicContext, entryPtr); break; case AofEntryType.StoredProcedure: ref var input = ref Unsafe.AsRef(entryPtr + sizeof(AofHeader)); @@ -259,37 +264,37 @@ private unsafe bool ReplayOp(byte* entryPtr) return true; } - static unsafe void StoreUpsert(ClientSession session, byte* ptr) + static unsafe void StoreUpsert(BasicContext basicContext, byte* ptr) { ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); ref var input = ref Unsafe.AsRef(ptr + sizeof(AofHeader) + key.TotalSize); ref var value = ref Unsafe.AsRef(ptr + sizeof(AofHeader) + key.TotalSize + input.TotalSize); SpanByteAndMemory output = default; - session.Upsert(ref key, ref input, ref value, ref output); + basicContext.Upsert(ref key, ref input, ref value, ref output); if (!output.IsSpanByte) output.Memory.Dispose(); } - static unsafe void StoreRMW(ClientSession session, byte* ptr) + static unsafe void StoreRMW(BasicContext basicContext, byte* ptr) { byte* pbOutput = stackalloc byte[32]; ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); ref var input = ref Unsafe.AsRef(ptr + sizeof(AofHeader) + key.TotalSize); var output = new SpanByteAndMemory(pbOutput, 32); - if (session.RMW(ref key, ref input, ref output).IsPending) - session.CompletePending(true); + if (basicContext.RMW(ref key, ref input, ref output).IsPending) + basicContext.CompletePending(true); if (!output.IsSpanByte) output.Memory.Dispose(); } - static unsafe void StoreDelete(ClientSession session, byte* ptr) + static unsafe void StoreDelete(BasicContext basicContext, byte* ptr) { ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); - session.Delete(ref key); + basicContext.Delete(ref key); } - static unsafe void ObjectStoreUpsert(ClientSession session, GarnetObjectSerializer garnetObjectSerializer, byte* ptr, byte* outputPtr, int outputLength) + static unsafe void ObjectStoreUpsert(BasicContext basicContext, GarnetObjectSerializer garnetObjectSerializer, byte* ptr, byte* outputPtr, int outputLength) { ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); var keyB = key.ToByteArray(); @@ -299,29 +304,29 @@ static unsafe void ObjectStoreUpsert(ClientSession session, byte* ptr, byte* outputPtr, int outputLength) + static unsafe void ObjectStoreRMW(BasicContext basicContext, byte* ptr, byte* outputPtr, int outputLength) { ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); var keyB = key.ToByteArray(); ref var input = ref Unsafe.AsRef(ptr + sizeof(AofHeader) + key.TotalSize); var output = new GarnetObjectStoreOutput { spanByteAndMemory = new(outputPtr, outputLength) }; - if (session.RMW(ref keyB, ref input, ref output).IsPending) - session.CompletePending(true); + if (basicContext.RMW(ref keyB, ref input, ref output).IsPending) + basicContext.CompletePending(true); if (!output.spanByteAndMemory.IsSpanByte) output.spanByteAndMemory.Memory.Dispose(); } - static unsafe void ObjectStoreDelete(ClientSession session, byte* ptr) + static unsafe void ObjectStoreDelete(BasicContext basicContext, byte* ptr) { ref var key = ref Unsafe.AsRef(ptr + sizeof(AofHeader)); var keyB = key.ToByteArray(); - session.Delete(ref keyB); + basicContext.Delete(ref keyB); } /// diff --git a/libs/server/Providers/TsavoriteKVProviderBase.cs b/libs/server/Providers/TsavoriteKVProviderBase.cs index 461941b622..2966a51a06 100644 --- a/libs/server/Providers/TsavoriteKVProviderBase.cs +++ b/libs/server/Providers/TsavoriteKVProviderBase.cs @@ -12,7 +12,7 @@ namespace Garnet.server /// [K, V, I, O, F, P] /// public abstract class TsavoriteKVProviderBase : ISessionProvider - where Functions : IFunctions + where Functions : ISessionFunctions where ParameterSerializer : IServerSerializer { /// diff --git a/libs/server/Storage/Functions/MainStore/CallbackMethods.cs b/libs/server/Storage/Functions/MainStore/CallbackMethods.cs index 01b2891531..0de37bd162 100644 --- a/libs/server/Storage/Functions/MainStore/CallbackMethods.cs +++ b/libs/server/Storage/Functions/MainStore/CallbackMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public void ReadCompletionCallback(ref SpanByte key, ref SpanByte input, ref SpanByteAndMemory output, long ctx, Status status, RecordMetadata recordMetadata) diff --git a/libs/server/Storage/Functions/MainStore/DeleteMethods.cs b/libs/server/Storage/Functions/MainStore/DeleteMethods.cs index 0f3b416901..a6ec4a05d9 100644 --- a/libs/server/Storage/Functions/MainStore/DeleteMethods.cs +++ b/libs/server/Storage/Functions/MainStore/DeleteMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public bool SingleDeleter(ref SpanByte key, ref SpanByte value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) diff --git a/libs/server/Storage/Functions/MainStore/DisposeMethods.cs b/libs/server/Storage/Functions/MainStore/DisposeMethods.cs index acf7ebbe37..6575389a17 100644 --- a/libs/server/Storage/Functions/MainStore/DisposeMethods.cs +++ b/libs/server/Storage/Functions/MainStore/DisposeMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public void DisposeSingleWriter(ref SpanByte key, ref SpanByte input, ref SpanByte src, ref SpanByte dst, ref SpanByteAndMemory output, ref UpsertInfo upsertInfo, WriteReason reason) diff --git a/libs/server/Storage/Functions/MainStore/MainStoreFunctions.cs b/libs/server/Storage/Functions/MainStore/MainStoreFunctions.cs index fa6ad4b4c9..8d10db10e3 100644 --- a/libs/server/Storage/Functions/MainStore/MainStoreFunctions.cs +++ b/libs/server/Storage/Functions/MainStore/MainStoreFunctions.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { readonly FunctionsState functionsState; diff --git a/libs/server/Storage/Functions/MainStore/PrivateMethods.cs b/libs/server/Storage/Functions/MainStore/PrivateMethods.cs index 93f44b4b6d..26bcd4a3f6 100644 --- a/libs/server/Storage/Functions/MainStore/PrivateMethods.cs +++ b/libs/server/Storage/Functions/MainStore/PrivateMethods.cs @@ -11,7 +11,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { static void CopyTo(ref SpanByte src, ref SpanByteAndMemory dst, MemoryPool memoryPool) { diff --git a/libs/server/Storage/Functions/MainStore/RMWMethods.cs b/libs/server/Storage/Functions/MainStore/RMWMethods.cs index e090c2d135..490ed9f9f8 100644 --- a/libs/server/Storage/Functions/MainStore/RMWMethods.cs +++ b/libs/server/Storage/Functions/MainStore/RMWMethods.cs @@ -12,7 +12,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public bool NeedInitialUpdate(ref SpanByte key, ref SpanByte input, ref SpanByteAndMemory output, ref RMWInfo rmwInfo) diff --git a/libs/server/Storage/Functions/MainStore/ReadMethods.cs b/libs/server/Storage/Functions/MainStore/ReadMethods.cs index 3311206317..c8f7e21c3a 100644 --- a/libs/server/Storage/Functions/MainStore/ReadMethods.cs +++ b/libs/server/Storage/Functions/MainStore/ReadMethods.cs @@ -10,7 +10,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public bool SingleReader(ref SpanByte key, ref SpanByte input, ref SpanByte value, ref SpanByteAndMemory dst, ref ReadInfo readInfo) diff --git a/libs/server/Storage/Functions/MainStore/UpsertMethods.cs b/libs/server/Storage/Functions/MainStore/UpsertMethods.cs index 7a8a40195b..7e25c2104f 100644 --- a/libs/server/Storage/Functions/MainStore/UpsertMethods.cs +++ b/libs/server/Storage/Functions/MainStore/UpsertMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// public bool SingleWriter(ref SpanByte key, ref SpanByte input, ref SpanByte src, ref SpanByte dst, ref SpanByteAndMemory output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) diff --git a/libs/server/Storage/Functions/MainStore/VarLenInputMethods.cs b/libs/server/Storage/Functions/MainStore/VarLenInputMethods.cs index e8f41b6a17..eb23d7e5b3 100644 --- a/libs/server/Storage/Functions/MainStore/VarLenInputMethods.cs +++ b/libs/server/Storage/Functions/MainStore/VarLenInputMethods.cs @@ -9,7 +9,7 @@ namespace Garnet.server /// /// Callback functions for main store /// - public readonly unsafe partial struct MainStoreFunctions : IFunctions + public readonly unsafe partial struct MainStoreFunctions : ISessionFunctions { /// /// Parse ASCII byte array into long and validate that only contains ASCII decimal characters diff --git a/libs/server/Storage/Functions/ObjectStore/CallbackMethods.cs b/libs/server/Storage/Functions/ObjectStore/CallbackMethods.cs index ce471143f0..2757130ae3 100644 --- a/libs/server/Storage/Functions/ObjectStore/CallbackMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/CallbackMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public void ReadCompletionCallback(ref byte[] key, ref SpanByte input, ref GarnetObjectStoreOutput output, long ctx, Status status, RecordMetadata recordMetadata) diff --git a/libs/server/Storage/Functions/ObjectStore/DeleteMethods.cs b/libs/server/Storage/Functions/ObjectStore/DeleteMethods.cs index 03e46d3459..941be35462 100644 --- a/libs/server/Storage/Functions/ObjectStore/DeleteMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/DeleteMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public bool SingleDeleter(ref byte[] key, ref IGarnetObject value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) diff --git a/libs/server/Storage/Functions/ObjectStore/DisposeMethods.cs b/libs/server/Storage/Functions/ObjectStore/DisposeMethods.cs index cef9b3b1fc..bd45404417 100644 --- a/libs/server/Storage/Functions/ObjectStore/DisposeMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/DisposeMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public void DisposeSingleWriter(ref byte[] key, ref SpanByte input, ref IGarnetObject src, ref IGarnetObject dst, ref GarnetObjectStoreOutput output, ref UpsertInfo upsertInfo, WriteReason reason) diff --git a/libs/server/Storage/Functions/ObjectStore/ObjectStoreFunctions.cs b/libs/server/Storage/Functions/ObjectStore/ObjectStoreFunctions.cs index 70946a56c2..2d629e9c18 100644 --- a/libs/server/Storage/Functions/ObjectStore/ObjectStoreFunctions.cs +++ b/libs/server/Storage/Functions/ObjectStore/ObjectStoreFunctions.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { readonly FunctionsState functionsState; diff --git a/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs b/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs index 12a0be7e77..8e1cd5e0b7 100644 --- a/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/PrivateMethods.cs @@ -11,7 +11,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// /// Logging upsert from diff --git a/libs/server/Storage/Functions/ObjectStore/RMWMethods.cs b/libs/server/Storage/Functions/ObjectStore/RMWMethods.cs index 3cb1eb4169..d9319a5e83 100644 --- a/libs/server/Storage/Functions/ObjectStore/RMWMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/RMWMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public bool NeedInitialUpdate(ref byte[] key, ref SpanByte input, ref GarnetObjectStoreOutput output, ref RMWInfo rmwInfo) diff --git a/libs/server/Storage/Functions/ObjectStore/ReadMethods.cs b/libs/server/Storage/Functions/ObjectStore/ReadMethods.cs index 4405441fd6..8fdf2b53f0 100644 --- a/libs/server/Storage/Functions/ObjectStore/ReadMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/ReadMethods.cs @@ -10,7 +10,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public bool SingleReader(ref byte[] key, ref SpanByte input, ref IGarnetObject value, ref GarnetObjectStoreOutput dst, ref ReadInfo readInfo) diff --git a/libs/server/Storage/Functions/ObjectStore/UpsertMethods.cs b/libs/server/Storage/Functions/ObjectStore/UpsertMethods.cs index 2636a4ff70..407de0e0a9 100644 --- a/libs/server/Storage/Functions/ObjectStore/UpsertMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/UpsertMethods.cs @@ -8,7 +8,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public bool SingleWriter(ref byte[] key, ref SpanByte input, ref IGarnetObject src, ref IGarnetObject dst, ref GarnetObjectStoreOutput output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) diff --git a/libs/server/Storage/Functions/ObjectStore/VarLenInputMethods.cs b/libs/server/Storage/Functions/ObjectStore/VarLenInputMethods.cs index 6818dcddd9..4a0af4c0fe 100644 --- a/libs/server/Storage/Functions/ObjectStore/VarLenInputMethods.cs +++ b/libs/server/Storage/Functions/ObjectStore/VarLenInputMethods.cs @@ -9,7 +9,7 @@ namespace Garnet.server /// /// Object store functions /// - public readonly unsafe partial struct ObjectStoreFunctions : IFunctions + public readonly unsafe partial struct ObjectStoreFunctions : ISessionFunctions { /// public int GetRMWModifiedValueLength(ref IGarnetObject value, ref SpanByte input) diff --git a/libs/server/Storage/Session/MainStore/BitmapOps.cs b/libs/server/Storage/Session/MainStore/BitmapOps.cs index 49aed81ffd..1e138ac617 100644 --- a/libs/server/Storage/Session/MainStore/BitmapOps.cs +++ b/libs/server/Storage/Session/MainStore/BitmapOps.cs @@ -156,7 +156,7 @@ public unsafe GarnetStatus StringBitOperation(ArgSlice[] keys, BitmapOperation b var localSrcBitmapPtr = (byte*)((IntPtr)(*(long*)outputBitmapPtr)); var len = *(int*)(outputBitmapPtr + 8); - // Keep track of pointers returned from IFunctions + // Keep track of pointers returned from ISessionFunctions srcBitmapStartPtrs[keysFound] = localSrcBitmapPtr; srcBitmapEndPtrs[keysFound] = localSrcBitmapPtr + len; keysFound++; diff --git a/libs/server/StoreWrapper.cs b/libs/server/StoreWrapper.cs index f374b3d8c4..9246ba05a2 100644 --- a/libs/server/StoreWrapper.cs +++ b/libs/server/StoreWrapper.cs @@ -448,13 +448,13 @@ void DoCompaction(int mainStoreMaxSegments, int objectStoreMaxSegments, int numS break; case LogCompactionType.Scan: - objectStore.Log.Compact>( - new SimpleFunctions(), untilAddress, CompactionType.Scan); + objectStore.Log.Compact>( + new SimpleSessionFunctions(), untilAddress, CompactionType.Scan); break; case LogCompactionType.Lookup: - objectStore.Log.Compact>( - new SimpleFunctions(), untilAddress, CompactionType.Lookup); + objectStore.Log.Compact>( + new SimpleSessionFunctions(), untilAddress, CompactionType.Lookup); break; default: diff --git a/libs/server/Transaction/TransactionManager.cs b/libs/server/Transaction/TransactionManager.cs index a87934c524..c8edaacf41 100644 --- a/libs/server/Transaction/TransactionManager.cs +++ b/libs/server/Transaction/TransactionManager.cs @@ -23,6 +23,11 @@ public sealed unsafe partial class TransactionManager /// readonly ClientSession session; + /// + /// Basic context for main store + /// + readonly BasicContext basicContext; + /// /// Lockable context for main store /// @@ -33,6 +38,11 @@ public sealed unsafe partial class TransactionManager /// readonly ClientSession objectStoreSession; + /// + /// Basic context for object store + /// + readonly BasicContext objectStoreBasicContext; + /// /// Lockable context for object store /// @@ -87,11 +97,15 @@ internal TransactionManager( ILogger logger = null) { this.session = storageSession.session; + basicContext = session.BasicContext; lockableContext = session.LockableContext; this.objectStoreSession = storageSession.objectStoreSession; if (objectStoreSession != null) + { + objectStoreBasicContext = objectStoreSession.BasicContext; objectStoreLockableContext = objectStoreSession.LockableContext; + } this.functionsState = storageSession.functionsState; this.appendOnlyFile = functionsState.appendOnlyFile; @@ -232,9 +246,9 @@ internal void Watch(ArgSlice key, StoreType type) watchContainer.AddWatch(key, type); if (type == StoreType.Main || type == StoreType.All) - session.ResetModified(key.SpanByte); - if (type == StoreType.Object || type == StoreType.All) - objectStoreSession?.ResetModified(key.ToArray()); + basicContext.ResetModified(key.SpanByte); + if ((type == StoreType.Object || type == StoreType.All) && objectStoreSession != null) + objectStoreBasicContext.ResetModified(key.ToArray()); } void UpdateTransactionStoreType(StoreType type) diff --git a/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs b/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs index 115df8cafd..744ddb81ef 100644 --- a/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs +++ b/libs/storage/Tsavorite/cs/benchmark/FixedLenYcsbBenchmark.cs @@ -139,7 +139,7 @@ private void RunYcsbUnsafeContext(int thread_idx) int count = 0; #endif - var session = store.NewSession(functions); + using var session = store.NewSession(functions); var uContext = session.UnsafeContext; uContext.BeginUnsafe(); @@ -210,8 +210,6 @@ private void RunYcsbUnsafeContext(int thread_idx) uContext.EndUnsafe(); } - session.Dispose(); - sw.Stop(); #if DASHBOARD @@ -245,7 +243,8 @@ private void RunYcsbSafeContext(int thread_idx) long writes_done = 0; long deletes_done = 0; - var session = store.NewSession(functions); + using var session = store.NewSession(functions); + var bContext = session.BasicContext; while (!done) { @@ -260,34 +259,33 @@ private void RunYcsbSafeContext(int thread_idx) for (long idx = chunk_idx; idx < chunk_idx + YcsbConstants.kChunkSize && !done; ++idx) { if (idx % 512 == 0) - session.CompletePending(false); + bContext.CompletePending(false); int r = (int)rng.Generate(100); // rng.Next() is not inclusive of the upper bound so this will be <= 99 if (r < readPercent) { - session.Read(ref txn_keys_[idx], ref input, ref output, Empty.Default); + bContext.Read(ref txn_keys_[idx], ref input, ref output, Empty.Default); ++reads_done; continue; } if (r < upsertPercent) { - session.Upsert(ref txn_keys_[idx], ref value, Empty.Default); + bContext.Upsert(ref txn_keys_[idx], ref value, Empty.Default); ++writes_done; continue; } if (r < rmwPercent) { - session.RMW(ref txn_keys_[idx], ref input_[idx & 0x7], Empty.Default); + bContext.RMW(ref txn_keys_[idx], ref input_[idx & 0x7], Empty.Default); ++writes_done; continue; } - session.Delete(ref txn_keys_[idx], Empty.Default); + bContext.Delete(ref txn_keys_[idx], Empty.Default); ++deletes_done; } } - session.CompletePending(true); - session.Dispose(); + bContext.CompletePending(true); sw.Stop(); @@ -502,7 +500,8 @@ private void SetupYcsbSafeContext(int thread_idx) } waiter.Wait(); - var session = store.NewSession(functions); + using var session = store.NewSession(functions); + var bContext = session.BasicContext; Value value = default; @@ -514,20 +513,19 @@ private void SetupYcsbSafeContext(int thread_idx) { if (idx % 256 == 0) { - session.Refresh(); + bContext.Refresh(); if (idx % 65536 == 0) { - session.CompletePending(false); + bContext.CompletePending(false); } } - session.Upsert(ref init_keys_[idx], ref value, Empty.Default); + bContext.Upsert(ref init_keys_[idx], ref value, Empty.Default); } } - session.CompletePending(true); - session.Dispose(); + bContext.CompletePending(true); } #if DASHBOARD diff --git a/libs/storage/Tsavorite/cs/benchmark/Functions.cs b/libs/storage/Tsavorite/cs/benchmark/Functions.cs index df16f88372..fe3b6735aa 100644 --- a/libs/storage/Tsavorite/cs/benchmark/Functions.cs +++ b/libs/storage/Tsavorite/cs/benchmark/Functions.cs @@ -6,7 +6,7 @@ namespace Tsavorite.benchmark { - public struct Functions : IFunctions + public struct Functions : ISessionFunctions { public void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Empty ctx, Status status, RecordMetadata recordMetadata) { diff --git a/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs b/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs index 9665084c79..d93cb17084 100644 --- a/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs +++ b/libs/storage/Tsavorite/cs/benchmark/SpanByteYcsbBenchmark.cs @@ -152,7 +152,7 @@ private void RunYcsbUnsafeContext(int thread_idx) int count = 0; #endif - var session = store.NewSession(functions); + using var session = store.NewSession(functions); var uContext = session.UnsafeContext; uContext.BeginUnsafe(); @@ -223,8 +223,6 @@ private void RunYcsbUnsafeContext(int thread_idx) uContext.EndUnsafe(); } - session.Dispose(); - sw.Stop(); #if DASHBOARD @@ -269,7 +267,8 @@ private void RunYcsbSafeContext(int thread_idx) int count = 0; #endif - var session = store.NewSession(functions); + using var session = store.NewSession(functions); + var bContext = session.BasicContext; while (!done) { @@ -286,30 +285,30 @@ private void RunYcsbSafeContext(int thread_idx) if (idx % 512 == 0) { if (!testLoader.Options.UseSafeContext) - session.Refresh(); - session.CompletePending(false); + bContext.Refresh(); + bContext.CompletePending(false); } int r = (int)rng.Generate(100); // rng.Next() is not inclusive of the upper bound so this will be <= 99 if (r < readPercent) { - session.Read(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _input, ref _output, Empty.Default); + bContext.Read(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _input, ref _output, Empty.Default); ++reads_done; continue; } if (r < upsertPercent) { - session.Upsert(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _value, Empty.Default); + bContext.Upsert(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _value, Empty.Default); ++writes_done; continue; } if (r < rmwPercent) { - session.RMW(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _input, Empty.Default); + bContext.RMW(ref SpanByte.Reinterpret(ref txn_keys_[idx]), ref _input, Empty.Default); ++writes_done; continue; } - session.Delete(ref SpanByte.Reinterpret(ref txn_keys_[idx]), Empty.Default); + bContext.Delete(ref SpanByte.Reinterpret(ref txn_keys_[idx]), Empty.Default); ++deletes_done; } @@ -330,8 +329,7 @@ private void RunYcsbSafeContext(int thread_idx) #endif } - session.CompletePending(true); - session.Dispose(); + bContext.CompletePending(true); sw.Stop(); @@ -482,7 +480,7 @@ private void SetupYcsbUnsafeContext(int thread_idx) } waiter.Wait(); - var session = store.NewSession(functions); + using var session = store.NewSession(functions); var uContext = session.UnsafeContext; uContext.BeginUnsafe(); @@ -537,7 +535,6 @@ private void SetupYcsbUnsafeContext(int thread_idx) { uContext.EndUnsafe(); } - session.Dispose(); } private void SetupYcsbSafeContext(int thread_idx) @@ -551,7 +548,8 @@ private void SetupYcsbSafeContext(int thread_idx) } waiter.Wait(); - var session = store.NewSession(functions); + using var session = store.NewSession(functions); + var bContext = session.BasicContext; Span value = stackalloc byte[kValueSize]; ref SpanByte _value = ref SpanByte.Reinterpret(value); @@ -564,20 +562,19 @@ private void SetupYcsbSafeContext(int thread_idx) { if (idx % 256 == 0) { - session.Refresh(); + bContext.Refresh(); if (idx % 65536 == 0) { - session.CompletePending(false); + bContext.CompletePending(false); } } - session.Upsert(ref SpanByte.Reinterpret(ref init_keys_[idx]), ref _value, Empty.Default); + bContext.Upsert(ref SpanByte.Reinterpret(ref init_keys_[idx]), ref _value, Empty.Default); } } - session.CompletePending(true); - session.Dispose(); + bContext.CompletePending(true); } #if DASHBOARD diff --git a/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorScan.cs b/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorScan.cs index 84bbd9120d..3f77148ce0 100644 --- a/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorScan.cs +++ b/libs/storage/Tsavorite/cs/src/core/Allocator/AllocatorScan.cs @@ -208,6 +208,7 @@ protected bool ScanLookup(Tsavor where TScanIterator : ITsavoriteScanIterator, IPushScanIterator { using var session = store.NewSession>(new LogScanCursorFunctions()); + var bContext = session.BasicContext; if (cursor >= GetTailAddress()) goto IterationComplete; @@ -226,13 +227,13 @@ protected bool ScanLookup(Tsavor { ref var key = ref iter.GetKey(); ref var value = ref iter.GetValue(); - var status = session.ConditionalScanPush(scanCursorState, recordInfo, ref key, ref value, iter.NextAddress); + var status = bContext.ConditionalScanPush(scanCursorState, recordInfo, ref key, ref value, iter.NextAddress); if (status.IsPending) { ++numPending; if (numPending == count - scanCursorState.acceptedCount || numPending > 256) { - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); numPending = 0; } } @@ -250,7 +251,7 @@ protected bool ScanLookup(Tsavor // Drain any pending pushes. We have ended the iteration; there are no more records, so drop through to end it. if (numPending > 0) - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); IterationComplete: cursor = 0; @@ -259,7 +260,7 @@ protected bool ScanLookup(Tsavor [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ConditionalScanPush(TsavoriteSession tsavoriteSession, ScanCursorState scanCursorState, RecordInfo recordInfo, ref Key key, ref Value value, long minAddress) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(epoch.ThisInstanceProtected(), "This is called only from ScanLookup so the epoch should be protected"); TsavoriteKV.PendingContext pendingContext = new(comparer.GetHashCode64(ref key)); @@ -310,7 +311,7 @@ internal static OperationStatus PrepareIOForConditionalScan.PendingContext pendingContext, ref Key key, ref Input input, ref Value value, ref Output output, Context userContext, ref OperationStackContext stackCtx, long minAddress, ScanCursorState scanCursorState) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // WriteReason is not surfaced for this operation, so pick anything. var status = tsavoriteSession.Store.PrepareIOForConditionalOperation(tsavoriteSession, ref pendingContext, ref key, ref input, ref value, ref output, @@ -319,7 +320,7 @@ internal static OperationStatus PrepareIOForConditionalScan : IFunctions + internal struct LogScanCursorFunctions : ISessionFunctions { public bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo) => true; public bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) => true; diff --git a/libs/storage/Tsavorite/cs/src/core/Async/AsyncOperationInternal.cs b/libs/storage/Tsavorite/cs/src/core/Async/AsyncOperationInternal.cs index 6a2f1d8abb..a3d4fe5ac8 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/AsyncOperationInternal.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/AsyncOperationInternal.cs @@ -32,20 +32,20 @@ internal interface IAsyncOperation /// /// The instance the async call was made on /// The for the pending operation - /// The for this operation + /// The for this operation /// The output to be populated by this operation /// - Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ITsavoriteSession tsavoriteSession, + Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ISessionFunctionsWrapper tsavoriteSession, out Output output); /// /// Performs the asynchronous operation. This may be a wait for either a page-flush or a disk-read IO. /// /// The instance the async call was made on - /// The for this operation + /// The for this operation /// The for the pending operation /// The cancellation token, if any /// - ValueTask DoSlowOperation(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + ValueTask DoSlowOperation(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, CancellationToken token); /// @@ -61,7 +61,7 @@ internal sealed class AsyncOperationInternal _tsavoriteKV; - readonly ITsavoriteSession _tsavoriteSession; + readonly ISessionFunctionsWrapper _tsavoriteSession; #pragma warning disable IDE0044 // Add readonly modifier // This cannot be readonly or it defensively copies and we will modify the internal state of the *temporary* @@ -71,7 +71,7 @@ internal sealed class AsyncOperationInternal _pendingContext; int CompletionComputeStatus; - internal AsyncOperationInternal(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + internal AsyncOperationInternal(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, ExceptionDispatchInfo exceptionDispatchInfo, TAsyncOperation asyncOperation) { _exception = exceptionDispatchInfo; diff --git a/libs/storage/Tsavorite/cs/src/core/Async/CompletePendingAsync.cs b/libs/storage/Tsavorite/cs/src/core/Async/CompletePendingAsync.cs index 08a2d46822..add8892a2e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/CompletePendingAsync.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/CompletePendingAsync.cs @@ -29,7 +29,7 @@ internal static ValueTask ReadyToCompletePendingAsync(Ts /// internal async ValueTask CompletePendingAsync(TsavoriteSession tsavoriteSession, CancellationToken token, CompletedOutputIterator completedOutputs) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { while (true) { diff --git a/libs/storage/Tsavorite/cs/src/core/Async/DeleteAsync.cs b/libs/storage/Tsavorite/cs/src/core/Async/DeleteAsync.cs index 9b9eb8a855..0049e947f0 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/DeleteAsync.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/DeleteAsync.cs @@ -24,7 +24,7 @@ internal DeleteAsyncOperation(ref DeleteOptions deleteOptions) public DeleteAsyncResult CreateCompletedResult(Status status, Output output, RecordMetadata recordMetadata) => new DeleteAsyncResult(status); /// - public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ITsavoriteSession tsavoriteSession, + public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ISessionFunctionsWrapper tsavoriteSession, out Output output) { OperationStatus internalStatus; @@ -39,7 +39,7 @@ public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingCo } /// - public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, CancellationToken token) => SlowDeleteAsync(tsavoriteKV, tsavoriteSession, pendingContext, deleteOptions, token); @@ -63,7 +63,7 @@ internal DeleteAsyncResult(Status status) updateAsyncInternal = default; } - internal DeleteAsyncResult(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + internal DeleteAsyncResult(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, ref DeleteOptions deleteOptions, ExceptionDispatchInfo exceptionDispatchInfo) { Status = new(StatusCode.Pending); @@ -86,7 +86,7 @@ public ValueTask> CompleteAsync(Cancel [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ValueTask> DeleteAsync(TsavoriteSession tsavoriteSession, ref Key key, ref DeleteOptions deleteOptions, Context userContext, CancellationToken token = default) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext { IsAsync = true }; @@ -114,7 +114,7 @@ internal ValueTask> DeleteAsync> SlowDeleteAsync( TsavoriteKV @this, - ITsavoriteSession tsavoriteSession, + ISessionFunctionsWrapper tsavoriteSession, PendingContext pcontext, DeleteOptions deleteOptions, CancellationToken token = default) { ExceptionDispatchInfo exceptionDispatchInfo = await WaitForFlushCompletionAsync(@this, pcontext.flushEvent, token).ConfigureAwait(false); diff --git a/libs/storage/Tsavorite/cs/src/core/Async/RMWAsync.cs b/libs/storage/Tsavorite/cs/src/core/Async/RMWAsync.cs index b5c925d1c8..7d1b6f6219 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/RMWAsync.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/RMWAsync.cs @@ -26,7 +26,7 @@ internal RmwAsyncOperation(AsyncIOContext diskRequest, ref RMWOption public RmwAsyncResult CreateCompletedResult(Status status, Output output, RecordMetadata recordMetadata) => new(status, output, recordMetadata); /// - public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ITsavoriteSession tsavoriteSession, + public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ISessionFunctionsWrapper tsavoriteSession, out Output output) { Status status = !diskRequest.IsDefault() @@ -39,7 +39,7 @@ public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingCo } /// - public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, CancellationToken token) => SlowRmwAsync(tsavoriteKV, tsavoriteSession, pendingContext, rmwOptions, diskRequest, token); @@ -72,14 +72,14 @@ internal RmwAsyncResult(Status status, TOutput output, RecordMetadata recordMeta updateAsyncInternal = default; } - internal RmwAsyncResult(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + internal RmwAsyncResult(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper sessionFunctions, PendingContext pendingContext, ref RMWOptions rmwOptions, AsyncIOContext diskRequest, ExceptionDispatchInfo exceptionDispatchInfo) { Status = new(StatusCode.Pending); Output = default; RecordMetadata = default; updateAsyncInternal = new AsyncOperationInternal, RmwAsyncResult>( - tsavoriteKV, tsavoriteSession, pendingContext, exceptionDispatchInfo, new(diskRequest, ref rmwOptions)); + tsavoriteKV, sessionFunctions, pendingContext, exceptionDispatchInfo, new(diskRequest, ref rmwOptions)); } /// Complete the RMW operation, issuing additional (rare) I/O asynchronously if needed. It is usually preferable to use Complete() instead of this. @@ -112,7 +112,7 @@ public ValueTask> CompleteAsync(Cancella [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ValueTask> RmwAsync(TsavoriteSession tsavoriteSession, ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context, CancellationToken token = default) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext { IsAsync = true }; var diskRequest = default(AsyncIOContext); @@ -134,7 +134,7 @@ internal ValueTask> RmwAsync(ITsavoriteSession tsavoriteSession, ref PendingContext pcontext, + private Status CallInternalRMW(ISessionFunctionsWrapper tsavoriteSession, ref PendingContext pcontext, ref Key key, ref Input input, ref Output output, ref RMWOptions rmwOptions, Context context, out AsyncIOContext diskRequest) { OperationStatus internalStatus; @@ -147,7 +147,7 @@ private Status CallInternalRMW(ITsavoriteSession> SlowRmwAsync( - TsavoriteKV @this, ITsavoriteSession tsavoriteSession, + TsavoriteKV @this, ISessionFunctionsWrapper tsavoriteSession, PendingContext pcontext, RMWOptions rmwOptions, AsyncIOContext diskRequest, CancellationToken token = default) { ExceptionDispatchInfo exceptionDispatchInfo; diff --git a/libs/storage/Tsavorite/cs/src/core/Async/ReadAsync.cs b/libs/storage/Tsavorite/cs/src/core/Async/ReadAsync.cs index 606fc8f8a4..b60e1f91d0 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/ReadAsync.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/ReadAsync.cs @@ -28,7 +28,7 @@ internal ReadAsyncOperation(AsyncIOContext diskRequest, long readAtA /// public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, - ITsavoriteSession tsavoriteSession, out Output output) + ISessionFunctionsWrapper tsavoriteSession, out Output output) { Status status = !diskRequest.IsDefault() ? tsavoriteKV.InternalCompletePendingRequestFromContext(tsavoriteSession, diskRequest, ref pendingContext, out var newDiskRequest) @@ -40,7 +40,7 @@ public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingCo } /// - public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, CancellationToken token) => SlowReadAsync(tsavoriteKV, tsavoriteSession, pendingContext, readAtAddress, readOptions, diskRequest, token); @@ -72,14 +72,14 @@ internal ReadAsyncResult(Status status, TOutput output, RecordMetadata recordMet updateAsyncInternal = default; } - internal ReadAsyncResult(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, PendingContext pendingContext, + internal ReadAsyncResult(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper sessionFunctions, PendingContext pendingContext, long readAtAddress, ref ReadOptions readOptions, AsyncIOContext diskRequest, ExceptionDispatchInfo exceptionDispatchInfo) { Status = new(StatusCode.Pending); Output = default; RecordMetadata = default; updateAsyncInternal = new AsyncOperationInternal, ReadAsyncResult>( - tsavoriteKV, tsavoriteSession, pendingContext, exceptionDispatchInfo, new ReadAsyncOperation(diskRequest, readAtAddress, ref readOptions)); + tsavoriteKV, sessionFunctions, pendingContext, exceptionDispatchInfo, new ReadAsyncOperation(diskRequest, readAtAddress, ref readOptions)); } /// Complete the RMW operation, issuing additional (rare) I/O synchronously if needed. @@ -103,7 +103,7 @@ internal ReadAsyncResult(TsavoriteKV tsavoriteKV, ITsavoriteSession< } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ValueTask> ReadAsync(ITsavoriteSession tsavoriteSession, + internal ValueTask> ReadAsync(ISessionFunctionsWrapper tsavoriteSession, ref Key key, ref Input input, ref ReadOptions readOptions, Context context, CancellationToken token, bool noKey = false) { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions, ref readOptions, isAsync: true, noKey: noKey); @@ -126,7 +126,7 @@ internal ValueTask> ReadAsync> ReadAtAddressAsync(ITsavoriteSession tsavoriteSession, + internal ValueTask> ReadAtAddressAsync(ISessionFunctionsWrapper tsavoriteSession, long readAtAddress, ref Key key, ref Input input, ref ReadOptions readOptions, Context context, CancellationToken token, bool noKey = false) { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions, ref readOptions, isAsync: true, noKey: noKey); @@ -149,7 +149,7 @@ internal ValueTask> ReadAtAddressAsync(ITsavoriteSession tsavoriteSession, + private Status CallInternalRead(ISessionFunctionsWrapper tsavoriteSession, ref PendingContext pcontext, long readAtAddress, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context context, out AsyncIOContext diskRequest) { @@ -168,7 +168,7 @@ private Status CallInternalRead(ITsavoriteSession> SlowReadAsync( - TsavoriteKV @this, ITsavoriteSession tsavoriteSession, + TsavoriteKV @this, ISessionFunctionsWrapper tsavoriteSession, PendingContext pcontext, long readAtAddress, ReadOptions readOptions, AsyncIOContext diskRequest, CancellationToken token = default) { ExceptionDispatchInfo exceptionDispatchInfo; diff --git a/libs/storage/Tsavorite/cs/src/core/Async/UpsertAsync.cs b/libs/storage/Tsavorite/cs/src/core/Async/UpsertAsync.cs index e64dec171b..bc1e50edf5 100644 --- a/libs/storage/Tsavorite/cs/src/core/Async/UpsertAsync.cs +++ b/libs/storage/Tsavorite/cs/src/core/Async/UpsertAsync.cs @@ -24,7 +24,7 @@ internal UpsertAsyncOperation(ref UpsertOptions upsertOptions) public UpsertAsyncResult CreateCompletedResult(Status status, Output output, RecordMetadata recordMetadata) => new UpsertAsyncResult(status, output, recordMetadata); /// - public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ITsavoriteSession tsavoriteSession, + public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingContext pendingContext, ISessionFunctionsWrapper tsavoriteSession, out Output output) { output = default; @@ -40,7 +40,7 @@ public Status DoFastOperation(TsavoriteKV tsavoriteKV, ref PendingCo } /// - public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + public ValueTask> DoSlowOperation(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper tsavoriteSession, PendingContext pendingContext, CancellationToken token) => SlowUpsertAsync(tsavoriteKV, tsavoriteSession, pendingContext, upsertOptions, token); @@ -72,14 +72,14 @@ internal UpsertAsyncResult(Status status, TOutput output, RecordMetadata recordM updateAsyncInternal = default; } - internal UpsertAsyncResult(TsavoriteKV tsavoriteKV, ITsavoriteSession tsavoriteSession, + internal UpsertAsyncResult(TsavoriteKV tsavoriteKV, ISessionFunctionsWrapper sessionFunctions, PendingContext pendingContext, ref UpsertOptions upsertOptions, ExceptionDispatchInfo exceptionDispatchInfo) { Status = new(StatusCode.Pending); Output = default; RecordMetadata = default; updateAsyncInternal = new AsyncOperationInternal, UpsertAsyncResult>( - tsavoriteKV, tsavoriteSession, pendingContext, exceptionDispatchInfo, new(ref upsertOptions)); + tsavoriteKV, sessionFunctions, pendingContext, exceptionDispatchInfo, new(ref upsertOptions)); } /// Complete the Upsert operation, issuing additional allocation asynchronously if needed. It is usually preferable to use Complete() instead of this. @@ -111,7 +111,7 @@ public ValueTask> CompleteAsync(Cance [MethodImpl(MethodImplOptions.AggressiveInlining)] internal ValueTask> UpsertAsync(TsavoriteSession tsavoriteSession, ref Key key, ref Input input, ref Value value, ref UpsertOptions upsertOptions, Context userContext, CancellationToken token = default) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext { IsAsync = true }; Output output = default; @@ -139,7 +139,7 @@ internal ValueTask> UpsertAsync> SlowUpsertAsync( - TsavoriteKV @this, ITsavoriteSession tsavoriteSession, + TsavoriteKV @this, ISessionFunctionsWrapper tsavoriteSession, PendingContext pcontext, UpsertOptions upsertOptions, CancellationToken token = default) { ExceptionDispatchInfo exceptionDispatchInfo = await WaitForFlushCompletionAsync(@this, pcontext.flushEvent, token).ConfigureAwait(false); diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/BasicContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/BasicContext.cs index edc34587b7..c6d89a3c36 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/BasicContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/BasicContext.cs @@ -11,22 +11,26 @@ namespace Tsavorite.core /// Basic Tsavorite Context implementation. /// public readonly struct BasicContext : ITsavoriteContext - where Functions : IFunctions + where Functions : ISessionFunctions { readonly ClientSession clientSession; + internal readonly SessionFunctionsWrapper> sessionFunctions; /// Indicates whether this struct has been initialized public bool IsNull => clientSession is null; + private TsavoriteKV store => clientSession.store; + internal BasicContext(ClientSession clientSession) { this.clientSession = clientSession; + sessionFunctions = new(clientSession); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnsafeResumeThread() - => clientSession.UnsafeResumeThread(); + => clientSession.UnsafeResumeThread(sessionFunctions); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -43,349 +47,598 @@ public void UnsafeSuspendThread() /// public bool CompletePending(bool wait = false, bool spinWaitForCommit = false) - => clientSession.CompletePending(wait, spinWaitForCommit); + => clientSession.CompletePending(sessionFunctions, wait, spinWaitForCommit); /// public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) - => clientSession.CompletePendingWithOutputs(out completedOutputs, wait, spinWaitForCommit); + => clientSession.CompletePendingWithOutputs(sessionFunctions, out completedOutputs, wait, spinWaitForCommit); /// public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingAsync(waitForCommit, token); + => clientSession.CompletePendingAsync(sessionFunctions, waitForCommit, token); /// public ValueTask> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingWithOutputsAsync(waitForCommit, token); + => clientSession.CompletePendingWithOutputsAsync(sessionFunctions, waitForCommit, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default) - => clientSession.Read(ref key, ref input, ref output, userContext); + { + UnsafeResumeThread(); + try + { + return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext = default) - => clientSession.Read(ref key, ref input, ref output, ref readOptions, userContext); + => Read(ref key, ref input, ref output, ref readOptions, out _, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(Key key, Input input, out Output output, Context userContext = default) - => clientSession.Read(key, input, out output, userContext); + { + output = default; + return Read(ref key, ref input, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(Key key, Input input, out Output output, ref ReadOptions readOptions, Context userContext = default) - => clientSession.Read(key, input, out output, ref readOptions, userContext); + { + output = default; + return Read(ref key, ref input, ref output, ref readOptions, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Output output, Context userContext = default) - => clientSession.Read(ref key, ref output, userContext); + { + Input input = default; + return Read(ref key, ref input, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Output output, ref ReadOptions readOptions, Context userContext = default) - => clientSession.Read(ref key, ref output, ref readOptions, userContext); + { + Input input = default; + return Read(ref key, ref input, ref output, ref readOptions, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(Key key, out Output output, Context userContext = default) - => clientSession.Read(key, out output, userContext); + { + Input input = default; + output = default; + return Read(ref key, ref input, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(Key key, out Output output, ref ReadOptions readOptions, Context userContext = default) - => clientSession.Read(key, out output, ref readOptions, userContext); + { + Input input = default; + output = default; + return Read(ref key, ref input, ref output, ref readOptions, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Status status, Output output) Read(Key key, Context userContext = default) - => clientSession.Read(key, userContext); + { + Input input = default; + Output output = default; + return (Read(ref key, ref input, ref output, userContext), output); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public (Status status, Output output) Read(Key key, ref ReadOptions readOptions, Context userContext = default) - => clientSession.Read(key, ref readOptions, userContext); + { + Input input = default; + Output output = default; + return (Read(ref key, ref input, ref output, ref readOptions, userContext), output); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.Read(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext); + { + UnsafeResumeThread(); + try + { + return store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.ReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext); + { + UnsafeResumeThread(); + try + { + return store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.ReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext); + { + UnsafeResumeThread(); + try + { + return store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, Context userContext = default, CancellationToken cancellationToken = default) - => clientSession.ReadAsync(ref key, ref input, userContext, cancellationToken); + { + ReadOptions readOptions = default; + return store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); + } + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) + => store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, Context context = default, CancellationToken token = default) - => clientSession.ReadAsync(key, input, context, token); + => ReadAsync(ref key, ref input, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, ref ReadOptions readOptions, Context context = default, CancellationToken token = default) - => clientSession.ReadAsync(key, input, ref readOptions, context, token); + => store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, Context userContext = default, CancellationToken token = default) - => clientSession.ReadAsync(ref key, userContext, token); + { + ReadOptions readOptions = default; + return ReadAsync(ref key, ref readOptions, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref ReadOptions readOptions, Context userContext = default, CancellationToken token = default) - => clientSession.ReadAsync(ref key, ref readOptions, userContext, token); + { + Input input = default; + return store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(Key key, Context context = default, CancellationToken token = default) - => clientSession.ReadAsync(key, context, token); + => ReadAsync(ref key, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAsync(Key key, ref ReadOptions readOptions, Context context = default, CancellationToken token = default) - => clientSession.ReadAsync(key, ref readOptions, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) - => clientSession.ReadAsync(ref key, ref input, ref readOptions, userContext, cancellationToken); + => ReadAsync(ref key, ref readOptions, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) - => clientSession.ReadAtAddressAsync(address, ref input, ref readOptions, userContext, cancellationToken); + public ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Input input, ref ReadOptions readOptions, + Context userContext = default, CancellationToken cancellationToken = default) + { + Key key = default; + return store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) - => clientSession.ReadAtAddressAsync(address, ref key, ref input, ref readOptions, userContext, cancellationToken); + => store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Value desiredValue, Context userContext = default) - => clientSession.Upsert(ref key, ref desiredValue, userContext); + { + Input input = default; + Output output = default; + return Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default) - => clientSession.Upsert(ref key, ref desiredValue, ref upsertOptions, userContext); + { + Input input = default; + Output output = default; + return Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) - => clientSession.Upsert(ref key, ref input, ref desiredValue, ref output, userContext); + => Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, Context userContext = default) - => clientSession.Upsert(ref key, ref input, ref desiredValue, ref output, ref upsertOptions, userContext); + => Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) + { + UnsafeResumeThread(); + try + { + return store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.Upsert(ref key, ref input, ref desiredValue, ref output, out recordMetadata, userContext); + => Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, out recordMetadata, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.Upsert(ref key, ref input, ref desiredValue, ref output, ref upsertOptions, out recordMetadata, userContext); + => Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, out recordMetadata, userContext); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) + { + UnsafeResumeThread(); + try + { + return store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(Key key, Value desiredValue, Context userContext = default) - => clientSession.Upsert(key, desiredValue, userContext); + => Upsert(ref key, ref desiredValue, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(Key key, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default) - => clientSession.Upsert(key, desiredValue, ref upsertOptions, userContext); + => Upsert(ref key, ref desiredValue, ref upsertOptions, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(Key key, Input input, Value desiredValue, ref Output output, Context userContext = default) - => clientSession.Upsert(key, input, desiredValue, ref output, userContext); + => Upsert(ref key, ref input, ref desiredValue, ref output, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Upsert(Key key, Input input, Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, Context userContext = default) - => clientSession.Upsert(ref key, ref input, ref desiredValue, ref output, ref upsertOptions, userContext); + => Upsert(ref key, ref input, ref desiredValue, ref output, ref upsertOptions, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Value desiredValue, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(ref key, ref desiredValue, userContext, token); + { + Input input = default; + return UpsertAsync(ref key, ref input, ref desiredValue, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(ref key, ref desiredValue, ref upsertOptions, userContext, token); + { + Input input = default; + return UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, Context userContext = default, CancellationToken token = default) + { + UpsertOptions upsertOptions = default; + return UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(ref key, ref input, ref desiredValue, userContext, token); + public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) + => store.UpsertAsync>>(sessionFunctions, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Value desiredValue, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(key, desiredValue, userContext, token); + => UpsertAsync(ref key, ref desiredValue, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(key, desiredValue, ref upsertOptions, userContext, token); + => UpsertAsync(ref key, ref desiredValue, ref upsertOptions, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Input input, Value desiredValue, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(key, input, desiredValue, userContext, token); + => UpsertAsync(ref key, ref input, ref desiredValue, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Input input, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => clientSession.UpsertAsync(key, input, desiredValue, ref upsertOptions, userContext, token); + => UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(ref Key key, ref Input input, ref Output output, Context userContext = default) - => clientSession.RMW(ref key, ref input, ref output, userContext); + => RMW(ref key, store.comparer.GetHashCode64(ref key), ref input, ref output, out _, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOptions rmwOptions, Context userContext = default) - => clientSession.RMW(ref key, ref input, ref output, ref rmwOptions, userContext); + => RMW(ref key, rmwOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref output, out _, userContext); + + /// + public Status RMW(ref Key key, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) + => RMW(ref key, store.comparer.GetHashCode64(ref key), ref input, ref output, out recordMetadata, userContext); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOptions rmwOptions, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.RMW(ref key, ref input, ref output, ref rmwOptions, out recordMetadata, userContext); + { + var keyHash = rmwOptions.KeyHash ?? store.comparer.GetHashCode64(ref key); + return RMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(ref Key key, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - => clientSession.RMW(ref key, ref input, ref output, out recordMetadata, userContext); + private Status RMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) + { + UnsafeResumeThread(); + try + { + return store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(Key key, Input input, out Output output, Context userContext = default) - => clientSession.RMW(key, input, out output, userContext); + { + output = default; + return RMW(ref key, ref input, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(Key key, Input input, out Output output, ref RMWOptions rmwOptions, Context userContext = default) - => clientSession.RMW(key, input, out output, ref rmwOptions, userContext); + { + output = default; + return RMW(ref key, ref input, ref output, ref rmwOptions, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(ref Key key, ref Input input, Context userContext = default) - => clientSession.RMW(ref key, ref input, userContext); + { + Output output = default; + return RMW(ref key, ref input, ref output, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context userContext = default) - => clientSession.RMW(ref key, ref input, ref rmwOptions, userContext); + { + Output output = default; + return RMW(ref key, ref input, ref output, ref rmwOptions, userContext); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(Key key, Input input, Context userContext = default) - => clientSession.RMW(key, input, userContext); + => RMW(ref key, ref input, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status RMW(Key key, Input input, ref RMWOptions rmwOptions, Context userContext = default) - => clientSession.RMW(key, input, ref rmwOptions, userContext); + => RMW(ref key, ref input, ref rmwOptions, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, Context context = default, CancellationToken token = default) - => clientSession.RMWAsync(ref key, ref input, context, token); + { + RMWOptions rmwOptions = default; + return store.RmwAsync>>(sessionFunctions, ref key, ref input, ref rmwOptions, context, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) - => clientSession.RMWAsync(ref key, ref input, ref rmwOptions, context, token); + => store.RmwAsync>>(sessionFunctions, ref key, ref input, ref rmwOptions, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.RmwAsyncResult> RMWAsync(Key key, Input input, Context context = default, CancellationToken token = default) - => clientSession.RMWAsync(key, input, context, token); + => RMWAsync(ref key, ref input, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.RmwAsyncResult> RMWAsync(Key key, Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) - => clientSession.RMWAsync(key, input, ref rmwOptions, context, token); + => RMWAsync(ref key, ref input, ref rmwOptions, context, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Delete(ref Key key, Context userContext = default) - => clientSession.Delete(ref key, userContext); + => Delete(ref key, store.comparer.GetHashCode64(ref key), userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Delete(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default) - => clientSession.Delete(ref key, ref deleteOptions, userContext); + => Delete(ref key, deleteOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), userContext); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private Status Delete(ref Key key, long keyHash, Context userContext = default) + { + UnsafeResumeThread(); + try + { + return store.ContextDelete>>(ref key, keyHash, userContext, sessionFunctions); + } + finally + { + UnsafeSuspendThread(); + } + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Delete(Key key, Context userContext = default) - => clientSession.Delete(key, userContext); + => Delete(ref key, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Delete(Key key, ref DeleteOptions deleteOptions, Context userContext = default) - => clientSession.Delete(key, ref deleteOptions, userContext); + => Delete(ref key, ref deleteOptions, userContext); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.DeleteAsyncResult> DeleteAsync(ref Key key, Context userContext = default, CancellationToken token = default) - => clientSession.DeleteAsync(ref key, userContext, token); + { + DeleteOptions deleteOptions = default; + return store.DeleteAsync>>(sessionFunctions, ref key, ref deleteOptions, userContext, token); + } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.DeleteAsyncResult> DeleteAsync(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) - => clientSession.DeleteAsync(ref key, ref deleteOptions, userContext, token); + => store.DeleteAsync>>(sessionFunctions, ref key, ref deleteOptions, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.DeleteAsyncResult> DeleteAsync(Key key, Context userContext = default, CancellationToken token = default) - => clientSession.DeleteAsync(key, userContext, token); + => DeleteAsync(ref key, userContext, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ValueTask.DeleteAsyncResult> DeleteAsync(Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) - => clientSession.DeleteAsync(key, ref deleteOptions, userContext, token); + => DeleteAsync(ref key, ref deleteOptions, userContext, token); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void ResetModified(Key key) + => clientSession.ResetModified(sessionFunctions, ref key); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetModified(ref Key key) - => clientSession.ResetModified(ref key); + => clientSession.ResetModified(sessionFunctions, ref key); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsModified(Key key) - => clientSession.IsModified(ref key); + => clientSession.IsModified(sessionFunctions, ref key); /// public void Refresh() - => clientSession.Refresh(); + => clientSession.Refresh(sessionFunctions); #endregion ITsavoriteContext + + /// + /// Copy key and value to tail, succeed only if key is known to not exist in between expectedLogicalAddress and tail. + /// + /// + /// + /// + /// + /// Lower-bound address (addresses are searched from tail (high) to head (low); do not search for "future records" earlier than this) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Status CompactionCopyToTail(ref Key key, ref Input input, ref Value value, ref Output output, long untilAddress) + { + UnsafeResumeThread(); + try + { + return store.CompactionConditionalCopyToTail>>( + sessionFunctions, ref key, ref input, ref value, ref output, untilAddress); + } + finally + { + UnsafeSuspendThread(); + } + } + + /// + /// Push a scan record to client if key is known to not exist in between expectedLogicalAddress and tail. + /// + /// Scan cursor tracking state, from the session on which this scan was initiated + /// + /// + /// + /// Lower-bound address (addresses are searched from tail (high) to head (low); do not search for "future records" earlier than this) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Status ConditionalScanPush(ScanCursorState scanCursorState, RecordInfo recordInfo, ref Key key, ref Value value, long untilAddress) + { + UnsafeResumeThread(); + try + { + return store.hlog.ConditionalScanPush>>( + sessionFunctions, scanCursorState, recordInfo, ref key, ref value, untilAddress); + } + finally + { + UnsafeSuspendThread(); + } + } + + /// + /// Checks whether specified record is present in memory (between max(fromAddress, HeadAddress) and tail), including tombstones. + /// + /// Key of the record. + /// Logical address of record, if found + /// Look until this address; if less than HeadAddress, then HeadAddress is used + /// Status + internal Status ContainsKeyInMemory(ref Key key, out long logicalAddress, long fromAddress = -1) + { + UnsafeResumeThread(); + try + { + return store.InternalContainsKeyInMemory>>( + ref key, sessionFunctions, out logicalAddress, fromAddress); + } + finally + { + UnsafeSuspendThread(); + } + } } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs index f7bca30d10..ada6ebc4b5 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSession.cs @@ -13,14 +13,8 @@ namespace Tsavorite.core /// /// Thread-independent session interface to Tsavorite /// - /// - /// - /// - /// - /// - /// - public sealed class ClientSession : IClientSession, ITsavoriteContext, IDisposable - where Functions : IFunctions + public sealed class ClientSession : IClientSession, IDisposable + where Functions : ISessionFunctions { internal readonly TsavoriteKV store; @@ -30,8 +24,6 @@ public sealed class ClientSession internal CompletedOutputIterator completedOutputs; - internal readonly InternalTsavoriteSession TsavoriteSession; - readonly UnsafeContext uContext; readonly LockableUnsafeContext luContext; readonly LockableContext lContext; @@ -50,7 +42,8 @@ public sealed class ClientSession ScanCursorState scanCursorState; - internal void AcquireLockable() + internal void AcquireLockable(TSessionFunctions sessionFunctions) + where TSessionFunctions : ISessionFunctionsWrapper { CheckIsNotAcquiredLockable(); @@ -60,7 +53,7 @@ internal void AcquireLockable() while (IsInPreparePhase()) { if (store.epoch.ThisInstanceProtected()) - store.InternalRefresh(TsavoriteSession); + store.InternalRefresh(sessionFunctions); Thread.Yield(); } @@ -121,7 +114,6 @@ internal ClientSession( this.store = store; this.ctx = ctx; this.functions = functions; - TsavoriteSession = new InternalTsavoriteSession(this); } /// @@ -140,7 +132,9 @@ internal ClientSession( public void Dispose() { completedOutputs?.Dispose(); - CompletePending(true); + + // By the time Dispose is called, we should have no outstanding locks, so can use the BasicContext's sessionFunctions. + _ = CompletePending(bContext.sessionFunctions, true); store.DisposeClientSession(ID, ctx.phase); } @@ -189,13 +183,13 @@ public LockableContext LockableCo public long GetKeyHash(ref Key key) => store.GetKeyHash(ref key); /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default) + internal void Refresh(TSessionFunctionsWrapper sessionFunctions) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { - UnsafeResumeThread(); + UnsafeResumeThread(sessionFunctions); try { - return store.ContextRead(ref key, ref input, ref output, userContext, TsavoriteSession); + store.InternalRefresh(sessionFunctions); } finally { @@ -204,497 +198,13 @@ public Status Read(ref Key key, ref Input input, ref Output output, Context user } /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext = default) - => Read(ref key, ref input, ref output, ref readOptions, out _, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(Key key, Input input, out Output output, Context userContext = default) - { - output = default; - return Read(ref key, ref input, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(Key key, Input input, out Output output, ref ReadOptions readOptions, Context userContext = default) - { - output = default; - return Read(ref key, ref input, ref output, ref readOptions, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(ref Key key, ref Output output, Context userContext = default) - { - Input input = default; - return Read(ref key, ref input, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(ref Key key, ref Output output, ref ReadOptions readOptions, Context userContext = default) - { - Input input = default; - return Read(ref key, ref input, ref output, ref readOptions, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(Key key, out Output output, Context userContext = default) - { - Input input = default; - output = default; - return Read(ref key, ref input, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(Key key, out Output output, ref ReadOptions readOptions, Context userContext = default) - { - Input input = default; - output = default; - return Read(ref key, ref input, ref output, ref readOptions, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (Status status, Output output) Read(Key key, Context userContext = default) - { - Input input = default; - Output output = default; - return (Read(ref key, ref input, ref output, userContext), output); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public (Status status, Output output) Read(Key key, ref ReadOptions readOptions, Context userContext = default) - { - Input input = default; - Output output = default; - return (Read(ref key, ref input, ref output, ref readOptions, userContext), output); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) + internal void ResetModified(TSessionFunctionsWrapper sessionFunctions, ref Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { - UnsafeResumeThread(); + UnsafeResumeThread(sessionFunctions); try { - return store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, Context userContext = default, CancellationToken cancellationToken = default) - { - ReadOptions readOptions = default; - return store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) - => store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, Context context = default, CancellationToken token = default) - => ReadAsync(ref key, ref input, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, ref ReadOptions readOptions, Context context = default, CancellationToken token = default) - => store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, Context userContext = default, CancellationToken token = default) - { - ReadOptions readOptions = default; - return ReadAsync(ref key, ref readOptions, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref ReadOptions readOptions, Context userContext = default, CancellationToken token = default) - { - Input input = default; - return store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(Key key, Context context = default, CancellationToken token = default) - => ReadAsync(ref key, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAsync(Key key, ref ReadOptions readOptions, Context context = default, CancellationToken token = default) - => ReadAsync(ref key, ref readOptions, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Input input, ref ReadOptions readOptions, - Context userContext = default, CancellationToken cancellationToken = default) - { - Key key = default; - return store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) - => store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Value desiredValue, Context userContext = default) - { - Input input = default; - Output output = default; - return Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default) - { - Input input = default; - Output output = default; - return Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) - => Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, Context userContext = default) - => Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - => Upsert(ref key, store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, out recordMetadata, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, out RecordMetadata recordMetadata, Context userContext = default) - => Upsert(ref key, upsertOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref desiredValue, ref output, out recordMetadata, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(Key key, Value desiredValue, Context userContext = default) - => Upsert(ref key, ref desiredValue, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(Key key, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default) - => Upsert(ref key, ref desiredValue, ref upsertOptions, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(Key key, Input input, Value desiredValue, ref Output output, Context userContext = default) - => Upsert(ref key, ref input, ref desiredValue, ref output, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Upsert(Key key, Input input, Value desiredValue, ref Output output, ref UpsertOptions upsertOptions, Context userContext = default) - => Upsert(ref key, ref input, ref desiredValue, ref output, ref upsertOptions, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Value desiredValue, Context userContext = default, CancellationToken token = default) - { - Input input = default; - return UpsertAsync(ref key, ref input, ref desiredValue, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - { - Input input = default; - return UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, Context userContext = default, CancellationToken token = default) - { - UpsertOptions upsertOptions = default; - return UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => store.UpsertAsync(TsavoriteSession, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Value desiredValue, Context userContext = default, CancellationToken token = default) - => UpsertAsync(ref key, ref desiredValue, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => UpsertAsync(ref key, ref desiredValue, ref upsertOptions, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Input input, Value desiredValue, Context userContext = default, CancellationToken token = default) - => UpsertAsync(ref key, ref input, ref desiredValue, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.UpsertAsyncResult> UpsertAsync(Key key, Input input, Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) - => UpsertAsync(ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(ref Key key, ref Input input, ref Output output, Context userContext = default) - => RMW(ref key, store.comparer.GetHashCode64(ref key), ref input, ref output, out _, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOptions rmwOptions, Context userContext = default) - => RMW(ref key, rmwOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), ref input, ref output, out _, userContext); - - /// - public Status RMW(ref Key key, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - => RMW(ref key, store.comparer.GetHashCode64(ref key), ref input, ref output, out recordMetadata, userContext); - - /// - public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOptions rmwOptions, out RecordMetadata recordMetadata, Context userContext = default) - { - var keyHash = rmwOptions.KeyHash ?? store.comparer.GetHashCode64(ref key); - return RMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Status RMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(Key key, Input input, out Output output, Context userContext = default) - { - output = default; - return RMW(ref key, ref input, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(Key key, Input input, out Output output, ref RMWOptions rmwOptions, Context userContext = default) - { - output = default; - return RMW(ref key, ref input, ref output, ref rmwOptions, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(ref Key key, ref Input input, Context userContext = default) - { - Output output = default; - return RMW(ref key, ref input, ref output, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context userContext = default) - { - Output output = default; - return RMW(ref key, ref input, ref output, ref rmwOptions, userContext); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(Key key, Input input, Context userContext = default) - => RMW(ref key, ref input, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status RMW(Key key, Input input, ref RMWOptions rmwOptions, Context userContext = default) - => RMW(ref key, ref input, ref rmwOptions, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, Context context = default, CancellationToken token = default) - { - RMWOptions rmwOptions = default; - return store.RmwAsync(TsavoriteSession, ref key, ref input, ref rmwOptions, context, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) - => store.RmwAsync(TsavoriteSession, ref key, ref input, ref rmwOptions, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.RmwAsyncResult> RMWAsync(Key key, Input input, Context context = default, CancellationToken token = default) - => RMWAsync(ref key, ref input, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.RmwAsyncResult> RMWAsync(Key key, Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) - => RMWAsync(ref key, ref input, ref rmwOptions, context, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Delete(ref Key key, Context userContext = default) - => Delete(ref key, store.comparer.GetHashCode64(ref key), userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Delete(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default) - => Delete(ref key, deleteOptions.KeyHash ?? store.comparer.GetHashCode64(ref key), userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private Status Delete(ref Key key, long keyHash, Context userContext = default) - { - UnsafeResumeThread(); - try - { - return store.ContextDelete(ref key, keyHash, userContext, TsavoriteSession); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Delete(Key key, Context userContext = default) - => Delete(ref key, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Status Delete(Key key, ref DeleteOptions deleteOptions, Context userContext = default) - => Delete(ref key, ref deleteOptions, userContext); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.DeleteAsyncResult> DeleteAsync(ref Key key, Context userContext = default, CancellationToken token = default) - { - DeleteOptions deleteOptions = default; - return store.DeleteAsync(TsavoriteSession, ref key, ref deleteOptions, userContext, token); - } - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.DeleteAsyncResult> DeleteAsync(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) - => store.DeleteAsync(TsavoriteSession, ref key, ref deleteOptions, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.DeleteAsyncResult> DeleteAsync(Key key, Context userContext = default, CancellationToken token = default) - => DeleteAsync(ref key, userContext, token); - - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ValueTask.DeleteAsyncResult> DeleteAsync(Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) - => DeleteAsync(ref key, ref deleteOptions, userContext, token); - - /// - public void Refresh() - { - UnsafeResumeThread(); - store.InternalRefresh(TsavoriteSession); - UnsafeSuspendThread(); - } - - /// - public void ResetModified(ref Key key) - { - UnsafeResumeThread(); - try - { - UnsafeResetModified(ref key); + UnsafeResetModified(sessionFunctions, ref key); } finally { @@ -722,14 +232,16 @@ public void ResetModified(ref Key key) #region Pending Operations /// - public bool CompletePending(bool wait = false, bool spinWaitForCommit = false) - => CompletePending(false, wait, spinWaitForCommit); + internal bool CompletePending(TSessionFunctionsWrapper sessionFunctions, bool wait = false, bool spinWaitForCommit = false) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper + => CompletePending(sessionFunctions, getOutputs: false, wait, spinWaitForCommit); /// - public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) + internal bool CompletePendingWithOutputs(TSessionFunctionsWrapper sessionFunctions, out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { InitializeCompletedOutputs(); - var result = CompletePending(true, wait, spinWaitForCommit); + var result = CompletePending(sessionFunctions, getOutputs: true, wait, spinWaitForCommit); completedOutputs = this.completedOutputs; return result; } @@ -738,11 +250,11 @@ public bool CompletePendingWithOutputs(out CompletedOutputIterator - internal bool UnsafeCompletePendingWithOutputs(TsavoriteSession tsavoriteSession, out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) - where TsavoriteSession : ITsavoriteSession + internal bool UnsafeCompletePendingWithOutputs(TSessionFunctionsWrapper sessionFunctions, out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { InitializeCompletedOutputs(); - var result = UnsafeCompletePending(tsavoriteSession, true, wait, spinWaitForCommit); + var result = UnsafeCompletePending(sessionFunctions, true, wait, spinWaitForCommit); completedOutputs = this.completedOutputs; return result; } @@ -755,12 +267,13 @@ private void InitializeCompletedOutputs() completedOutputs.Dispose(); } - internal bool CompletePending(bool getOutputs, bool wait, bool spinWaitForCommit) + internal bool CompletePending(TSessionFunctionsWrapper sessionFunctions, bool getOutputs, bool wait, bool spinWaitForCommit) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { - UnsafeResumeThread(); + UnsafeResumeThread(sessionFunctions); try { - return UnsafeCompletePending(TsavoriteSession, getOutputs, wait, spinWaitForCommit); + return UnsafeCompletePending(sessionFunctions, getOutputs, wait, spinWaitForCommit); } finally { @@ -768,21 +281,21 @@ internal bool CompletePending(bool getOutputs, bool wait, bool spinWaitForCommit } } - internal bool UnsafeCompletePending(TsavoriteSession tsavoriteSession, bool getOutputs, bool wait, bool spinWaitForCommit) - where TsavoriteSession : ITsavoriteSession + internal bool UnsafeCompletePending(TSessionFunctionsWrapper sessionFunctions, bool getOutputs, bool wait, bool spinWaitForCommit) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { var requestedOutputs = getOutputs ? completedOutputs : default; - var result = store.InternalCompletePending(tsavoriteSession, wait, requestedOutputs); + var result = store.InternalCompletePending(sessionFunctions, wait, requestedOutputs); if (spinWaitForCommit) { if (!wait) throw new TsavoriteException("Can spin-wait for commit (checkpoint completion) only if wait is true"); do { - store.InternalCompletePending(tsavoriteSession, wait, requestedOutputs); + _ = store.InternalCompletePending(sessionFunctions, wait, requestedOutputs); if (store.InRestPhase()) { - store.InternalCompletePending(tsavoriteSession, wait, requestedOutputs); + _ = store.InternalCompletePending(sessionFunctions, wait, requestedOutputs); return true; } } while (wait); @@ -791,18 +304,22 @@ internal bool UnsafeCompletePending(TsavoriteSession tsavorite } /// - public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default) - => CompletePendingAsync(false, waitForCommit, token); + internal ValueTask CompletePendingAsync(TSessionFunctionsWrapper sessionFunctions, bool waitForCommit = false, CancellationToken token = default) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper + => CompletePendingAsync(sessionFunctions, getOutputs: false, waitForCommit, token); /// - public async ValueTask> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default) + internal async ValueTask> CompletePendingWithOutputsAsync(TSessionFunctionsWrapper sessionFunctions, + bool waitForCommit = false, CancellationToken token = default) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { InitializeCompletedOutputs(); - await CompletePendingAsync(true, waitForCommit, token).ConfigureAwait(false); + await CompletePendingAsync(sessionFunctions, getOutputs: true, waitForCommit, token).ConfigureAwait(false); return completedOutputs; } - private async ValueTask CompletePendingAsync(bool getOutputs, bool waitForCommit = false, CancellationToken token = default) + private async ValueTask CompletePendingAsync(TSessionFunctionsWrapper sessionFunctions, bool getOutputs, bool waitForCommit = false, CancellationToken token = default) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { token.ThrowIfCancellationRequested(); @@ -810,11 +327,11 @@ private async ValueTask CompletePendingAsync(bool getOutputs, bool waitForCommit throw new NotSupportedException("Async operations not supported over protected epoch"); // Complete all pending operations on session - await store.CompletePendingAsync(TsavoriteSession, token, getOutputs ? completedOutputs : null).ConfigureAwait(false); + await store.CompletePendingAsync(sessionFunctions, token, getOutputs ? completedOutputs : null).ConfigureAwait(false); // Wait for commit if necessary if (waitForCommit) - await WaitForCommitAsync(token).ConfigureAwait(false); + await WaitForCommitAsync(sessionFunctions, token).ConfigureAwait(false); } /// @@ -837,24 +354,28 @@ public async ValueTask ReadyToCompletePendingAsync(CancellationToken token = def #region Other Operations - internal void UnsafeResetModified(ref Key key) + internal void UnsafeResetModified(TSessionFunctionsWrapper sessionFunctions, ref Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { OperationStatus status; do status = store.InternalModifiedBitOperation(ref key, out _); - while (store.HandleImmediateNonPendingRetryStatus(status, TsavoriteSession)); + while (store.HandleImmediateNonPendingRetryStatus(status, sessionFunctions)); } /// - public unsafe void ResetModified(Key key) => ResetModified(ref key); + internal unsafe void ResetModified(TSessionFunctionsWrapper sessionFunctions, Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper + => ResetModified(sessionFunctions, ref key); /// - internal bool IsModified(ref Key key) + internal bool IsModified(TSessionFunctionsWrapper sessionFunctions, ref Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { - UnsafeResumeThread(); + UnsafeResumeThread(sessionFunctions); try { - return UnsafeIsModified(ref key); + return UnsafeIsModified(sessionFunctions, ref key); } finally { @@ -862,25 +383,29 @@ internal bool IsModified(ref Key key) } } - internal bool UnsafeIsModified(ref Key key) + internal bool UnsafeIsModified(TSessionFunctionsWrapper sessionFunctions, ref Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { RecordInfo modifiedInfo; OperationStatus status; do status = store.InternalModifiedBitOperation(ref key, out modifiedInfo, false); - while (store.HandleImmediateNonPendingRetryStatus(status, TsavoriteSession)); + while (store.HandleImmediateNonPendingRetryStatus(status, sessionFunctions)); return modifiedInfo.Modified; } /// - internal unsafe bool IsModified(Key key) => IsModified(ref key); + internal unsafe bool IsModified(TSessionFunctionsWrapper sessionFunctions, Key key) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper + => IsModified(sessionFunctions, ref key); /// /// Wait for commit of all operations completed until the current point in session. /// Does not itself issue checkpoint/commits. /// /// - public async ValueTask WaitForCommitAsync(CancellationToken token = default) + private async ValueTask WaitForCommitAsync(TSessionFunctionsWrapper sessionFunctions, CancellationToken token = default) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { token.ThrowIfCancellationRequested(); @@ -888,14 +413,14 @@ public async ValueTask WaitForCommitAsync(CancellationToken token = default) throw new TsavoriteException("Make sure all async operations issued on this session are awaited and completed first"); // Complete all pending sync operations on session - await CompletePendingAsync(token: token).ConfigureAwait(false); + await CompletePendingAsync(sessionFunctions, token: token).ConfigureAwait(false); var task = store.CheckpointTask; while (true) { _ = await task.WithCancellationAsync(token).ConfigureAwait(false); - Refresh(); + Refresh(sessionFunctions); task = store.CheckpointTask; } } @@ -954,70 +479,6 @@ public long Compact(ref Input input, ref Output output, lon return store.Compact(functions, compactionFunctions, ref input, ref output, untilAddress, compactionType); } - /// - /// Copy key and value to tail, succeed only if key is known to not exist in between expectedLogicalAddress and tail. - /// - /// - /// - /// - /// - /// Lower-bound address (addresses are searched from tail (high) to head (low); do not search for "future records" earlier than this) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Status CompactionCopyToTail(ref Key key, ref Input input, ref Value value, ref Output output, long untilAddress) - { - UnsafeResumeThread(); - try - { - return store.CompactionConditionalCopyToTail(TsavoriteSession, ref key, ref input, ref value, ref output, untilAddress); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - /// Push a scan record to client if key is known to not exist in between expectedLogicalAddress and tail. - /// - /// Scan cursor tracking state, from the session on which this scan was initiated - /// - /// - /// - /// Lower-bound address (addresses are searched from tail (high) to head (low); do not search for "future records" earlier than this) - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Status ConditionalScanPush(ScanCursorState scanCursorState, RecordInfo recordInfo, ref Key key, ref Value value, long untilAddress) - { - UnsafeResumeThread(); - try - { - return store.hlog.ConditionalScanPush(TsavoriteSession, scanCursorState, recordInfo, ref key, ref value, untilAddress); - } - finally - { - UnsafeSuspendThread(); - } - } - - /// - /// Checks whether specified record is present in memory (between max(fromAddress, HeadAddress) and tail), including tombstones. - /// - /// Key of the record. - /// Logical address of record, if found - /// Look until this address; if less than HeadAddress, then HeadAddress is used - /// Status - internal Status ContainsKeyInMemory(ref Key key, out long logicalAddress, long fromAddress = -1) - { - UnsafeResumeThread(); - try - { - return store.InternalContainsKeyInMemory(ref key, TsavoriteSession, out logicalAddress, fromAddress); - } - finally - { - UnsafeSuspendThread(); - } - } - /// /// Pull iterator for all (distinct) live key-values stored in Tsavorite /// @@ -1060,12 +521,13 @@ public bool ScanCursor(ref long cursor, long count, TScanFunctio /// Resume session on current thread. IMPORTANT: Call SuspendThread before any async op. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void UnsafeResumeThread() + internal void UnsafeResumeThread(TSessionFunctionsWrapper sessionFunctions) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { // We do not track any "acquired" state here; if someone mixes calls between safe and unsafe contexts, they will // get the "trying to acquire already-acquired epoch" error. store.epoch.Resume(); - store.InternalRefresh(TsavoriteSession); + store.InternalRefresh(sessionFunctions); } /// @@ -1080,7 +542,7 @@ internal void UnsafeSuspendThread() void IClientSession.AtomicSwitch(long version) { - TsavoriteKV.AtomicSwitch(ctx, ctx.prevCtx, version); + _ = TsavoriteKV.AtomicSwitch(ctx, ctx.prevCtx, version); } /// @@ -1102,7 +564,8 @@ internal bool IsInPreparePhase() #region ITsavoriteSession [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RecordInfo recordInfo, ref RMWInfo rmwInfo, out OperationStatus status) + internal bool InPlaceUpdater(TSessionFunctionsWrapper sessionFunctions, ref Key key, ref Input input, ref Value value, ref Output output, ref RecordInfo recordInfo, ref RMWInfo rmwInfo, out OperationStatus status) + where TSessionFunctionsWrapper : ISessionFunctionsWrapper { // Note: KeyIndexes do not need notification of in-place updates because the key does not change. if (functions.InPlaceUpdater(ref key, ref input, ref value, ref output, ref rmwInfo, ref recordInfo)) @@ -1120,8 +583,8 @@ internal bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, ref if (rmwInfo.Action == RMWAction.ExpireAndResume) { // This inserts the tombstone if appropriate - return store.ReinitializeExpiredRecord(ref key, ref input, ref value, ref output, ref recordInfo, - ref rmwInfo, rmwInfo.Address, TsavoriteSession, isIpu: true, out status); + return store.ReinitializeExpiredRecord(ref key, ref input, ref value, ref output, ref recordInfo, + ref rmwInfo, rmwInfo.Address, sessionFunctions, isIpu: true, out status); } if (rmwInfo.Action == RMWAction.ExpireAndStop) { @@ -1134,213 +597,6 @@ internal bool InPlaceUpdater(ref Key key, ref Input input, ref Value value, ref return false; } - // This is a struct to allow JIT to inline calls (and bypass default interface call mechanism) - internal readonly struct InternalTsavoriteSession : ITsavoriteSession - { - private readonly ClientSession _clientSession; - - public InternalTsavoriteSession(ClientSession clientSession) - { - _clientSession = clientSession; - } - - public bool IsManualLocking => false; - public TsavoriteKV Store => _clientSession.store; - - #region IFunctions - Reads - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo) - => _clientSession.functions.SingleReader(ref key, ref input, ref value, ref dst, ref readInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) - => _clientSession.functions.ConcurrentReader(ref key, ref input, ref value, ref dst, ref readInfo, ref recordInfo); - - public void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) - => _clientSession.functions.ReadCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); - - #endregion IFunctions - Reads - - #region IFunctions - Upserts - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public 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) - => _clientSession.functions.SingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentWriter(long physicalAddress, ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo) - { - (upsertInfo.UsedValueLength, upsertInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref dst, ref recordInfo); - if (!_clientSession.functions.ConcurrentWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, ref recordInfo)) - return false; - _clientSession.store.SetExtraValueLength(ref dst, ref recordInfo, upsertInfo.UsedValueLength, upsertInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion IFunctions - Upserts - - #region IFunctions - RMWs - #region InitialUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NeedInitialUpdate(ref Key key, ref Input input, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.NeedInitialUpdate(ref key, ref input, ref output, ref rmwInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - => _clientSession.functions.InitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); - } - #endregion InitialUpdater - - #region CopyUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.NeedCopyUpdate(ref key, ref input, ref oldValue, ref output, ref rmwInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - => _clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); - } - #endregion CopyUpdater - - #region InPlaceUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool InPlaceUpdater(long physicalAddress, ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, out OperationStatus status, ref RecordInfo recordInfo) - { - (rmwInfo.UsedValueLength, rmwInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); - if (!_clientSession.InPlaceUpdater(ref key, ref input, ref value, ref output, ref recordInfo, ref rmwInfo, out status)) - return false; - _clientSession.store.SetExtraValueLength(ref value, ref recordInfo, rmwInfo.UsedValueLength, rmwInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion InPlaceUpdater - - public void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) - => _clientSession.functions.RMWCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); - - #endregion IFunctions - RMWs - - #region IFunctions - Deletes - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) - => _clientSession.functions.SingleDeleter(ref key, ref value, ref deleteInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostSingleDeleter(ref Key key, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostSingleDeleter(ref key, ref deleteInfo); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentDeleter(long physicalAddress, ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo, out int allocatedSize) - { - (deleteInfo.UsedValueLength, deleteInfo.FullValueLength, allocatedSize) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); - if (!_clientSession.functions.ConcurrentDeleter(ref key, ref value, ref deleteInfo, ref recordInfo)) - return false; - _clientSession.store.SetTombstoneAndExtraValueLength(ref value, ref recordInfo, deleteInfo.UsedValueLength, deleteInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion IFunctions - Deletes - - #region IFunctions - Dispose - public void DisposeSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason) - => _clientSession.functions.DisposeSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); - public void DisposeCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.DisposeCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); - public void DisposeInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.DisposeInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); - public void DisposeSingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo) - => _clientSession.functions.DisposeSingleDeleter(ref key, ref value, ref deleteInfo); - public void DisposeDeserializedFromDisk(ref Key key, ref Value value, ref RecordInfo recordInfo) - => _clientSession.functions.DisposeDeserializedFromDisk(ref key, ref value); - public void DisposeForRevivification(ref Key key, ref Value value, int newKeySize, ref RecordInfo recordInfo) - => _clientSession.functions.DisposeForRevivification(ref key, ref value, newKeySize); - #endregion IFunctions - Dispose - - #region Transient locking - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) - { - if (!Store.IsLocking) - return true; - if (!Store.LockTable.TryLockTransientExclusive(ref key, ref stackCtx.hei)) - return false; - stackCtx.recSrc.SetHasTransientXLock(); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx) - { - if (!Store.IsLocking) - return true; - if (!Store.LockTable.TryLockTransientShared(ref key, ref stackCtx.hei)) - return false; - stackCtx.recSrc.SetHasTransientSLock(); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) - { - if (!Store.IsLocking) - return; - Store.LockTable.UnlockExclusive(ref key, ref stackCtx.hei); - stackCtx.recSrc.ClearHasTransientXLock(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx) - { - if (!Store.IsLocking) - return; - Store.LockTable.UnlockShared(ref key, ref stackCtx.hei); - stackCtx.recSrc.ClearHasTransientSLock(); - } - #endregion Transient locking - - #region Internal utilities - public int GetRMWInitialValueLength(ref Input input) => _clientSession.functions.GetRMWInitialValueLength(ref input); - - public int GetRMWModifiedValueLength(ref Value t, ref Input input) => _clientSession.functions.GetRMWModifiedValueLength(ref t, ref input); - - public IHeapContainer GetHeapContainer(ref Input input) - { - if (typeof(Input) == typeof(SpanByte)) - return new SpanByteHeapContainer(ref Unsafe.As(ref input), _clientSession.store.hlog.bufferPool) as IHeapContainer; - return new StandardHeapContainer(ref input); - } - - public void UnsafeResumeThread() => _clientSession.UnsafeResumeThread(); - - public void UnsafeSuspendThread() => _clientSession.UnsafeSuspendThread(); - - public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) - => _clientSession.CompletePendingWithOutputs(out completedOutputs, wait, spinWaitForCommit); - - public TsavoriteKV.TsavoriteExecutionContext Ctx => _clientSession.ctx; - #endregion Internal utilities - } #endregion ITsavoriteSession } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSessionBuilder.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSessionBuilder.cs index 1b2a527bfe..a3aa328fff 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSessionBuilder.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/ClientSessionBuilder.cs @@ -20,7 +20,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase /// Session instance public ClientSession NewSession(Functions functions, string sessionName = null, ReadCopyOptions readCopyOptions = default) - where Functions : IFunctions + where Functions : ISessionFunctions { if (functions == null) throw new ArgumentNullException(nameof(functions)); diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/ITsavoriteContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/ITsavoriteContext.cs index 2f2c5157cd..42d764eb2f 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/ITsavoriteContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/ITsavoriteContext.cs @@ -71,7 +71,7 @@ public interface ITsavoriteContext : ITsavor /// Input to help extract the retrieved value into /// The location to place the retrieved value /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default); /// @@ -82,7 +82,7 @@ public interface ITsavoriteContext : ITsavor /// The location to place the retrieved value /// Contains options controlling the Read operation /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext = default); /// @@ -92,7 +92,7 @@ public interface ITsavoriteContext : ITsavor /// Input to help extract the retrieved value into /// The location to place the retrieved value /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(Key key, Input input, out Output output, Context userContext = default); /// @@ -103,7 +103,7 @@ public interface ITsavoriteContext : ITsavor /// The location to place the retrieved value /// Contains options controlling the Read operation /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(Key key, Input input, out Output output, ref ReadOptions readOptions, Context userContext = default); /// @@ -112,7 +112,7 @@ public interface ITsavoriteContext : ITsavor /// The key to look up /// The location to place the retrieved value /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(ref Key key, ref Output output, Context userContext = default); /// @@ -122,7 +122,7 @@ public interface ITsavoriteContext : ITsavor /// The location to place the retrieved value /// Contains options controlling the Read operation /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(ref Key key, ref Output output, ref ReadOptions readOptions, Context userContext = default); /// @@ -178,7 +178,7 @@ public interface ITsavoriteContext : ITsavor /// /// /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation + /// is populated by the implementation Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default); /// @@ -190,7 +190,7 @@ public interface ITsavoriteContext : ITsavor /// Contains options controlling the Read operation, including the address to read at in StartAddress /// On output, receives metadata about the record /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation; this should store the key if it needs it + /// is populated by the implementation; this should store the key if it needs it Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default); /// @@ -203,7 +203,7 @@ public interface ITsavoriteContext : ITsavor /// Contains options controlling the Read operation, including the address to read at in StartAddress /// On output, receives metadata about the record /// User application context passed in case the read goes pending due to IO - /// is populated by the implementation; this should store the key if it needs it + /// is populated by the implementation; this should store the key if it needs it Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default); /// @@ -220,7 +220,7 @@ public interface ITsavoriteContext : ITsavor /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, Context userContext = default, CancellationToken cancellationToken = default); /// @@ -238,7 +238,7 @@ public interface ITsavoriteContext : ITsavor /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, Context context = default, CancellationToken token = default); /// @@ -257,7 +257,7 @@ public interface ITsavoriteContext : ITsavor /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, ref ReadOptions readOptions, Context context = default, CancellationToken token = default); /// @@ -277,7 +277,7 @@ public interface ITsavoriteContext : ITsavor /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record /// ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default); @@ -295,7 +295,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(ref Key key, Context userContext = default, CancellationToken token = default); /// @@ -312,7 +312,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref ReadOptions readOptions, Context userContext = default, CancellationToken token = default); /// @@ -329,7 +329,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(Key key, Context context = default, CancellationToken token = default); /// @@ -347,7 +347,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAsync(Key key, ref ReadOptions readOptions, Context context = default, CancellationToken token = default); /// @@ -365,7 +365,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default); @@ -385,7 +385,7 @@ ValueTask.ReadAsyncResult> ReadA /// result. /// /// to complete the read operation and obtain the result status, the output that is populated by the - /// implementation, and optionally a copy of the header for the retrieved record + /// implementation, and optionally a copy of the header for the retrieved record ValueTask.ReadAsyncResult> ReadAtAddressAsync(long address, ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default); diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs index af1fb57c8e..e444073df4 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableContext.cs @@ -13,10 +13,10 @@ namespace Tsavorite.core /// Tsavorite Context implementation that allows manual control of record locking and epoch management. For advanced use only. /// public readonly struct LockableContext : ITsavoriteContext, ILockableContext - where Functions : IFunctions + where Functions : ISessionFunctions { readonly ClientSession clientSession; - readonly InternalTsavoriteSession TsavoriteSession; + readonly SessionFunctionsWrapper> sessionFunctions; /// Indicates whether this struct has been initialized public bool IsNull => clientSession is null; @@ -26,13 +26,13 @@ namespace Tsavorite.core internal LockableContext(ClientSession clientSession) { this.clientSession = clientSession; - TsavoriteSession = new InternalTsavoriteSession(clientSession); + sessionFunctions = new(clientSession); } #region Begin/EndLockable /// - public void BeginLockable() => clientSession.AcquireLockable(); + public void BeginLockable() => clientSession.AcquireLockable(sessionFunctions); /// public void EndLockable() => clientSession.ReleaseLockable(); @@ -59,7 +59,7 @@ internal LockableContext(ClientSession(TsavoriteSession tsavoriteSession, ClientSession clientSession, TLockableKey[] keys, int start, int count) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : 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, @@ -103,7 +103,7 @@ internal static bool DoInternalLock(TsavoriteSes [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool DoInternalTryLock(TsavoriteSession tsavoriteSession, ClientSession clientSession, TLockableKey[] keys, int start, int count, TimeSpan timeout, CancellationToken cancellationToken) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : 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, @@ -158,7 +158,7 @@ internal static bool DoInternalTryLock(Tsavorite [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static bool DoInternalTryPromoteLock(TsavoriteSession tsavoriteSession, ClientSession clientSession, TLockableKey key, TimeSpan timeout, CancellationToken cancellationToken) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper where TLockableKey : ILockableKey { var startTime = DateTime.UtcNow; @@ -234,10 +234,10 @@ public void Lock(TLockableKey[] keys, int start, int count) bool lockAquired = false; while (!lockAquired) { - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - lockAquired = DoInternalLock(TsavoriteSession, clientSession, keys, start, count); + lockAquired = DoInternalLock(sessionFunctions, clientSession, keys, start, count); } finally { @@ -283,10 +283,10 @@ public bool TryLock(TLockableKey[] keys, int start, int count, Tim clientSession.CheckIsAcquiredLockable(); Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected(), "Trying to protect an already-protected epoch for LockableUnsafeContext.Lock()"); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return DoInternalTryLock(TsavoriteSession, clientSession, keys, start, count, timeout, cancellationToken); + return DoInternalTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); } finally { @@ -316,10 +316,10 @@ public bool TryPromoteLock(TLockableKey key, TimeSpan timeout, Can clientSession.CheckIsAcquiredLockable(); Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected(), "Trying to protect an already-protected epoch for LockableUnsafeContext.Lock()"); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return DoInternalTryPromoteLock(TsavoriteSession, clientSession, key, timeout, cancellationToken); + return DoInternalTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); } finally { @@ -337,7 +337,7 @@ public void Unlock(TLockableKey[] keys, int start, int count) clientSession.CheckIsAcquiredLockable(); Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected(), "Trying to protect an already-protected epoch for LockableUnsafeContext.Unlock()"); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { DoInternalUnlock(clientSession, keys, start, start + count - 1); @@ -367,10 +367,10 @@ public void Unlock(TLockableKey[] keys, int start, int count) public bool CompletePending(bool wait = false, bool spinWaitForCommit = false) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.UnsafeCompletePending(TsavoriteSession, false, wait, spinWaitForCommit); + return clientSession.UnsafeCompletePending(sessionFunctions, false, wait, spinWaitForCommit); } finally { @@ -382,10 +382,10 @@ public bool CompletePending(bool wait = false, bool spinWaitForCommit = false) public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.UnsafeCompletePendingWithOutputs(TsavoriteSession, out completedOutputs, wait, spinWaitForCommit); + return clientSession.UnsafeCompletePendingWithOutputs(sessionFunctions, out completedOutputs, wait, spinWaitForCommit); } finally { @@ -395,21 +395,21 @@ public bool CompletePendingWithOutputs(out CompletedOutputIterator public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingAsync(waitForCommit, token); + => clientSession.CompletePendingAsync(sessionFunctions, waitForCommit, token); /// public ValueTask> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingWithOutputsAsync(waitForCommit, token); + => clientSession.CompletePendingWithOutputsAsync(sessionFunctions, waitForCommit, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, sessionFunctions); } finally { @@ -495,10 +495,10 @@ public Status Read(Key key, out Output output, ref ReadOptions readOptions, Cont public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } finally { @@ -511,10 +511,10 @@ public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOpti public Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } finally { @@ -527,10 +527,10 @@ public Status ReadAtAddress(long address, ref Input input, ref Output output, re public Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } finally { @@ -551,7 +551,7 @@ public ValueTask.ReadAsyncResult public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); } /// @@ -581,7 +581,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Input input = default; - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, token); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, token); } /// @@ -601,7 +601,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Key key = default; - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); } /// @@ -610,7 +610,7 @@ public ValueTask.ReadAsyncResult Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); } /// @@ -646,10 +646,10 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, sessionFunctions); } finally { @@ -672,10 +672,10 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, sessionFunctions); } finally { @@ -732,7 +732,8 @@ public ValueTask.UpsertAsyncResult.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.UpsertAsync(TsavoriteSession, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + return clientSession.store.UpsertAsync>> + (sessionFunctions, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); } /// @@ -780,10 +781,10 @@ public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOption private Status RMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, sessionFunctions); } finally { @@ -852,7 +853,8 @@ public ValueTask.RmwAsyncResult> public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.RmwAsync(TsavoriteSession, ref key, ref input, ref rmwOptions, context, token); + return clientSession.store.RmwAsync>>( + sessionFunctions, ref key, ref input, ref rmwOptions, context, token); } /// @@ -880,10 +882,11 @@ public Status Delete(ref Key key, ref DeleteOptions deleteOptions, Context userC private Status Delete(ref Key key, long keyHash, Context userContext = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - return clientSession.store.ContextDelete(ref key, keyHash, userContext, TsavoriteSession); + return clientSession.store.ContextDelete>>( + ref key, keyHash, userContext, sessionFunctions); } finally { @@ -914,7 +917,8 @@ public ValueTask.DeleteAsyncResult.DeleteAsyncResult> DeleteAsync(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.DeleteAsync(TsavoriteSession, ref key, ref deleteOptions, userContext, token); + return clientSession.store.DeleteAsync>>( + sessionFunctions, ref key, ref deleteOptions, userContext, token); } /// @@ -930,21 +934,21 @@ public ValueTask.DeleteAsyncResult [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetModified(ref Key key) - => clientSession.ResetModified(ref key); + => clientSession.ResetModified(sessionFunctions, ref key); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsModified(Key key) - => clientSession.IsModified(ref key); + => clientSession.IsModified(sessionFunctions, ref key); /// public void Refresh() { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - clientSession.UnsafeResumeThread(); + clientSession.UnsafeResumeThread(sessionFunctions); try { - clientSession.store.InternalRefresh(TsavoriteSession); + clientSession.store.InternalRefresh>>(sessionFunctions); } finally { @@ -954,213 +958,5 @@ public void Refresh() #endregion ITsavoriteContext - #region ITsavoriteSession - - // This is a struct to allow JIT to inline calls (and bypass default interface call mechanism) - internal readonly struct InternalTsavoriteSession : ITsavoriteSession - { - private readonly ClientSession _clientSession; - - public InternalTsavoriteSession(ClientSession clientSession) - { - _clientSession = clientSession; - } - - public bool IsManualLocking => true; - public TsavoriteKV Store => _clientSession.store; - - #region IFunctions - Reads - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo) - => _clientSession.functions.SingleReader(ref key, ref input, ref value, ref dst, ref readInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) - => _clientSession.functions.ConcurrentReader(ref key, ref input, ref value, ref dst, ref readInfo, ref recordInfo); - - public void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) - => _clientSession.functions.ReadCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); - - #endregion IFunctions - Reads - - #region IFunctions - Upserts - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public 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) - => _clientSession.functions.SingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentWriter(long physicalAddress, ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo) - { - (upsertInfo.UsedValueLength, upsertInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref dst, ref recordInfo); - if (!_clientSession.functions.ConcurrentWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, ref recordInfo)) - return false; - _clientSession.store.SetExtraValueLength(ref dst, ref recordInfo, upsertInfo.UsedValueLength, upsertInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion IFunctions - Upserts - - #region IFunctions - RMWs - #region InitialUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NeedInitialUpdate(ref Key key, ref Input input, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.NeedInitialUpdate(ref key, ref input, ref output, ref rmwInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - => _clientSession.functions.InitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); - } - #endregion InitialUpdater - - #region CopyUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.NeedCopyUpdate(ref key, ref input, ref oldValue, ref output, ref rmwInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - => _clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); - } - #endregion CopyUpdater - - #region InPlaceUpdater - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool InPlaceUpdater(long physicalAddress, ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, out OperationStatus status, ref RecordInfo recordInfo) - { - (rmwInfo.UsedValueLength, rmwInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); - if (!_clientSession.InPlaceUpdater(ref key, ref input, ref value, ref output, ref recordInfo, ref rmwInfo, out status)) - return false; - _clientSession.store.SetExtraValueLength(ref value, ref recordInfo, rmwInfo.UsedValueLength, rmwInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion InPlaceUpdater - - public void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) - => _clientSession.functions.RMWCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); - - #endregion IFunctions - RMWs - - #region IFunctions - Deletes - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) - => _clientSession.functions.SingleDeleter(ref key, ref value, ref deleteInfo, ref recordInfo); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void PostSingleDeleter(ref Key key, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) - { - recordInfo.SetDirtyAndModified(); - _clientSession.functions.PostSingleDeleter(ref key, ref deleteInfo); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool ConcurrentDeleter(long physicalAddress, ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo, out int allocatedSize) - { - (deleteInfo.UsedValueLength, deleteInfo.FullValueLength, allocatedSize) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); - if (!_clientSession.functions.ConcurrentDeleter(ref key, ref value, ref deleteInfo, ref recordInfo)) - return false; - _clientSession.store.SetTombstoneAndExtraValueLength(ref value, ref recordInfo, deleteInfo.UsedValueLength, deleteInfo.FullValueLength); - recordInfo.SetDirtyAndModified(); - return true; - } - #endregion IFunctions - Deletes - - #region IFunctions - Dispose - public void DisposeSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason) - => _clientSession.functions.DisposeSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); - public void DisposeCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.DisposeCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); - public void DisposeInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo) - => _clientSession.functions.DisposeInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); - public void DisposeSingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo) - => _clientSession.functions.DisposeSingleDeleter(ref key, ref value, ref deleteInfo); - public void DisposeDeserializedFromDisk(ref Key key, ref Value value, ref RecordInfo recordInfo) - => _clientSession.functions.DisposeDeserializedFromDisk(ref key, ref value); - public void DisposeForRevivification(ref Key key, ref Value value, int newKeySize, ref RecordInfo recordInfo) - => _clientSession.functions.DisposeForRevivification(ref key, ref value, newKeySize); - #endregion IFunctions - Dispose - - #region Transient locking - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) - { - Debug.Assert(Store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei), - $"Attempting to use a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {_clientSession.store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {_clientSession.store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx) - { - Debug.Assert(Store.LockTable.IsLocked(ref key, ref stackCtx.hei), - $"Attempting to use a non-Locked (S or X) key in a Lockable context (requesting SLock):" - + $" XLocked {_clientSession.store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {_clientSession.store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) - { - Debug.Assert(Store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei), - $"Attempting to unlock a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {_clientSession.store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {_clientSession.store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx) - { - Debug.Assert(Store.LockTable.IsLockedShared(ref key, ref stackCtx.hei), - $"Attempting to use a non-XLocked key in a Lockable context (requesting XLock):" - + $" XLocked {_clientSession.store.LockTable.IsLockedExclusive(ref key, ref stackCtx.hei)}," - + $" Slocked {_clientSession.store.LockTable.IsLockedShared(ref key, ref stackCtx.hei)}"); - } - #endregion - - #region Internal utilities - public int GetRMWInitialValueLength(ref Input input) => _clientSession.functions.GetRMWInitialValueLength(ref input); - - public int GetRMWModifiedValueLength(ref Value value, ref Input input) => _clientSession.functions.GetRMWModifiedValueLength(ref value, ref input); - - public IHeapContainer GetHeapContainer(ref Input input) - { - if (typeof(Input) == typeof(SpanByte)) - return new SpanByteHeapContainer(ref Unsafe.As(ref input), _clientSession.store.hlog.bufferPool) as IHeapContainer; - return new StandardHeapContainer(ref input); - } - - public void UnsafeResumeThread() => _clientSession.UnsafeResumeThread(); - - public void UnsafeSuspendThread() => _clientSession.UnsafeSuspendThread(); - - public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) - => _clientSession.CompletePendingWithOutputs(out completedOutputs, wait, spinWaitForCommit); - - public TsavoriteKV.TsavoriteExecutionContext Ctx => _clientSession.ctx; - #endregion Internal utilities - } - #endregion ITsavoriteSession } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs index e137445602..71faacf971 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/LockableUnsafeContext.cs @@ -13,10 +13,10 @@ namespace Tsavorite.core /// Tsavorite Context implementation that allows manual control of record locking and epoch management. For advanced use only. /// public readonly struct LockableUnsafeContext : ITsavoriteContext, ILockableContext, IUnsafeContext - where Functions : IFunctions + where Functions : ISessionFunctions { readonly ClientSession clientSession; - internal readonly LockableContext.InternalTsavoriteSession TsavoriteSession; + readonly SessionFunctionsWrapper> sessionFunctions; /// Indicates whether this struct has been initialized public bool IsNull => clientSession is null; @@ -24,14 +24,14 @@ namespace Tsavorite.core internal LockableUnsafeContext(ClientSession clientSession) { this.clientSession = clientSession; - TsavoriteSession = new LockableContext.InternalTsavoriteSession(clientSession); + sessionFunctions = new(clientSession); } #region Begin/EndUnsafe /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void BeginUnsafe() => clientSession.UnsafeResumeThread(); + public void BeginUnsafe() => clientSession.UnsafeResumeThread(sessionFunctions); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -42,7 +42,7 @@ internal LockableUnsafeContext(ClientSession - public void BeginLockable() => clientSession.AcquireLockable(); + public void BeginLockable() => clientSession.AcquireLockable(sessionFunctions); /// public void EndLockable() => clientSession.ReleaseLockable(); @@ -76,7 +76,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(TsavoriteSession, clientSession, keys, start, count)) + if (LockableContext.DoInternalLock(sessionFunctions, clientSession, keys, start, count)) { break; } @@ -123,7 +123,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(TsavoriteSession, clientSession, keys, start, count, timeout, cancellationToken); + return LockableContext.DoInternalTryLock(sessionFunctions, clientSession, keys, start, count, timeout, cancellationToken); } /// @@ -148,7 +148,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(TsavoriteSession, clientSession, key, timeout, cancellationToken); + return LockableContext.DoInternalTryPromoteLock(sessionFunctions, clientSession, key, timeout, cancellationToken); } /// @@ -183,30 +183,30 @@ public void Unlock(TLockableKey[] keys, int start, int count) public bool CompletePending(bool wait = false, bool spinWaitForCommit = false) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.UnsafeCompletePending(TsavoriteSession, false, wait, spinWaitForCommit); + return clientSession.UnsafeCompletePending(sessionFunctions, false, wait, spinWaitForCommit); } /// public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.UnsafeCompletePendingWithOutputs(TsavoriteSession, out completedOutputs, wait, spinWaitForCommit); + return clientSession.UnsafeCompletePendingWithOutputs(sessionFunctions, out completedOutputs, wait, spinWaitForCommit); } /// public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingAsync(waitForCommit, token); + => clientSession.CompletePendingAsync(sessionFunctions, waitForCommit, token); /// public ValueTask> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingWithOutputsAsync(waitForCommit, token); + => clientSession.CompletePendingWithOutputsAsync(sessionFunctions, waitForCommit, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, sessionFunctions); } /// @@ -214,7 +214,7 @@ public Status Read(ref Key key, ref Input input, ref Output output, Context user public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out _, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out _, userContext, sessionFunctions); } /// @@ -290,7 +290,7 @@ public Status Read(Key key, out Output output, ref ReadOptions readOptions, Cont public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -298,7 +298,7 @@ public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOpti public Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -306,7 +306,7 @@ public Status ReadAtAddress(long address, ref Input input, ref Output output, re public Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -322,7 +322,7 @@ public ValueTask.ReadAsyncResult public ValueTask.ReadAsyncResult> ReadAsync(ref Key key, ref Input input, ref ReadOptions readOptions, Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); } /// @@ -335,7 +335,7 @@ public ValueTask.ReadAsyncResult public ValueTask.ReadAsyncResult> ReadAsync(Key key, Input input, ref ReadOptions readOptions, Context context = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, context, token); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, context, token); } /// @@ -352,7 +352,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Input input = default; - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, token); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, token); } /// @@ -372,7 +372,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Key key = default; - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); } /// @@ -381,7 +381,7 @@ public ValueTask.ReadAsyncResult Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); } /// @@ -417,7 +417,7 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, sessionFunctions); } /// @@ -435,7 +435,7 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O public Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, sessionFunctions); } /// @@ -487,8 +487,8 @@ public ValueTask.UpsertAsyncResult.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.UpsertAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + return clientSession.store.UpsertAsync>>( + sessionFunctions, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); } /// @@ -536,7 +536,7 @@ public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOption public Status RMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, sessionFunctions); } /// @@ -600,8 +600,8 @@ public ValueTask.RmwAsyncResult> public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.RmwAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref input, ref rmwOptions, context, token); + return clientSession.store.RmwAsync>>( + sessionFunctions, ref key, ref input, ref rmwOptions, context, token); } /// @@ -630,8 +630,8 @@ public Status Delete(ref Key key, ref DeleteOptions deleteOptions, Context userC public Status Delete(ref Key key, long keyHash, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextDelete.InternalTsavoriteSession>( - ref key, keyHash, userContext, TsavoriteSession); + return clientSession.store.ContextDelete>>( + ref key, keyHash, userContext, sessionFunctions); } /// @@ -657,8 +657,8 @@ public ValueTask.DeleteAsyncResult.DeleteAsyncResult> DeleteAsync(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.DeleteAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref deleteOptions, userContext, token); + return clientSession.store.DeleteAsync>>( + sessionFunctions, ref key, ref deleteOptions, userContext, token); } /// @@ -674,18 +674,18 @@ public ValueTask.DeleteAsyncResult [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetModified(ref Key key) - => clientSession.UnsafeResetModified(ref key); + => clientSession.UnsafeResetModified(sessionFunctions, ref key); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsModified(Key key) - => clientSession.UnsafeIsModified(ref key); + => clientSession.UnsafeIsModified(sessionFunctions, ref key); /// public void Refresh() { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - clientSession.store.InternalRefresh.InternalTsavoriteSession>(TsavoriteSession); + clientSession.store.InternalRefresh>>(sessionFunctions); } #endregion ITsavoriteContext diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs new file mode 100644 index 0000000000..77fdff01e0 --- /dev/null +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/SessionFunctionsWrapper.cs @@ -0,0 +1,196 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Runtime.CompilerServices; + +namespace Tsavorite.core +{ + internal readonly struct SessionFunctionsWrapper : ISessionFunctionsWrapper + where Functions : ISessionFunctions + where TSessionLocker : struct, ISessionLocker + { + private readonly ClientSession _clientSession; + private readonly TSessionLocker _sessionLocker; // Has no data members + + public SessionFunctionsWrapper(ClientSession clientSession) + { + _clientSession = clientSession; + _sessionLocker = new TSessionLocker(); + } + + public TsavoriteKV Store => _clientSession.store; + public OverflowBucketLockTable LockTable => _clientSession.store.LockTable; + + #region Reads + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo) + => _clientSession.functions.SingleReader(ref key, ref input, ref value, ref dst, ref readInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) + => _clientSession.functions.ConcurrentReader(ref key, ref input, ref value, ref dst, ref readInfo, ref recordInfo); + + public void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) + => _clientSession.functions.ReadCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); + + #endregion Reads + + #region Upserts + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public 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) + => _clientSession.functions.SingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason, ref recordInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PostSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) + { + recordInfo.SetDirtyAndModified(); + _clientSession.functions.PostSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ConcurrentWriter(long physicalAddress, ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo) + { + (upsertInfo.UsedValueLength, upsertInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref dst, ref recordInfo); + if (!_clientSession.functions.ConcurrentWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, ref recordInfo)) + return false; + _clientSession.store.SetExtraValueLength(ref dst, ref recordInfo, upsertInfo.UsedValueLength, upsertInfo.FullValueLength); + recordInfo.SetDirtyAndModified(); + return true; + } + #endregion Upserts + + #region RMWs + #region InitialUpdater + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool NeedInitialUpdate(ref Key key, ref Input input, ref Output output, ref RMWInfo rmwInfo) + => _clientSession.functions.NeedInitialUpdate(ref key, ref input, ref output, ref rmwInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) + => _clientSession.functions.InitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo, ref recordInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PostInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) + { + recordInfo.SetDirtyAndModified(); + _clientSession.functions.PostInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); + } + #endregion InitialUpdater + + #region CopyUpdater + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue, ref Output output, ref RMWInfo rmwInfo) + => _clientSession.functions.NeedCopyUpdate(ref key, ref input, ref oldValue, ref output, ref rmwInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) + => _clientSession.functions.CopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo, ref recordInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PostCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) + { + recordInfo.SetDirtyAndModified(); + _clientSession.functions.PostCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); + } + #endregion CopyUpdater + + #region InPlaceUpdater + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool InPlaceUpdater(long physicalAddress, ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, out OperationStatus status, ref RecordInfo recordInfo) + { + (rmwInfo.UsedValueLength, rmwInfo.FullValueLength, _) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); + if (!_clientSession.InPlaceUpdater(this, ref key, ref input, ref value, ref output, ref recordInfo, ref rmwInfo, out status)) + return false; + _clientSession.store.SetExtraValueLength(ref value, ref recordInfo, rmwInfo.UsedValueLength, rmwInfo.FullValueLength); + recordInfo.SetDirtyAndModified(); + return true; + } + #endregion InPlaceUpdater + + public void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata) + => _clientSession.functions.RMWCompletionCallback(ref key, ref input, ref output, ctx, status, recordMetadata); + + #endregion RMWs + + #region Deletes + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) + => _clientSession.functions.SingleDeleter(ref key, ref value, ref deleteInfo, ref recordInfo); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void PostSingleDeleter(ref Key key, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo) + { + recordInfo.SetDirtyAndModified(); + _clientSession.functions.PostSingleDeleter(ref key, ref deleteInfo); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool ConcurrentDeleter(long physicalAddress, ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo, out int allocatedSize) + { + (deleteInfo.UsedValueLength, deleteInfo.FullValueLength, allocatedSize) = _clientSession.store.GetRecordLengths(physicalAddress, ref value, ref recordInfo); + if (!_clientSession.functions.ConcurrentDeleter(ref key, ref value, ref deleteInfo, ref recordInfo)) + return false; + _clientSession.store.SetTombstoneAndExtraValueLength(ref value, ref recordInfo, deleteInfo.UsedValueLength, deleteInfo.FullValueLength); + recordInfo.SetDirtyAndModified(); + return true; + } + #endregion Deletes + + #region Dispose + public void DisposeSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason) + => _clientSession.functions.DisposeSingleWriter(ref key, ref input, ref src, ref dst, ref output, ref upsertInfo, reason); + public void DisposeCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo) + => _clientSession.functions.DisposeCopyUpdater(ref key, ref input, ref oldValue, ref newValue, ref output, ref rmwInfo); + public void DisposeInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo) + => _clientSession.functions.DisposeInitialUpdater(ref key, ref input, ref value, ref output, ref rmwInfo); + public void DisposeSingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo) + => _clientSession.functions.DisposeSingleDeleter(ref key, ref value, ref deleteInfo); + public void DisposeDeserializedFromDisk(ref Key key, ref Value value, ref RecordInfo recordInfo) + => _clientSession.functions.DisposeDeserializedFromDisk(ref key, ref value); + public void DisposeForRevivification(ref Key key, ref Value value, int newKeySize, ref RecordInfo recordInfo) + => _clientSession.functions.DisposeForRevivification(ref key, ref value, newKeySize); + #endregion Dispose + + #region Transient locking + public bool IsManualLocking => _sessionLocker.IsManualLocking; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) => + _sessionLocker.TryLockTransientExclusive(Store, ref key, ref stackCtx); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx) + => _sessionLocker.TryLockTransientShared(Store, ref key, ref stackCtx); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx) + => _sessionLocker.UnlockTransientExclusive(Store, ref key, ref stackCtx); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx) + => _sessionLocker.UnlockTransientShared(Store, ref key, ref stackCtx); + #endregion Transient locking + + #region Internal utilities + public int GetRMWInitialValueLength(ref Input input) => _clientSession.functions.GetRMWInitialValueLength(ref input); + + public int GetRMWModifiedValueLength(ref Value t, ref Input input) => _clientSession.functions.GetRMWModifiedValueLength(ref t, ref input); + + public IHeapContainer GetHeapContainer(ref Input input) + { + if (typeof(Input) == typeof(SpanByte)) + return new SpanByteHeapContainer(ref Unsafe.As(ref input), _clientSession.store.hlog.bufferPool) as IHeapContainer; + return new StandardHeapContainer(ref input); + } + + public void UnsafeResumeThread() => _clientSession.UnsafeResumeThread(this); + + public void UnsafeSuspendThread() => _clientSession.UnsafeSuspendThread(); + + public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) + => _clientSession.CompletePendingWithOutputs(this, out completedOutputs, wait, spinWaitForCommit); + + public TsavoriteKV.TsavoriteExecutionContext Ctx => _clientSession.ctx; + #endregion Internal utilities + } +} diff --git a/libs/storage/Tsavorite/cs/src/core/ClientSession/UnsafeContext.cs b/libs/storage/Tsavorite/cs/src/core/ClientSession/UnsafeContext.cs index 9f8a2319db..e5800be6ec 100644 --- a/libs/storage/Tsavorite/cs/src/core/ClientSession/UnsafeContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/ClientSession/UnsafeContext.cs @@ -12,10 +12,10 @@ namespace Tsavorite.core /// Tsavorite Operations implementation that allows manual control of record epoch management. For advanced use only. /// public readonly struct UnsafeContext : ITsavoriteContext, IUnsafeContext - where Functions : IFunctions + where Functions : ISessionFunctions { readonly ClientSession clientSession; - internal readonly ClientSession.InternalTsavoriteSession TsavoriteSession; + internal readonly SessionFunctionsWrapper> sessionFunctions; /// Indicates whether this struct has been initialized public bool IsNull => clientSession is null; @@ -23,14 +23,14 @@ namespace Tsavorite.core internal UnsafeContext(ClientSession clientSession) { this.clientSession = clientSession; - TsavoriteSession = new ClientSession.InternalTsavoriteSession(clientSession); + sessionFunctions = new(clientSession); } #region Begin/EndUnsafe /// [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void BeginUnsafe() => clientSession.UnsafeResumeThread(); + public void BeginUnsafe() => clientSession.UnsafeResumeThread(sessionFunctions); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -50,30 +50,30 @@ internal UnsafeContext(ClientSession public bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.UnsafeCompletePendingWithOutputs(TsavoriteSession, out completedOutputs, wait, spinWaitForCommit); + return clientSession.UnsafeCompletePendingWithOutputs(sessionFunctions, out completedOutputs, wait, spinWaitForCommit); } /// public ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingAsync(waitForCommit, token); + => clientSession.CompletePendingAsync(sessionFunctions, waitForCommit, token); /// public ValueTask> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default) - => clientSession.CompletePendingWithOutputsAsync(waitForCommit, token); + => clientSession.CompletePendingWithOutputsAsync(sessionFunctions, waitForCommit, token); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Status Read(ref Key key, ref Input input, ref Output output, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, userContext, sessionFunctions); } /// @@ -81,7 +81,7 @@ public Status Read(ref Key key, ref Input input, ref Output output, Context user public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out _, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out _, userContext, sessionFunctions); } /// @@ -157,7 +157,7 @@ public Status Read(Key key, out Output output, ref ReadOptions readOptions, Cont public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRead(ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -165,7 +165,7 @@ public Status Read(ref Key key, ref Input input, ref Output output, ref ReadOpti public Status ReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -173,7 +173,7 @@ public Status ReadAtAddress(long address, ref Input input, ref Output output, re public Status ReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, userContext, sessionFunctions); } /// @@ -182,7 +182,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); ReadOptions readOptions = default; - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); } /// @@ -209,7 +209,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Input input = default; - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, token); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, token); } /// @@ -228,7 +228,7 @@ public ValueTask.ReadAsyncResult Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAsync(TsavoriteSession, ref key, ref input, ref readOptions, userContext, cancellationToken); + return clientSession.store.ReadAsync(sessionFunctions, ref key, ref input, ref readOptions, userContext, cancellationToken); } /// @@ -238,7 +238,7 @@ public ValueTask.ReadAsyncResult { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); Key key = default; - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: true); } /// @@ -247,7 +247,7 @@ public ValueTask.ReadAsyncResult Context userContext = default, CancellationToken cancellationToken = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ReadAtAddressAsync(TsavoriteSession, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); + return clientSession.store.ReadAtAddressAsync(sessionFunctions, address, ref key, ref input, ref readOptions, userContext, cancellationToken, noKey: false); } /// @@ -283,7 +283,7 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, userContext, sessionFunctions); } /// @@ -301,7 +301,7 @@ public Status Upsert(ref Key key, ref Input input, ref Value desiredValue, ref O private Status Upsert(ref Key key, long keyHash, ref Input input, ref Value desiredValue, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextUpsert(ref key, keyHash, ref input, ref desiredValue, ref output, out recordMetadata, userContext, sessionFunctions); } /// @@ -353,8 +353,8 @@ public ValueTask.UpsertAsyncResult.UpsertAsyncResult> UpsertAsync(ref Key key, ref Input input, ref Value desiredValue, ref UpsertOptions upsertOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.UpsertAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); + return clientSession.store.UpsertAsync>>( + sessionFunctions, ref key, ref input, ref desiredValue, ref upsertOptions, userContext, token); } /// @@ -402,7 +402,7 @@ public Status RMW(ref Key key, ref Input input, ref Output output, ref RMWOption public Status RMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, TsavoriteSession); + return clientSession.store.ContextRMW(ref key, keyHash, ref input, ref output, out recordMetadata, userContext, sessionFunctions); } /// @@ -463,8 +463,8 @@ public ValueTask.RmwAsyncResult> public ValueTask.RmwAsyncResult> RMWAsync(ref Key key, ref Input input, ref RMWOptions rmwOptions, Context context = default, CancellationToken token = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.RmwAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref input, ref rmwOptions, context, token); + return clientSession.store.RmwAsync>>( + sessionFunctions, ref key, ref input, ref rmwOptions, context, token); } /// @@ -493,8 +493,8 @@ public Status Delete(ref Key key, ref DeleteOptions deleteOptions, Context userC public Status Delete(ref Key key, long keyHash, Context userContext = default) { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.ContextDelete.InternalTsavoriteSession>( - ref key, keyHash, userContext, TsavoriteSession); + return clientSession.store.ContextDelete>>( + ref key, keyHash, userContext, sessionFunctions); } /// @@ -520,8 +520,8 @@ public ValueTask.DeleteAsyncResult.DeleteAsyncResult> DeleteAsync(ref Key key, ref DeleteOptions deleteOptions, Context userContext = default, CancellationToken token = default) { Debug.Assert(!clientSession.store.epoch.ThisInstanceProtected()); - return clientSession.store.DeleteAsync.InternalTsavoriteSession>( - TsavoriteSession, ref key, ref deleteOptions, userContext, token); + return clientSession.store.DeleteAsync>>( + sessionFunctions, ref key, ref deleteOptions, userContext, token); } /// @@ -537,18 +537,18 @@ public ValueTask.DeleteAsyncResult [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetModified(ref Key key) - => clientSession.UnsafeResetModified(ref key); + => clientSession.UnsafeResetModified(sessionFunctions, ref key); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool IsModified(Key key) - => clientSession.UnsafeIsModified(ref key); + => clientSession.UnsafeIsModified(sessionFunctions, ref key); /// public void Refresh() { Debug.Assert(clientSession.store.epoch.ThisInstanceProtected()); - clientSession.store.InternalRefresh.InternalTsavoriteSession>(TsavoriteSession); + clientSession.store.InternalRefresh>>(sessionFunctions); } #endregion ITsavoriteContext } diff --git a/libs/storage/Tsavorite/cs/src/core/Compaction/ICompactionFunctions.cs b/libs/storage/Tsavorite/cs/src/core/Compaction/ICompactionFunctions.cs index 0921c55a17..45acc69f5e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Compaction/ICompactionFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/Compaction/ICompactionFunctions.cs @@ -12,7 +12,7 @@ public interface ICompactionFunctions { /// /// Checks if record in the Tsavorite log is logically deleted. - /// If the record was deleted via + /// If the record was deleted via /// then this function is not called for such a record. /// /// diff --git a/libs/storage/Tsavorite/cs/src/core/Compaction/LogCompactionFunctions.cs b/libs/storage/Tsavorite/cs/src/core/Compaction/LogCompactionFunctions.cs index 53f7c485ff..b920977604 100644 --- a/libs/storage/Tsavorite/cs/src/core/Compaction/LogCompactionFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/Compaction/LogCompactionFunctions.cs @@ -3,8 +3,8 @@ namespace Tsavorite.core { - internal sealed class LogCompactionFunctions : IFunctions - where Functions : IFunctions + internal sealed class LogCompactionFunctions : ISessionFunctions + where Functions : ISessionFunctions { readonly Functions _functions; diff --git a/libs/storage/Tsavorite/cs/src/core/Compaction/TsavoriteCompaction.cs b/libs/storage/Tsavorite/cs/src/core/Compaction/TsavoriteCompaction.cs index 6395430b03..378aabb33a 100644 --- a/libs/storage/Tsavorite/cs/src/core/Compaction/TsavoriteCompaction.cs +++ b/libs/storage/Tsavorite/cs/src/core/Compaction/TsavoriteCompaction.cs @@ -20,7 +20,7 @@ public partial class TsavoriteKV : TsavoriteBase /// Compaction type (whether we lookup records or scan log for liveness checking) /// Address until which compaction was done internal long Compact(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress, CompactionType compactionType) - where Functions : IFunctions + where Functions : ISessionFunctions where CompactionFunctions : ICompactionFunctions { return compactionType switch @@ -32,7 +32,7 @@ internal long Compact(Fu } private long CompactLookup(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress) - where Functions : IFunctions + where Functions : ISessionFunctions where CompactionFunctions : ICompactionFunctions { if (untilAddress > hlog.SafeReadOnlyAddress) @@ -40,6 +40,7 @@ private long CompactLookup(functions); using var storeSession = NewSession>(lf); + var storebContext = storeSession.BasicContext; using (var iter1 = Log.Scan(Log.BeginAddress, untilAddress)) { @@ -51,10 +52,10 @@ private long CompactLookup 256) { - storeSession.CompletePending(wait: true); + storebContext.CompletePending(wait: true); numPending = 0; } } @@ -63,14 +64,14 @@ private long CompactLookup 0) - storeSession.CompletePending(wait: true); + storebContext.CompletePending(wait: true); } Log.ShiftBeginAddress(untilAddress, false); return untilAddress; } private long CompactScan(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress) - where Functions : IFunctions + where Functions : ISessionFunctions where CompactionFunctions : ICompactionFunctions { if (untilAddress > hlog.SafeReadOnlyAddress) @@ -80,10 +81,12 @@ private long CompactScan var lf = new LogCompactionFunctions(functions); using var storeSession = NewSession>(lf); + var storebContext = storeSession.BasicContext; using (var tempKv = new TsavoriteKV(IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice() }, comparer: Comparer, loggerFactory: loggerFactory)) using (var tempKvSession = tempKv.NewSession(functions)) { + var tempbContext = tempKvSession.BasicContext; using (var iter1 = Log.Scan(hlog.BeginAddress, untilAddress)) { while (iter1.GetNext(out var recordInfo)) @@ -92,9 +95,9 @@ private long CompactScan ref var value = ref iter1.GetValue(); if (recordInfo.Tombstone || cf.IsDeleted(ref key, ref value)) - tempKvSession.Delete(ref key); + tempbContext.Delete(ref key); else - tempKvSession.Upsert(ref key, ref value); + tempbContext.Upsert(ref key, ref value); } // Ensure address is at record boundary untilAddress = originalUntilAddress = iter1.NextAddress; @@ -103,7 +106,7 @@ private long CompactScan // Scan until SafeReadOnlyAddress var scanUntil = hlog.SafeReadOnlyAddress; if (untilAddress < scanUntil) - ScanImmutableTailToRemoveFromTempKv(ref untilAddress, scanUntil, tempKvSession); + ScanImmutableTailToRemoveFromTempKv(ref untilAddress, scanUntil, tempbContext); var numPending = 0; using var iter3 = tempKv.Log.Scan(tempKv.Log.BeginAddress, tempKv.Log.TailAddress); @@ -115,35 +118,35 @@ private long CompactScan // Try to ensure we have checked all immutable records scanUntil = hlog.SafeReadOnlyAddress; if (untilAddress < scanUntil) - ScanImmutableTailToRemoveFromTempKv(ref untilAddress, scanUntil, tempKvSession); + ScanImmutableTailToRemoveFromTempKv(ref untilAddress, scanUntil, tempbContext); // If record is not the latest in tempKv's memory for this key, ignore it (will not be returned if deleted) - if (!tempKvSession.ContainsKeyInMemory(ref iter3.GetKey(), out long tempKeyAddress).Found || iter3.CurrentAddress != tempKeyAddress) + if (!tempbContext.ContainsKeyInMemory(ref iter3.GetKey(), out long tempKeyAddress).Found || iter3.CurrentAddress != tempKeyAddress) continue; // As long as there's no record of the same key whose address is >= untilAddress (scan boundary), we are safe to copy the old record // to the tail. We don't know the actualAddress of the key in the main kv, but we it will not be below untilAddress. - var status = storeSession.CompactionCopyToTail(ref iter3.GetKey(), ref input, ref iter3.GetValue(), ref output, untilAddress - 1); + var status = storebContext.CompactionCopyToTail(ref iter3.GetKey(), ref input, ref iter3.GetValue(), ref output, untilAddress - 1); if (status.IsPending && ++numPending > 256) { - storeSession.CompletePending(wait: true); + storebContext.CompletePending(wait: true); numPending = 0; } } if (numPending > 0) - storeSession.CompletePending(wait: true); + storebContext.CompletePending(wait: true); } Log.ShiftBeginAddress(originalUntilAddress, false); return originalUntilAddress; } - private void ScanImmutableTailToRemoveFromTempKv(ref long untilAddress, long scanUntil, ClientSession tempKvSession) - where Functions : IFunctions + private void ScanImmutableTailToRemoveFromTempKv(ref long untilAddress, long scanUntil, BasicContext tempbContext) + where Functions : ISessionFunctions { using var iter = Log.Scan(untilAddress, scanUntil); while (iter.GetNext(out var _)) { - tempKvSession.Delete(ref iter.GetKey(), default); + tempbContext.Delete(ref iter.GetKey(), default); untilAddress = iter.NextAddress; } } diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/CallbackInfos.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/CallbackInfos.cs index 237138e3c3..8a57f874b3 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/CallbackInfos.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/CallbackInfos.cs @@ -8,7 +8,7 @@ namespace Tsavorite.core { /// - /// What actions to take following the RMW IFunctions method call, such as cancellation or record expiration. + /// What actions to take following the RMW ISessionFunctions method call, such as cancellation or record expiration. /// public enum UpsertAction { @@ -24,7 +24,7 @@ public enum UpsertAction } /// - /// Information passed to record-update callbacks. + /// Information passed to record-update callbacks. /// public struct UpsertInfo { @@ -67,7 +67,7 @@ public struct UpsertInfo public int FullValueLength { get; internal set; } /// - /// What actions Tsavorite should perform on a false return from the IFunctions method + /// What actions Tsavorite should perform on a false return from the ISessionFunctions method /// public UpsertAction Action { get; set; } @@ -144,7 +144,7 @@ internal static unsafe void StaticSetUsedValueLength(ref RecordInfo reco } /// - /// What actions to take following the RMW IFunctions method call, such as cancellation or record expiration. + /// What actions to take following the RMW ISessionFunctions method call, such as cancellation or record expiration. /// public enum RMWAction { @@ -170,7 +170,7 @@ public enum RMWAction } /// - /// Information passed to record-update callbacks. + /// Information passed to record-update callbacks. /// public struct RMWInfo { @@ -220,7 +220,7 @@ public struct RMWInfo public bool PreserveCopyUpdaterSourceRecord { get; set; } /// - /// What actions Tsavorite should perform on a false return from the IFunctions method + /// What actions Tsavorite should perform on a false return from the ISessionFunctions method /// public RMWAction Action { get; set; } @@ -254,7 +254,7 @@ public unsafe void SetUsedValueLength(ref RecordInfo recordInfo, ref TVa } /// - /// What actions to take following the RMW IFunctions method call, such as cancellation or record expiration. + /// What actions to take following the RMW ISessionFunctions method call, such as cancellation or record expiration. /// public enum DeleteAction { @@ -269,7 +269,7 @@ public enum DeleteAction CancelOperation } /// - /// Information passed to record-update callbacks. + /// Information passed to record-update callbacks. /// public struct DeleteInfo { @@ -312,13 +312,13 @@ public struct DeleteInfo public int FullValueLength { get; internal set; } /// - /// What actions Tsavorite should perform on a false return from the IFunctions method + /// What actions Tsavorite should perform on a false return from the ISessionFunctions method /// public DeleteAction Action { get; set; } } /// - /// What actions to take following the RMW IFunctions method call, such as cancellation or record expiration. + /// What actions to take following the RMW ISessionFunctions method call, such as cancellation or record expiration. /// public enum ReadAction { @@ -339,7 +339,7 @@ public enum ReadAction } /// - /// Information passed to record-read callbacks. + /// Information passed to record-read callbacks. /// public struct ReadInfo { @@ -366,7 +366,7 @@ public struct ReadInfo public bool IsFromPending { get; internal set; } /// - /// What actions Tsavorite should perform on a false return from the IFunctions method + /// What actions Tsavorite should perform on a false return from the ISessionFunctions method /// public ReadAction Action { get; set; } } diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/IFunctions.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs similarity index 98% rename from libs/storage/Tsavorite/cs/src/core/Index/Interfaces/IFunctions.cs rename to libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs index dd70c3cb2d..36064bab79 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/IFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctions.cs @@ -11,7 +11,7 @@ namespace Tsavorite.core /// /// /// - public interface IFunctions + public interface ISessionFunctions { #region Reads /// @@ -306,7 +306,7 @@ public interface IFunctions /// /// /// - public interface IFunctions : IFunctions + public interface ISessionFunctions : ISessionFunctions { } @@ -316,7 +316,7 @@ public interface IFunctions : IFunctions /// /// - public interface IFunctions : IFunctions + public interface ISessionFunctions : ISessionFunctions { } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctionsWrapper.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctionsWrapper.cs new file mode 100644 index 0000000000..ee779827f4 --- /dev/null +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionFunctionsWrapper.cs @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +namespace Tsavorite.core +{ + /// + /// Provides thread management and all callbacks. A wrapper for IFunctions and additional methods called by TsavoriteImpl; the wrapped + /// IFunctions methods provide additional parameters to support the wrapper functionality, then call through to the user implementations. + /// + internal interface ISessionFunctionsWrapper : ITsavoriteSession, IVariableLengthInput + { + bool IsManualLocking { get; } + TsavoriteKV Store { get; } + + #region Reads + bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo); + bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo); + void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata); + #endregion reads + + #region Upserts + 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); + void PostSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo); + bool ConcurrentWriter(long physicalAddress, ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo); + #endregion Upserts + + #region RMWs + #region InitialUpdater + bool NeedInitialUpdate(ref Key key, ref Input input, ref Output output, ref RMWInfo rmwInfo); + bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); + void PostInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rMWInfo, ref RecordInfo recordInfo); + #endregion InitialUpdater + + #region CopyUpdater + bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue, ref Output output, ref RMWInfo rmwInfo); + bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); + void PostCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); + #endregion CopyUpdater + + #region InPlaceUpdater + bool InPlaceUpdater(long physicalAddress, ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, out OperationStatus status, ref RecordInfo recordInfo); + #endregion InPlaceUpdater + + void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata); + #endregion RMWs + + #region Deletes + bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); + void PostSingleDeleter(ref Key key, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); + bool ConcurrentDeleter(long physicalAddress, ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo, out int fullRecordLength); + #endregion Deletes + + #region Disposal + void DisposeSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason); + void DisposeCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo); + void DisposeInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo); + void DisposeSingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo); + void DisposeDeserializedFromDisk(ref Key key, ref Value value, ref RecordInfo recordInfo); + void DisposeForRevivification(ref Key key, ref Value value, int newKeySize, ref RecordInfo recordInfo); + #endregion Disposal + + #region Transient locking + bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx); + bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx); + void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx); + void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx); + #endregion + + bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false); + + TsavoriteKV.TsavoriteExecutionContext Ctx { get; } + + IHeapContainer GetHeapContainer(ref Input input); + } +} \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs new file mode 100644 index 0000000000..e13ffe361f --- /dev/null +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ISessionLocker.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +using System.Diagnostics; + +namespace Tsavorite.core +{ + /// + /// Provides thread management and all callbacks. A wrapper for ISessionFunctions and additional methods called by TsavoriteImpl; the wrapped + /// ISessionFunctions methods provide additional parameters to support the wrapper functionality, then call through to the user implementations. + /// + 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); + } + + /// + /// Basic (non-lockable) sessions must do transient locking. + /// + /// + /// This struct contains no data fields; SessionFunctionsWrapper redirects with its ClientSession. + /// + internal struct BasicSessionLocker : ISessionLocker + { + public bool IsManualLocking => false; + + public bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + if (!store.LockTable.IsEnabled) + return true; + if (!store.LockTable.TryLockTransientExclusive(ref key, ref stackCtx.hei)) + return false; + stackCtx.recSrc.SetHasTransientXLock(); + return true; + } + + public bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + if (!store.LockTable.IsEnabled) + return true; + if (!store.LockTable.TryLockTransientShared(ref key, ref stackCtx.hei)) + return false; + stackCtx.recSrc.SetHasTransientSLock(); + return true; + } + + public void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + if (!store.LockTable.IsEnabled) + return; + store.LockTable.UnlockExclusive(ref key, ref stackCtx.hei); + stackCtx.recSrc.ClearHasTransientXLock(); + } + + public void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + if (!store.LockTable.IsEnabled) + return; + store.LockTable.UnlockShared(ref key, ref stackCtx.hei); + stackCtx.recSrc.ClearHasTransientSLock(); + } + } + + /// + /// Lockable sessions are manual locking and thus must have already locked the record prior to an operation on it, so assert that. + /// + internal struct LockableSessionLocker : ISessionLocker + { + public bool IsManualLocking => true; + + public bool TryLockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + Debug.Assert(store.LockTable.IsLockedExclusive(ref key, 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)}"); + return true; + } + + public bool TryLockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + Debug.Assert(store.LockTable.IsLocked(ref key, 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)}"); + return true; + } + + public void UnlockTransientExclusive(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + Debug.Assert(store.LockTable.IsLockedExclusive(ref key, 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)}"); + } + + public void UnlockTransientShared(TsavoriteKV store, ref TKey key, ref OperationStackContext stackCtx) + { + Debug.Assert(store.LockTable.IsLockedShared(ref key, 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)}"); + } + } +} \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ITsavoriteSession.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ITsavoriteSession.cs index 055602fc51..be8e8a201d 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ITsavoriteSession.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/ITsavoriteSession.cs @@ -13,74 +13,4 @@ internal interface ITsavoriteSession void UnsafeResumeThread(); void UnsafeSuspendThread(); } - - /// - /// Provides thread management and all callbacks. A wrapper for IFunctions and additional methods called by TsavoriteImpl; the wrapped - /// IFunctions methods provide additional parameters to support the wrapper functionality, then call through to the user implementations. - /// - internal interface ITsavoriteSession : ITsavoriteSession, IVariableLengthInput - { - bool IsManualLocking { get; } - TsavoriteKV Store { get; } - - #region Reads - bool SingleReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo); - bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo); - void ReadCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata); - #endregion reads - - #region Upserts - 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); - void PostSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo); - bool ConcurrentWriter(long physicalAddress, ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo); - #endregion Upserts - - #region RMWs - #region InitialUpdater - bool NeedInitialUpdate(ref Key key, ref Input input, ref Output output, ref RMWInfo rmwInfo); - bool InitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); - void PostInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rMWInfo, ref RecordInfo recordInfo); - #endregion InitialUpdater - - #region CopyUpdater - bool NeedCopyUpdate(ref Key key, ref Input input, ref Value oldValue, ref Output output, ref RMWInfo rmwInfo); - bool CopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); - void PostCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo); - #endregion CopyUpdater - - #region InPlaceUpdater - bool InPlaceUpdater(long physicalAddress, ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo, out OperationStatus status, ref RecordInfo recordInfo); - #endregion InPlaceUpdater - - void RMWCompletionCallback(ref Key key, ref Input input, ref Output output, Context ctx, Status status, RecordMetadata recordMetadata); - #endregion RMWs - - #region Deletes - bool SingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); - void PostSingleDeleter(ref Key key, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo); - bool ConcurrentDeleter(long physicalAddress, ref Key key, ref Value value, ref DeleteInfo deleteInfo, ref RecordInfo recordInfo, out int fullRecordLength); - #endregion Deletes - - #region Disposal - void DisposeSingleWriter(ref Key key, ref Input input, ref Value src, ref Value dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason); - void DisposeCopyUpdater(ref Key key, ref Input input, ref Value oldValue, ref Value newValue, ref Output output, ref RMWInfo rmwInfo); - void DisposeInitialUpdater(ref Key key, ref Input input, ref Value value, ref Output output, ref RMWInfo rmwInfo); - void DisposeSingleDeleter(ref Key key, ref Value value, ref DeleteInfo deleteInfo); - void DisposeDeserializedFromDisk(ref Key key, ref Value value, ref RecordInfo recordInfo); - void DisposeForRevivification(ref Key key, ref Value value, int newKeySize, ref RecordInfo recordInfo); - #endregion Disposal - - #region Transient locking - bool TryLockTransientExclusive(ref Key key, ref OperationStackContext stackCtx); - bool TryLockTransientShared(ref Key key, ref OperationStackContext stackCtx); - void UnlockTransientExclusive(ref Key key, ref OperationStackContext stackCtx); - void UnlockTransientShared(ref Key key, ref OperationStackContext stackCtx); - #endregion - - bool CompletePendingWithOutputs(out CompletedOutputIterator completedOutputs, bool wait = false, bool spinWaitForCommit = false); - - TsavoriteKV.TsavoriteExecutionContext Ctx { get; } - - IHeapContainer GetHeapContainer(ref Input input); - } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/FunctionsBase.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/SessionFunctionsBase.cs similarity index 91% rename from libs/storage/Tsavorite/cs/src/core/Index/Interfaces/FunctionsBase.cs rename to libs/storage/Tsavorite/cs/src/core/Index/Interfaces/SessionFunctionsBase.cs index fb3ea228a6..68de20b7ff 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/FunctionsBase.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/SessionFunctionsBase.cs @@ -8,14 +8,14 @@ namespace Tsavorite.core { /// - /// Default empty functions base class to make it easy for users to provide their own implementation of IFunctions + /// Default empty functions base class to make it easy for users to provide their own implementation of ISessionFunctions /// /// /// /// /// /// - public abstract class FunctionsBase : IFunctions + public abstract class SessionFunctionsBase : ISessionFunctions { /// public virtual bool ConcurrentReader(ref Key key, ref Input input, ref Value value, ref Output dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) => true; @@ -78,11 +78,11 @@ public virtual void RMWCompletionCallback(ref Key key, ref Input input, ref Outp /// /// /// - public class SimpleFunctions : FunctionsBase + public class SimpleSessionFunctions : SessionFunctionsBase { private readonly Func merger; - public SimpleFunctions() => merger = (l, r) => l; - public SimpleFunctions(Func merger) => this.merger = merger; + public SimpleSessionFunctions() => merger = (l, r) => l; + public SimpleSessionFunctions(Func merger) => this.merger = merger; /// public override bool ConcurrentReader(ref Key key, ref Value input, ref Value value, ref Value dst, ref ReadInfo readInfo, ref RecordInfo recordInfo) @@ -122,9 +122,9 @@ public override bool ConcurrentWriter(ref Key key, ref Value input, ref Value sr public override bool InPlaceUpdater(ref Key key, ref Value input, ref Value value, ref Value output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { value = output = merger(input, value); return true; } } - public class SimpleFunctions : SimpleFunctions + public class SimpleSimpleFunctions : SimpleSessionFunctions { - public SimpleFunctions() : base() { } - public SimpleFunctions(Func merger) : base(merger) { } + public SimpleSimpleFunctions() : base() { } + public SimpleSimpleFunctions(Func merger) : base(merger) { } } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/TryAddFunctions.cs b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/TryAddFunctions.cs index 6b85f1e87b..d1d0d59e24 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/TryAddFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Interfaces/TryAddFunctions.cs @@ -11,7 +11,7 @@ namespace Tsavorite.core /// /// /// - public class TryAddFunctions : SimpleFunctions + public class TryAddFunctions : SimpleSessionFunctions { /// public override bool InPlaceUpdater(ref Key key, ref Value input, ref Value value, ref Value output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) => true; diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Recovery/Checkpoint.cs b/libs/storage/Tsavorite/cs/src/core/Index/Recovery/Checkpoint.cs index 8fda718bf6..c5ab2101cb 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Recovery/Checkpoint.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Recovery/Checkpoint.cs @@ -90,7 +90,7 @@ internal void InitializeHybridLogCheckpoint(Guid hybridLogToken, long version) _hybridLogCheckpoint.info.manualLockingActive = hlog.NumActiveLockingSessions > 0; } - internal long Compact(IFunctions functions, CompactionFunctions compactionFunctions, long untilAddress, CompactionType compactionType) + internal long Compact(ISessionFunctions functions, CompactionFunctions compactionFunctions, long untilAddress, CompactionType compactionType) where CompactionFunctions : ICompactionFunctions { throw new NotImplementedException(); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/BlockAllocate.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/BlockAllocate.cs index e77b86ff29..6b3a7a8b5e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/BlockAllocate.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/BlockAllocate.cs @@ -50,7 +50,7 @@ internal struct AllocateOptions bool TryAllocateRecord(TsavoriteSession tsavoriteSession, ref PendingContext pendingContext, ref OperationStackContext stackCtx, int actualSize, ref int allocatedSize, int newKeySize, AllocateOptions options, out long newLogicalAddress, out long newPhysicalAddress, out OperationStatus status) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { status = OperationStatus.SUCCESS; @@ -161,7 +161,7 @@ void SaveAllocationForRetry(ref PendingContext(TsavoriteSession tsavoriteSession, ref PendingContext pendingContext, long minAddress, ref int allocatedSize, int newKeySize, out long newLogicalAddress, out long newPhysicalAddress) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Use an earlier allocation from a failed operation, if possible. newLogicalAddress = pendingContext.retryNewLogicalAddress; diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ConditionalCopyToTail.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ConditionalCopyToTail.cs index efb2d94cf6..849d15c54b 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ConditionalCopyToTail.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ConditionalCopyToTail.cs @@ -28,7 +28,7 @@ private OperationStatus ConditionalCopyToTail pendingContext, ref Key key, ref Input input, ref Value value, ref Output output, Context userContext, ref OperationStackContext stackCtx, WriteReason writeReason, bool wantIO = true) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { bool callerHasTransientLock = stackCtx.recSrc.HasTransientSLock; @@ -89,7 +89,7 @@ private OperationStatus ConditionalCopyToTail(TsavoriteSession tsavoriteSession, ref Key key, ref Input input, ref Value value, ref Output output, long minAddress) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(epoch.ThisInstanceProtected(), "This is called only from Compaction so the epoch should be protected"); PendingContext pendingContext = new(); @@ -117,7 +117,7 @@ internal OperationStatus PrepareIOForConditionalOperation pendingContext, ref Key key, ref Input input, ref Value value, ref Output output, Context userContext, ref OperationStackContext stackCtx, long minAddress, WriteReason writeReason, OperationType opType = OperationType.CONDITIONAL_INSERT) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { pendingContext.type = opType; pendingContext.minAddress = minAddress; diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContainsKeyInMemory.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContainsKeyInMemory.cs index f1a764e51c..925517d54e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContainsKeyInMemory.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContainsKeyInMemory.cs @@ -10,7 +10,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status InternalContainsKeyInMemory( ref Key key, TsavoriteSession tsavoriteSession, out long logicalAddress, long fromAddress = -1) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { OperationStackContext stackCtx = new(comparer.GetHashCode64(ref key)); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContinuePending.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContinuePending.cs index 1af640fc1f..ae5c4fa575 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContinuePending.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/ContinuePending.cs @@ -28,7 +28,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase /// internal OperationStatus ContinuePendingRead(AsyncIOContext request, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { ref RecordInfo srcRecordInfo = ref hlog.GetInfoFromBytePointer(request.record.GetValidPointer()); srcRecordInfo.ClearBitsForDiskImages(); @@ -157,7 +157,7 @@ internal OperationStatus ContinuePendingRead internal OperationStatus ContinuePendingRMW(AsyncIOContext request, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { ref Key key = ref pendingContext.key.Get(); @@ -249,7 +249,7 @@ internal OperationStatus ContinuePendingRMW internal OperationStatus ContinuePendingConditionalCopyToTail(AsyncIOContext request, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // If the key was found at or above minAddress, do nothing. if (request.logicalAddress >= pendingContext.minAddress) @@ -305,7 +305,7 @@ internal OperationStatus ContinuePendingConditionalCopyToTail internal OperationStatus ContinuePendingConditionalScanPush(AsyncIOContext request, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // If the key was found at or above minAddress, do nothing; we'll push it when we get to it. If we flagged the iteration to stop, do nothing. if (request.logicalAddress >= pendingContext.minAddress || pendingContext.scanCursorState.stop) diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/EpochOperations.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/EpochOperations.cs index f29ff13e62..49d3def03e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/EpochOperations.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/EpochOperations.cs @@ -14,7 +14,7 @@ internal void SynchronizeEpoch( TsavoriteExecutionContext sessionCtx, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var version = sessionCtx.version; Debug.Assert(sessionCtx.version == version, $"sessionCtx.version ({sessionCtx.version}) should == version ({version})"); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/FindRecord.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/FindRecord.cs index 33b5199355..52cce157d5 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/FindRecord.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/FindRecord.cs @@ -49,7 +49,7 @@ internal bool TryFindRecordInMainLog(ref Key key, ref OperationStackContext(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, long minAddress, out OperationStatus internalStatus, out bool needIO) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { internalStatus = OperationStatus.SUCCESS; if (RevivificationManager.UseFreeRecordPool) diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HandleOperationStatus.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HandleOperationStatus.cs index c205cb59a8..d7388d30a8 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HandleOperationStatus.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HandleOperationStatus.cs @@ -15,7 +15,7 @@ private bool HandleImmediateRetryStatus pendingContext) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper => (internalStatus & OperationStatus.BASIC_MASK) > OperationStatus.MAX_MAP_TO_COMPLETED_STATUSCODE && HandleRetryStatus(internalStatus, tsavoriteSession, ref pendingContext); @@ -24,7 +24,7 @@ private bool HandleImmediateRetryStatus [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool HandleImmediateNonPendingRetryStatus(OperationStatus internalStatus, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(epoch.ThisInstanceProtected()); switch (internalStatus) @@ -45,7 +45,7 @@ private bool HandleRetryStatus( OperationStatus internalStatus, TsavoriteSession tsavoriteSession, ref PendingContext pendingContext) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(epoch.ThisInstanceProtected()); switch (internalStatus) diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HashEntryInfo.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HashEntryInfo.cs index a3f280003d..de7385ea1b 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HashEntryInfo.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/HashEntryInfo.cs @@ -8,7 +8,7 @@ namespace Tsavorite.core { /// Hash table entry information for a key - internal unsafe struct HashEntryInfo + public unsafe struct HashEntryInfo { /// The first bucket in this chain for this hash bucket internal HashBucket* firstBucket; diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Helpers.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Helpers.cs index 8dd7897665..3c94ada3b4 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Helpers.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Helpers.cs @@ -76,7 +76,7 @@ private bool IsEntryVersionNew(ref HashBucketEntry entry) // Also, it cannot be elided if it is frozen due to checkpointing. [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool CanElide(TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(!stackCtx.recSrc.HasReadCacheSrc, "Should not call CanElide() for readcache records"); return stackCtx.hei.Address == stackCtx.recSrc.LogicalAddress && srcRecordInfo.PreviousAddress < hlog.BeginAddress @@ -87,7 +87,7 @@ private bool CanElide(TsavoriteSession // if it is a new record. [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool IsFrozen(TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(!stackCtx.recSrc.HasReadCacheSrc, "Should not call IsFrozen() for readcache records"); return tsavoriteSession.Ctx.IsInV1 @@ -98,7 +98,7 @@ private bool IsFrozen(TsavoriteSession [MethodImpl(MethodImplOptions.AggressiveInlining)] private (bool elided, bool added) TryElideAndTransferToFreeList(TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, (int usedValueLength, int fullValueLength, int fullRecordLength) recordLengths) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Try to CAS out of the hashtable and if successful, add it to the free list. Debug.Assert(srcRecordInfo.IsSealed, "Expected a Sealed record in TryElideAndTransferToFreeList"); @@ -112,7 +112,7 @@ private bool IsFrozen(TsavoriteSession [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryTransferToFreeList(TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, (int usedValueLength, int fullValueLength, int fullRecordLength) recordLengths) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // The record has been CAS'd out of the hashtable or elided from the chain, so add it to the free list. Debug.Assert(srcRecordInfo.IsSealed, "Expected a Sealed record in TryTransferToFreeList"); @@ -204,7 +204,7 @@ private bool VerifyInMemoryAddresses(ref OperationStackContext stack [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool FindOrCreateTagAndTryTransientXLock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, out OperationStatus internalStatus) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Transient must lock the bucket before traceback, to prevent revivification from yanking the record out from underneath us. Manual locking already automatically locks the bucket. FindOrCreateTag(ref stackCtx.hei, hlog.BeginAddress); @@ -220,7 +220,7 @@ private bool FindOrCreateTagAndTryTransientXLock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, out OperationStatus internalStatus) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Transient must lock the bucket before traceback, to prevent revivification from yanking the record out from underneath us. Manual locking already automatically locks the bucket. internalStatus = OperationStatus.NOTFOUND; @@ -236,7 +236,7 @@ private bool FindTagAndTryTransientXLock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, out OperationStatus internalStatus) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Transient must lock the bucket before traceback, to prevent revivification from yanking the record out from underneath us. Manual locking already automatically locks the bucket. internalStatus = OperationStatus.NOTFOUND; 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 74bfdd96fd..5876866e33 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 @@ -40,7 +40,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] internal OperationStatus InternalDelete(ref Key key, long keyHash, ref Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var latchOperation = LatchOperation.None; @@ -223,7 +223,7 @@ internal OperationStatus InternalDelete(ref Key key, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { pendingContext.type = OperationType.DELETE; if (pendingContext.key == default) pendingContext.key = hlog.GetKeyContainer(ref key); @@ -250,7 +250,7 @@ private LatchDestination CheckCPRConsistencyDelete(Phase phase, ref OperationSta /// this is the for private OperationStatus CreateNewRecordDelete(ref Key key, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var value = default(Value); var (actualSize, allocatedSize, keySize) = hlog.GetRecordSize(ref key, ref value); 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 cf7289fcbd..572ff92e46 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 @@ -47,7 +47,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] internal OperationStatus InternalRMW(ref Key key, long keyHash, ref Input input, ref Output output, ref Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var latchOperation = LatchOperation.None; @@ -221,7 +221,7 @@ internal OperationStatus InternalRMW(r // No AggressiveInlining; this is a less-common function and it may improve inlining of InternalUpsert if the compiler decides not to inline this. private void CreatePendingRMWContext(ref Key key, ref Input input, Output output, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { pendingContext.type = OperationType.RMW; if (pendingContext.key == default) @@ -239,7 +239,7 @@ private void CreatePendingRMWContext(r private bool TryRevivifyInChain(ref Key key, ref Input input, ref Output output, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, ref RMWInfo rmwInfo, out OperationStatus status, ref Value recordValue) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (IsFrozen(tsavoriteSession, ref stackCtx, ref srcRecordInfo)) goto NeedNewRecord; @@ -401,7 +401,7 @@ private LatchDestination AcquireCPRLatchRMW(Phase phase, ref OperationStackConte /// The record Key /// Input to the operation /// Old value - /// The result of IFunctions.SingleWriter + /// The result of ISessionFunctions.SingleWriter /// Information about the operation context /// The current session /// Contains the and structures for this operation, @@ -415,7 +415,7 @@ private LatchDestination AcquireCPRLatchRMW(Phase phase, ref OperationStackConte private OperationStatus CreateNewRecordRMW(ref Key key, ref Input input, ref Value value, ref Output output, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, bool doingCU) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { bool forExpiration = false; @@ -615,7 +615,7 @@ private OperationStatus CreateNewRecordRMW(ref Key key, ref Input input, ref Value value, ref Output output, ref RecordInfo recordInfo, ref RMWInfo rmwInfo, long logicalAddress, TsavoriteSession tsavoriteSession, bool isIpu, out OperationStatus status) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // This is called for InPlaceUpdater or CopyUpdater only; CopyUpdater however does not copy an expired record, so we return CreatedRecord. var advancedStatusCode = isIpu ? StatusCode.InPlaceUpdatedRecord : StatusCode.CreatedRecord; 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 32b0a6fab5..6e7a2bf574 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 @@ -50,7 +50,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] internal OperationStatus InternalRead(ref Key key, long keyHash, ref Input input, ref Output output, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { OperationStackContext stackCtx = new(keyHash); pendingContext.keyHash = keyHash; @@ -171,7 +171,7 @@ internal OperationStatus InternalRead( // No AggressiveInlining; this is a less-common function and it may improve inlining of InternalRead to have this be a virtcall. private OperationStatus CopyFromImmutable(ref Key key, ref Input input, ref Output output, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref OperationStatus status, Value recordValue) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (pendingContext.readCopyOptions.CopyTo == ReadCopyTo.MainLog) { @@ -243,7 +243,7 @@ private static OperationStatus CheckFalseActionStatus(ReadInfo readInfo) [MethodImpl(MethodImplOptions.AggressiveInlining)] internal OperationStatus InternalReadAtAddress(long readAtAddress, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (readAtAddress < hlog.BeginAddress) return OperationStatus.NOTFOUND; @@ -315,7 +315,7 @@ internal OperationStatus InternalReadAtAddress= hlog.SafeReadOnlyAddress) { @@ -339,7 +339,7 @@ internal OperationStatus InternalReadAtAddress(ref Key key, ref Input input, Output output, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, long logicalAddress) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { pendingContext.type = OperationType.READ; if (!pendingContext.NoKey && pendingContext.key == default) // If this is true, we don't have a valid key 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 35ce31371b..e6b0e6e01c 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 @@ -43,7 +43,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] internal OperationStatus InternalUpsert(ref Key key, long keyHash, ref Input input, ref Value value, ref Output output, ref Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var latchOperation = LatchOperation.None; @@ -179,7 +179,7 @@ internal OperationStatus InternalUpsert(ref Key key, ref Input input, ref Value value, Output output, Context userContext, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { pendingContext.type = OperationType.UPSERT; if (pendingContext.key == default) @@ -199,7 +199,7 @@ private void CreatePendingUpsertContext(ref Key key, ref Input input, ref Value value, ref Output output, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, ref UpsertInfo upsertInfo, out OperationStatus status, ref Value recordValue) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (IsFrozen(tsavoriteSession, ref stackCtx, ref srcRecordInfo)) goto NeedNewRecord; @@ -339,7 +339,7 @@ private LatchDestination AcquireCPRLatchUpsert(Phase phase, ref OperationStackCo /// The record Key /// Input to the operation /// The value to insert - /// The result of IFunctions.SingleWriter + /// The result of ISessionFunctions.SingleWriter /// Information about the operation context /// The current session /// Contains the and structures for this operation, @@ -349,7 +349,7 @@ private LatchDestination AcquireCPRLatchUpsert(Phase phase, ref OperationStackCo private OperationStatus CreateNewRecordUpsert(ref Key key, ref Input input, ref Value value, ref Output output, ref PendingContext pendingContext, TsavoriteSession tsavoriteSession, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var (actualSize, allocatedSize, keySize) = hlog.GetRecordSize(ref key, ref value); // Input is not included in record-length calculations for Upsert AllocateOptions allocOptions = new() 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 8ef6e1f732..0a8c41b52e 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 @@ -9,8 +9,13 @@ namespace Tsavorite.core /// Manual-enabled (both manual and transient) LockTable interface definition /// /// - internal interface ILockTable : IDisposable + public interface ILockTable : IDisposable { + /// + /// Try to acquire a manual lock for the key. + /// + public bool IsEnabled { get; } + /// /// Try to acquire a manual lock for the key. /// 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 324fb199d4..af5e9ac8a7 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 @@ -14,7 +14,7 @@ internal struct OverflowBucketLockTable : ILockTable internal readonly long NumBuckets => IsEnabled ? store.state[store.resizeInfo.version].size_mask + 1 : 0; - internal readonly bool IsEnabled => store is not null; + public readonly bool IsEnabled => store is not null; internal OverflowBucketLockTable(TsavoriteKV f) => store = f; 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 b458cb143e..12acf67da5 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 @@ -11,7 +11,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool TryTransientXLock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, out OperationStatus status) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (tsavoriteSession.TryLockTransientExclusive(ref key, ref stackCtx)) { @@ -24,7 +24,7 @@ private bool TryTransientXLock(Tsavori [MethodImpl(MethodImplOptions.AggressiveInlining)] private static void TransientXUnlock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (stackCtx.recSrc.HasTransientXLock) tsavoriteSession.UnlockTransientExclusive(ref key, ref stackCtx); @@ -33,7 +33,7 @@ private static void TransientXUnlock(T [MethodImpl(MethodImplOptions.AggressiveInlining)] internal bool TryTransientSLock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx, out OperationStatus status) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (tsavoriteSession.TryLockTransientShared(ref key, ref stackCtx)) { @@ -46,7 +46,7 @@ internal bool TryTransientSLock(Tsavor [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void TransientSUnlock(TsavoriteSession tsavoriteSession, ref Key key, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (stackCtx.recSrc.HasTransientSLock) tsavoriteSession.UnlockTransientShared(ref key, ref stackCtx); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/OperationStackContext.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/OperationStackContext.cs index bbc3eefa47..4238c41360 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/OperationStackContext.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/OperationStackContext.cs @@ -6,7 +6,7 @@ namespace Tsavorite.core { - internal struct OperationStackContext + public struct OperationStackContext { // Note: Cannot use ref fields because they are not supported before net7.0. internal HashEntryInfo hei; diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RecordLengths.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RecordLengths.cs index 1ea2fab455..61e5d0438d 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RecordLengths.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/Revivification/RecordLengths.cs @@ -145,7 +145,7 @@ static void ClearExtraValueSpace(ref RecordInfo recordInfo, ref Value recordValu // Do not try to inline this; it causes TryAllocateRecord to bloat and slow bool TryTakeFreeRecord(TsavoriteSession tsavoriteSession, int requiredSize, ref int allocatedSize, int newKeySize, long minRevivAddress, out long logicalAddress, out long physicalAddress) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Caller checks for UseFreeRecordPool if (RevivificationManager.TryTake(allocatedSize, minRevivAddress, out logicalAddress, ref tsavoriteSession.Ctx.RevivificationStats)) @@ -207,7 +207,7 @@ internal void SetTombstoneAndExtraValueLength(ref Value recordValue, ref RecordI [MethodImpl(MethodImplOptions.AggressiveInlining)] internal (bool ok, int usedValueLength) TryReinitializeTombstonedValue(TsavoriteSession tsavoriteSession, ref RecordInfo srcRecordInfo, ref Key key, ref Value recordValue, int requiredSize, (int usedValueLength, int fullValueLength, int allocatedSize) recordLengths) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { if (RevivificationManager.IsFixedLength || recordLengths.allocatedSize < requiredSize) return (false, recordLengths.usedValueLength); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToReadCache.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToReadCache.cs index 63a92eac2b..9ef1fb9fce 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToReadCache.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToReadCache.cs @@ -19,7 +19,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase /// internal bool TryCopyToReadCache(TsavoriteSession tsavoriteSession, ref PendingContext pendingContext, ref Key key, ref Input input, ref Value recordValue, ref OperationStackContext stackCtx) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var (actualSize, allocatedSize, _) = hlog.GetRecordSize(ref key, ref recordValue); diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToTail.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToTail.cs index 20d7f64794..6b258b7111 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToTail.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Implementation/TryCopyToTail.cs @@ -27,7 +27,7 @@ public unsafe partial class TsavoriteKV : TsavoriteBase internal OperationStatus TryCopyToTail(ref PendingContext pendingContext, ref Key key, ref Input input, ref Value value, ref Output output, ref OperationStackContext stackCtx, ref RecordInfo srcRecordInfo, TsavoriteSession tsavoriteSession, WriteReason reason) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var (actualSize, allocatedSize, keySize) = hlog.GetRecordSize(ref key, ref value); if (!TryAllocateRecord(tsavoriteSession, ref pendingContext, ref stackCtx, actualSize, ref allocatedSize, keySize, new AllocateOptions() { Recycle = true }, diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/LogAccessor.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/LogAccessor.cs index c088d36c62..740b53c07e 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/LogAccessor.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/LogAccessor.cs @@ -335,7 +335,7 @@ public void DisposeFromMemory() /// Compaction type (whether we lookup records or scan log for liveness checking) /// Address until which compaction was done public long Compact(Functions functions, long untilAddress, CompactionType compactionType) - where Functions : IFunctions + where Functions : ISessionFunctions => Compact>(functions, default, untilAddress, compactionType); /// @@ -349,7 +349,7 @@ public long Compact(Functions functions, long /// Compaction type (whether we lookup records or scan log for liveness checking) /// Address until which compaction was done public long Compact(Functions functions, ref Input input, ref Output output, long untilAddress, CompactionType compactionType) - where Functions : IFunctions + where Functions : ISessionFunctions => Compact>(functions, default, ref input, ref output, untilAddress, compactionType); /// @@ -362,7 +362,7 @@ public long Compact(Functions functions, ref /// Compaction type (whether we lookup records or scan log for liveness checking) /// Address until which compaction was done public long Compact(Functions functions, CompactionFunctions cf, long untilAddress, CompactionType compactionType) - where Functions : IFunctions + where Functions : ISessionFunctions where CompactionFunctions : ICompactionFunctions { Input input = default; @@ -382,7 +382,7 @@ public long Compact(Func /// Compaction type (whether we lookup records or scan log for liveness checking) /// Address until which compaction was done public long Compact(Functions functions, CompactionFunctions cf, ref Input input, ref Output output, long untilAddress, CompactionType compactionType) - where Functions : IFunctions + where Functions : ISessionFunctions where CompactionFunctions : ICompactionFunctions => store.Compact(functions, cf, ref input, ref output, untilAddress, compactionType); } 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 25393a232c..ed73287d7f 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/Tsavorite.cs @@ -540,7 +540,7 @@ public async ValueTask CompleteCheckpointAsync(CancellationToken token = default [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextRead(ref Key key, ref Input input, ref Output output, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions); OperationStatus internalStatus; @@ -558,7 +558,7 @@ internal Status ContextRead(ref Key ke [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextRead(ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions, ref readOptions); OperationStatus internalStatus; @@ -575,7 +575,7 @@ internal Status ContextRead(ref Key ke [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextReadAtAddress(long address, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions, ref readOptions, noKey: true); Key key = default; @@ -584,7 +584,7 @@ internal Status ContextReadAtAddress(l [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = new PendingContext(tsavoriteSession.Ctx.ReadCopyOptions, ref readOptions, noKey: false); return ContextReadAtAddress(address, ref key, ref input, ref output, ref readOptions, out recordMetadata, context, ref pcontext, tsavoriteSession); @@ -593,7 +593,7 @@ internal Status ContextReadAtAddress(l [MethodImpl(MethodImplOptions.AggressiveInlining)] private Status ContextReadAtAddress(long address, ref Key key, ref Input input, ref Output output, ref ReadOptions readOptions, out RecordMetadata recordMetadata, Context context, ref PendingContext pcontext, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { OperationStatus internalStatus; do @@ -607,7 +607,7 @@ private Status ContextReadAtAddress(lo [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextUpsert(ref Key key, long keyHash, ref Input input, ref Value value, ref Output output, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = default(PendingContext); OperationStatus internalStatus; @@ -623,7 +623,7 @@ internal Status ContextUpsert(ref Key [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextUpsert(ref Key key, long keyHash, ref Input input, ref Value value, ref Output output, out RecordMetadata recordMetadata, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = default(PendingContext); OperationStatus internalStatus; @@ -640,7 +640,7 @@ internal Status ContextUpsert(ref Key [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextRMW(ref Key key, long keyHash, ref Input input, ref Output output, out RecordMetadata recordMetadata, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = default(PendingContext); OperationStatus internalStatus; @@ -656,7 +656,7 @@ internal Status ContextRMW(ref Key key [MethodImpl(MethodImplOptions.AggressiveInlining)] internal Status ContextDelete(ref Key key, long keyHash, Context context, TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { var pcontext = default(PendingContext); OperationStatus internalStatus; 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 120dcdb859..61b891f52f 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteIterator.cs @@ -16,7 +16,7 @@ public partial class TsavoriteKV : TsavoriteBase /// Report records until this address (tail by default) /// Tsavorite iterator public ITsavoriteScanIterator Iterate(Functions functions, long untilAddress = -1) - where Functions : IFunctions + where Functions : ISessionFunctions { if (untilAddress == -1) untilAddress = Log.TailAddress; @@ -31,7 +31,7 @@ public ITsavoriteScanIterator IterateReport records until this address (tail by default) /// Tsavorite iterator public bool Iterate(Functions functions, ref TScanFunctions scanFunctions, long untilAddress = -1) - where Functions : IFunctions + where Functions : ISessionFunctions where TScanFunctions : IScanIteratorFunctions { if (untilAddress == -1) @@ -72,11 +72,12 @@ public ITsavoriteScanIterator Iterate(Compactio } internal sealed class TsavoriteKVIterator : ITsavoriteScanIterator - where Functions : IFunctions + where Functions : ISessionFunctions { private readonly TsavoriteKV store; private readonly TsavoriteKV tempKv; private readonly ClientSession tempKvSession; + private readonly BasicContext tempbContext; private readonly ITsavoriteScanIterator mainKvIter; private readonly IPushScanIterator pushScanIterator; private ITsavoriteScanIterator tempKvIter; @@ -97,6 +98,7 @@ public TsavoriteKVIterator(TsavoriteKV store, Functions functions, l tempKv = new TsavoriteKV(store.IndexSize, new LogSettings { LogDevice = new NullDevice(), ObjectLogDevice = new NullDevice(), MutableFraction = 1 }, comparer: store.Comparer, loggerFactory: loggerFactory, concurrencyControlMode: ConcurrencyControlMode.None); tempKvSession = tempKv.NewSession(functions); + tempbContext = tempKvSession.BasicContext; mainKvIter = store.Log.Scan(store.Log.BeginAddress, untilAddress); pushScanIterator = mainKvIter as IPushScanIterator; } @@ -237,11 +239,11 @@ private void ProcessNonTailmostMainKvRecord(RecordInfo recordInfo, Key key) if (recordInfo.Tombstone) { // Check if it's in-memory first so we don't spuriously create a tombstone record. - if (tempKvSession.ContainsKeyInMemory(ref key, out _).Found) - tempKvSession.Delete(ref key); + if (tempbContext.ContainsKeyInMemory(ref key, out _).Found) + tempbContext.Delete(ref key); } else - tempKvSession.Upsert(ref key, ref mainKvIter.GetValue()); + tempbContext.Upsert(ref key, ref mainKvIter.GetValue()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -259,8 +261,8 @@ bool IsTailmostMainKvRecord(ref Key key, RecordInfo mainKvRecordInfo, ref Operat if (mainKvRecordInfo.PreviousAddress >= store.Log.BeginAddress) { // Check if it's in-memory first so we don't spuriously create a tombstone record. - if (tempKvSession.ContainsKeyInMemory(ref key, out _).Found) - tempKvSession.Delete(ref key); + if (tempbContext.ContainsKeyInMemory(ref key, out _).Found) + tempbContext.Delete(ref key); } // If the record is not deleted, we can let the caller process it directly within mainKvIter. diff --git a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteThread.cs b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteThread.cs index b3704ba284..6751565bae 100644 --- a/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteThread.cs +++ b/libs/storage/Tsavorite/cs/src/core/Index/Tsavorite/TsavoriteThread.cs @@ -12,7 +12,7 @@ public partial class TsavoriteKV : TsavoriteBase { [MethodImpl(MethodImplOptions.AggressiveInlining)] internal void InternalRefresh(TsavoriteSession tsavoriteSession) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { epoch.ProtectAndDrain(); @@ -85,7 +85,7 @@ internal static void CopyContext(TsavoriteExecutionConte internal bool InternalCompletePending(TsavoriteSession tsavoriteSession, bool wait = false, CompletedOutputIterator completedOutputs = null) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { while (true) { @@ -106,7 +106,7 @@ internal bool InternalCompletePending( #region Complete Pending Requests internal void InternalCompletePendingRequests(TsavoriteSession tsavoriteSession, CompletedOutputIterator completedOutputs) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { hlog.TryComplete(); @@ -120,7 +120,7 @@ internal void InternalCompletePendingRequests(TsavoriteSession tsavoriteSession, AsyncIOContext request, CompletedOutputIterator completedOutputs) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { // Get and Remove this request.id pending dictionary if it is there. if (tsavoriteSession.Ctx.ioPendingRequests.Remove(request.id, out var pendingContext)) @@ -141,7 +141,7 @@ internal void InternalCompletePendingRequest internal Status InternalCompletePendingRequestFromContext(TsavoriteSession tsavoriteSession, AsyncIOContext request, ref PendingContext pendingContext, out AsyncIOContext newRequest) - where TsavoriteSession : ITsavoriteSession + where TsavoriteSession : ISessionFunctionsWrapper { Debug.Assert(epoch.ThisInstanceProtected(), "InternalCompletePendingRequestFromContext requires epoch acquision"); newRequest = default; diff --git a/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs b/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs index 52b9d834c9..2fd4df3fc0 100644 --- a/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs +++ b/libs/storage/Tsavorite/cs/src/core/Utilities/LockType.cs @@ -123,7 +123,7 @@ public override string ToString() /// /// Lock state of a record /// - internal struct LockState + public struct LockState { internal bool IsLockedExclusive; internal bool IsFound; diff --git a/libs/storage/Tsavorite/cs/src/core/Utilities/StatusCode.cs b/libs/storage/Tsavorite/cs/src/core/Utilities/StatusCode.cs index ae57657e7d..aed7964c74 100644 --- a/libs/storage/Tsavorite/cs/src/core/Utilities/StatusCode.cs +++ b/libs/storage/Tsavorite/cs/src/core/Utilities/StatusCode.cs @@ -55,7 +55,7 @@ internal enum StatusCode : byte NotFound = 0x01, /// - /// The operation was canceled (e.g. by an IFunctions method setting info.CancelOperation). This is not combined with advanced enum values. + /// The operation was canceled (e.g. by an ISessionFunctions method setting info.CancelOperation). This is not combined with advanced enum values. /// Canceled = 0x02, diff --git a/libs/storage/Tsavorite/cs/src/core/VarLen/SpanByteFunctions.cs b/libs/storage/Tsavorite/cs/src/core/VarLen/SpanByteFunctions.cs index 694cd608ed..66d073a882 100644 --- a/libs/storage/Tsavorite/cs/src/core/VarLen/SpanByteFunctions.cs +++ b/libs/storage/Tsavorite/cs/src/core/VarLen/SpanByteFunctions.cs @@ -41,7 +41,7 @@ public override bool ConcurrentReader(ref SpanByte key, ref SpanByte input, ref /// /// Callback functions for key, value; specified , , and /// - public class SpanByteFunctions : FunctionsBase + public class SpanByteFunctions : SessionFunctionsBase { /// public override bool SingleWriter(ref SpanByte key, ref Input input, ref SpanByte src, ref SpanByte dst, ref Output output, ref UpsertInfo upsertInfo, WriteReason reason, ref RecordInfo recordInfo) diff --git a/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs b/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs index 2ac9f8adcf..02c01787f7 100644 --- a/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs +++ b/libs/storage/Tsavorite/cs/test/AdvancedLockTests.cs @@ -37,7 +37,7 @@ internal enum LockTestMode CTTAfterUpdate = 2, } - internal class Functions : FunctionsBase, IDisposable + 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); @@ -171,10 +171,11 @@ public void TearDown() void Populate(bool evict = false) { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; for (int key = 0; key < numKeys; key++) - session.Upsert(key, key + valueAdd); - session.CompletePending(true); + bContext.Upsert(key, key + valueAdd); + bContext.CompletePending(true); if (evict) store.Log.FlushAndEvict(wait: true); } @@ -191,7 +192,9 @@ void Populate(bool evict = false) 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 @@ -230,19 +233,19 @@ await DoTwoThreadRandomKeyTest(numKeys, doRandom: false, { // Upsert of a record not in mutable log returns NOTFOUND because it creates a new record. expectedFound = readCopyTo == ReadCopyTo.MainLog && testMode == LockTestMode.UpdateAfterCTT; - status = updateSession.Upsert(ref key, ref input, ref input.updatedValue, ref output); + status = updatebContext.Upsert(ref key, ref input, ref input.updatedValue, ref output); } else { // RMW will always find and update or copyupdate. expectedFound = true; - status = updateSession.RMW(ref key, ref input, ref output); + 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) { - updateSession.CompletePendingWithOutputs(out var completedOutputs, wait: true); + updatebContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); } } @@ -258,7 +261,7 @@ await DoTwoThreadRandomKeyTest(numKeys, doRandom: false, 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 = readSession.Read(ref key, ref input, ref output, ref readOptions, out _); + 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}"); @@ -276,14 +279,14 @@ await DoTwoThreadRandomKeyTest(numKeys, doRandom: false, expectedOutput += valueAdd; } - readSession.CompletePendingWithOutputs(out var completedOutputs, wait: true); + 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 = readSession.Read(ref key, ref output); + 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. diff --git a/libs/storage/Tsavorite/cs/test/AsyncLargeObjectTests.cs b/libs/storage/Tsavorite/cs/test/AsyncLargeObjectTests.cs index 5d0565f767..eaec19bfed 100644 --- a/libs/storage/Tsavorite/cs/test/AsyncLargeObjectTests.cs +++ b/libs/storage/Tsavorite/cs/test/AsyncLargeObjectTests.cs @@ -50,12 +50,13 @@ public async Task LargeObjectTest([Values] CheckpointType checkpointType) using (var s = store1.NewSession(functions)) { + var bContext = s.BasicContext; Random r = new Random(33); for (int key = 0; key < numOps; key++) { var mykey = new MyKey { key = key }; var value = new MyLargeValue(1 + r.Next(maxSize)); - s.Upsert(ref mykey, ref value, Empty.Default); + bContext.Upsert(ref mykey, ref value, Empty.Default); } } @@ -78,13 +79,14 @@ public async Task LargeObjectTest([Values] CheckpointType checkpointType) store2.Recover(token); using (var s2 = store2.NewSession(functions)) { + var bContext = s2.BasicContext; for (int keycnt = 0; keycnt < numOps; keycnt++) { var key = new MyKey { key = keycnt }; - var status = s2.Read(ref key, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key, ref input, ref output, Empty.Default); if (status.IsPending) - await s2.CompletePendingAsync(); + await bContext.CompletePendingAsync(); else { for (int i = 0; i < output.value.value.Length; i++) diff --git a/libs/storage/Tsavorite/cs/test/BasicLockTests.cs b/libs/storage/Tsavorite/cs/test/BasicLockTests.cs index 058884c78e..a03fc4b0a8 100644 --- a/libs/storage/Tsavorite/cs/test/BasicLockTests.cs +++ b/libs/storage/Tsavorite/cs/test/BasicLockTests.cs @@ -14,7 +14,7 @@ namespace Tsavorite.test.LockTests [TestFixture] public class BasicLockTests { - internal class Functions : SimpleFunctions + internal class Functions : SimpleSimpleFunctions { internal bool throwOnInitialUpdater; internal long initialUpdaterThrowAddress; @@ -71,6 +71,7 @@ internal class LocalComparer : ITsavoriteEqualityComparer private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; const int numRecords = 100; @@ -83,6 +84,7 @@ public void Setup() 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); session = store.NewSession(new Functions()); + bContext = session.BasicContext; } [TearDown] @@ -106,7 +108,7 @@ public void FunctionsLockTest([Values(1, 20)] int numThreads) for (int key = 0; key < numRecords; key++) { // For this test we should be in-memory, so no pending - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); } // Update @@ -118,7 +120,7 @@ public void FunctionsLockTest([Values(1, 20)] int numThreads) for (int key = 0; key < numRecords; key++) { var expectedValue = key * valueMult + numThreads * numIters; - Assert.IsFalse(session.Read(key, out int value).IsPending); + Assert.IsFalse(bContext.Read(key, out int value).IsPending); Assert.AreEqual(expectedValue, value); } } @@ -130,13 +132,13 @@ void UpdateFunc(bool useRMW, int numRecords, int numIters) for (int iter = 0; iter < numIters; iter++) { if ((iter & 7) == 7) - Assert.IsFalse(session.Read(key).status.IsPending); + Assert.IsFalse(bContext.Read(key).status.IsPending); // These will both just increment the stored value, ignoring the input argument. if (useRMW) - session.RMW(key, default); + bContext.RMW(key, default); else - session.Upsert(key, default); + bContext.Upsert(key, default); } } } @@ -149,13 +151,13 @@ public unsafe void CollidingDeletedRecordTest([Values(UpdateOp.RMW, UpdateOp.Ups for (int key = 0; key < numRecords; key++) { // For this test we should be in-memory, so no pending - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); } // Insert a colliding key so we don't elide the deleted key from the hash chain. int deleteKey = numRecords / 2; int collidingKey = deleteKey + numRecords; - Assert.IsFalse(session.Upsert(collidingKey, collidingKey * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(collidingKey, collidingKey * valueMult).IsPending); // Now make sure we did collide HashEntryInfo hei = new(store.comparer.GetHashCode64(ref deleteKey)); @@ -174,7 +176,7 @@ public unsafe void CollidingDeletedRecordTest([Values(UpdateOp.RMW, UpdateOp.Ups Assert.IsFalse(recordInfo.Tombstone, "Tombstone should be false"); // In-place delete. - Assert.IsFalse(session.Delete(deleteKey).IsPending); + Assert.IsFalse(bContext.Delete(deleteKey).IsPending); Assert.IsTrue(recordInfo.Tombstone, "Tombstone should be true after Delete"); if (flushMode == FlushMode.ReadOnly) @@ -182,8 +184,8 @@ public unsafe void CollidingDeletedRecordTest([Values(UpdateOp.RMW, UpdateOp.Ups var status = updateOp switch { - UpdateOp.RMW => session.RMW(deleteKey, default), - UpdateOp.Upsert => session.Upsert(deleteKey, default), + UpdateOp.RMW => bContext.RMW(deleteKey, default), + UpdateOp.Upsert => bContext.Upsert(deleteKey, default), UpdateOp.Delete => throw new InvalidOperationException("UpdateOp.Delete not expected in this test"), _ => throw new InvalidOperationException($"Unknown updateOp {updateOp}") }; @@ -203,7 +205,7 @@ public unsafe void SetInvalidOnException([Values] UpdateOp updateOp) for (int key = 0; key < numRecords; key++) { // For this test we should be in-memory, so no pending - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); } long expectedThrowAddress = store.Log.TailAddress; @@ -221,9 +223,9 @@ public unsafe void SetInvalidOnException([Values] UpdateOp updateOp) { var status = updateOp switch { - UpdateOp.RMW => session.RMW(insertKey, default), - UpdateOp.Upsert => session.Upsert(insertKey, default), - UpdateOp.Delete => session.Delete(deleteKey), + UpdateOp.RMW => bContext.RMW(insertKey, default), + UpdateOp.Upsert => bContext.Upsert(insertKey, default), + UpdateOp.Delete => bContext.Delete(deleteKey), _ => throw new InvalidOperationException($"Unknown updateOp {updateOp}") }; Assert.IsFalse(status.IsPending); diff --git a/libs/storage/Tsavorite/cs/test/BasicStorageTests.cs b/libs/storage/Tsavorite/cs/test/BasicStorageTests.cs index 22dba1adb3..92dec1d946 100644 --- a/libs/storage/Tsavorite/cs/test/BasicStorageTests.cs +++ b/libs/storage/Tsavorite/cs/test/BasicStorageTests.cs @@ -98,6 +98,7 @@ void TestDeviceWriteRead(IDevice log) (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 10 }); var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; InputStruct input = default; @@ -105,18 +106,18 @@ void TestDeviceWriteRead(IDevice log) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); // Update first 100 using RMW from storage for (int i = 0; i < 100; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = 1, ifield2 = 1 }; - var status = session.RMW(ref key1, ref input, Empty.Default); + var status = bContext.RMW(ref key1, ref input, Empty.Default); if (status.IsPending) - session.CompletePending(true); + bContext.CompletePending(true); } @@ -126,9 +127,9 @@ void TestDeviceWriteRead(IDevice log) var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - if (session.Read(ref key1, ref input, ref output, Empty.Default).IsPending) + if (bContext.Read(ref key1, ref input, ref output, Empty.Default).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { diff --git a/libs/storage/Tsavorite/cs/test/BasicTests.cs b/libs/storage/Tsavorite/cs/test/BasicTests.cs index 2912dafed1..ec0e7bdfec 100644 --- a/libs/storage/Tsavorite/cs/test/BasicTests.cs +++ b/libs/storage/Tsavorite/cs/test/BasicTests.cs @@ -21,6 +21,7 @@ internal class BasicTests { private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; DeviceType deviceType; @@ -38,6 +39,7 @@ private void Setup(long size, LogSettings logSettings, DeviceType deviceType, in logSettings.LogDevice = log; store = new TsavoriteKV(size, logSettings); session = store.NewSession(new Functions()); + bContext = session.BasicContext; } [TearDown] @@ -61,7 +63,7 @@ private void AssertCompleted(Status expected, Status actual) private (Status status, OutputStruct output) CompletePendingResult() { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); return GetSinglePendingResult(completedOutputs); } @@ -78,8 +80,8 @@ public void NativeInMemWriteRead([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(value.vfield1, output.value.vfield1); @@ -99,20 +101,20 @@ public void NativeInMemWriteReadDelete([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); - session.Delete(ref key1, Empty.Default); + bContext.Delete(ref key1, Empty.Default); - status = session.Read(ref key1, ref input, ref output, Empty.Default); + status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.NotFound), status); var key2 = new KeyStruct { kfield1 = 14, kfield2 = 15 }; var value2 = new ValueStruct { vfield1 = 24, vfield2 = 25 }; - session.Upsert(ref key2, ref value2, Empty.Default); - status = session.Read(ref key2, ref input, ref output, Empty.Default); + bContext.Upsert(ref key2, ref value2, Empty.Default); + status = bContext.Read(ref key2, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(value2.vfield1, output.value.vfield1); @@ -141,13 +143,13 @@ public void NativeInMemWriteReadDelete2() var key1 = new KeyStruct { kfield1 = i, kfield2 = 14 }; var value = new ValueStruct { vfield1 = i, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } for (int i = 0; i < 10 * count; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = 14 }; - session.Delete(ref key1, Empty.Default); + bContext.Delete(ref key1, Empty.Default); } for (int i = 0; i < 10 * count; i++) @@ -155,16 +157,16 @@ public void NativeInMemWriteReadDelete2() var key1 = new KeyStruct { kfield1 = i, kfield2 = 14 }; var value = new ValueStruct { vfield1 = i, vfield2 = 24 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.NotFound), status); - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } for (int i = 0; i < 10 * count; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = 14 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); } } @@ -191,7 +193,7 @@ public unsafe void NativeInMemWriteRead2() var i = r.Next(10000); var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } r = new Random(10); @@ -203,9 +205,9 @@ public unsafe void NativeInMemWriteRead2() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - if (session.Read(ref key1, ref input, ref output, Empty.Default).IsPending) + if (bContext.Read(ref key1, ref input, ref output, Empty.Default).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } Assert.AreEqual(value.vfield1, output.value.vfield1); @@ -221,7 +223,7 @@ public unsafe void NativeInMemWriteRead2() var i = r.Next(10000); OutputStruct output = default; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; - Assert.IsFalse(session.Read(ref key1, ref input, ref output, Empty.Default).Found); + Assert.IsFalse(bContext.Read(ref key1, ref input, ref output, Empty.Default).Found); } } @@ -246,7 +248,7 @@ public unsafe void TestShiftHeadAddress([Values] DeviceType deviceType, [Values] var i = r.Next(RandRange); var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } r = new Random(RandSeed); @@ -259,13 +261,13 @@ public unsafe void TestShiftHeadAddress([Values] DeviceType deviceType, [Values] var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - if (session.Read(ref key1, ref input, ref output, Empty.Default).IsPending) + if (bContext.Read(ref key1, ref input, ref output, Empty.Default).IsPending) { Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); } } - session.CompletePending(true); + bContext.CompletePending(true); // Shift head and retry - should not find in main memory now store.Log.FlushAndEvict(true); @@ -279,12 +281,12 @@ public unsafe void TestShiftHeadAddress([Values] DeviceType deviceType, [Values] var i = r.Next(RandRange); OutputStruct output = default; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; - Status foundStatus = session.Read(ref key1, ref input, ref output, Empty.Default); + Status foundStatus = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(foundStatus.IsPending); if (batchMode == BatchMode.NoBatch) { Status status; - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(key1.kfield1, output.value.vfield1); @@ -293,7 +295,7 @@ public unsafe void TestShiftHeadAddress([Values] DeviceType deviceType, [Values] } else if (c > 0 && (c % batchSize) == 0) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); int count = 0; while (outputs.Next()) { @@ -332,16 +334,16 @@ public unsafe void NativeInMemRMWRefKeys([Values] DeviceType deviceType) var i = nums[j]; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = i, ifield2 = i + 1 }; - session.RMW(ref key1, ref input, Empty.Default); + bContext.RMW(ref key1, ref input, Empty.Default); } for (int j = 0; j < nums.Length; ++j) { var i = nums[j]; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = i, ifield2 = i + 1 }; - if (session.RMW(ref key1, ref input, ref output, Empty.Default).IsPending) + if (bContext.RMW(ref key1, ref input, ref output, Empty.Default).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -360,7 +362,7 @@ public unsafe void NativeInMemRMWRefKeys([Values] DeviceType deviceType) key = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; ValueStruct value = new() { vfield1 = i, vfield2 = i + 1 }; - status = session.Read(ref key, ref input, ref output, Empty.Default); + status = bContext.Read(ref key, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(2 * value.vfield1, output.value.vfield1); @@ -368,7 +370,7 @@ public unsafe void NativeInMemRMWRefKeys([Values] DeviceType deviceType) } key = new KeyStruct { kfield1 = nums.Length, kfield2 = nums.Length + 1 }; - status = session.Read(ref key, ref input, ref output, Empty.Default); + status = bContext.Read(ref key, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.NotFound), status); } @@ -397,7 +399,7 @@ public unsafe void NativeInMemRMWNoRefKeys([Values] DeviceType deviceType) var i = nums[j]; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = i, ifield2 = i + 1 }; - session.RMW(ref key1, ref input, Empty.Default); + bContext.RMW(ref key1, ref input, Empty.Default); } // CopyUpdater @@ -406,7 +408,7 @@ public unsafe void NativeInMemRMWNoRefKeys([Values] DeviceType deviceType) var i = nums[j]; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = i, ifield2 = i + 1 }; - session.RMW(key1, input); // no ref and do not set any other params + bContext.RMW(key1, input); // no ref and do not set any other params } OutputStruct output = default; @@ -420,7 +422,7 @@ public unsafe void NativeInMemRMWNoRefKeys([Values] DeviceType deviceType) key = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; ValueStruct value = new() { vfield1 = i, vfield2 = i + 1 }; - status = session.Read(ref key, ref input, ref output, Empty.Default); + status = bContext.Read(ref key, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(2 * value.vfield1, output.value.vfield1); @@ -428,7 +430,7 @@ public unsafe void NativeInMemRMWNoRefKeys([Values] DeviceType deviceType) } key = new KeyStruct { kfield1 = nums.Length, kfield2 = nums.Length + 1 }; - status = session.Read(ref key, ref input, ref output, Empty.Default); + status = bContext.Read(ref key, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.NotFound), status); } @@ -445,8 +447,8 @@ public void ReadNoRefKeyInputOutput([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(key1, input, out OutputStruct output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(key1, input, out OutputStruct output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); // Verify the read data @@ -466,8 +468,8 @@ public void ReadNoRefKey([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(key1, out OutputStruct output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(key1, out OutputStruct output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); // Verify the read data @@ -491,8 +493,8 @@ public void ReadWithoutInput([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(ref key1, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(ref key1, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); // Verify the read data @@ -513,9 +515,9 @@ public void ReadBareMinParams([Values] DeviceType deviceType) var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); - var (status, output) = session.Read(key1); + var (status, output) = bContext.Read(key1); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(value.vfield1, output.value.vfield1); @@ -541,8 +543,8 @@ public void ReadAtAddressDefaultOptions() var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; ReadOptions readOptions = default; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.ReadAtAddress(store.Log.BeginAddress, ref input, ref output, ref readOptions, out _, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.ReadAtAddress(store.Log.BeginAddress, ref input, ref output, ref readOptions, out _, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(value.vfield1, output.value.vfield1); @@ -591,6 +593,7 @@ public void ReadAtAddressIgnoreReadCache() SkipReadCacheFunctions functions = new(); using var skipReadCacheSession = store.NewSession(functions); + var skipReadCachebContext = skipReadCacheSession.BasicContext; InputStruct input = default; OutputStruct output = default; @@ -599,7 +602,7 @@ public void ReadAtAddressIgnoreReadCache() var readAtAddress = store.Log.BeginAddress; Status status; - skipReadCacheSession.Upsert(ref key1, ref value, Empty.Default); + skipReadCachebContext.Upsert(ref key1, ref value, Empty.Default); void VerifyOutput() { @@ -614,7 +617,7 @@ void VerifyResult() { if (status.IsPending) { - skipReadCacheSession.CompletePendingWithOutputs(out var completedOutputs, wait: true); + skipReadCachebContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); } Assert.IsTrue(status.Found); @@ -623,7 +626,7 @@ void VerifyResult() // This will just be an ordinary read, as the record is in memory. functions.expectedReadAddress = readAtAddress; - status = skipReadCacheSession.Read(ref key1, ref input, ref output); + status = skipReadCachebContext.Read(ref key1, ref input, ref output); Assert.IsTrue(status.Found); VerifyOutput(); @@ -638,7 +641,7 @@ void VerifyResult() // Do not put it into the read cache. functions.expectedReadAddress = readAtAddress; ReadOptions readOptions = new() { CopyOptions = ReadCopyOptions.None }; - status = skipReadCacheSession.ReadAtAddress(readAtAddress, ref key1, ref input, ref output, ref readOptions, out _); + status = skipReadCachebContext.ReadAtAddress(readAtAddress, ref key1, ref input, ref output, ref readOptions, out _); VerifyResult(); Assert.AreEqual(store.ReadCache.BeginAddress, store.ReadCache.TailAddress); @@ -646,7 +649,7 @@ void VerifyResult() // Put it into the read cache. functions.expectedReadAddress = readAtAddress; readOptions.CopyOptions = new(ReadCopyFrom.AllImmutable, ReadCopyTo.ReadCache); - status = skipReadCacheSession.ReadAtAddress(readAtAddress, ref key1, ref input, ref output, ref readOptions, out _); + status = skipReadCachebContext.ReadAtAddress(readAtAddress, ref key1, ref input, ref output, ref readOptions, out _); Assert.IsTrue(status.IsPending); VerifyResult(); @@ -654,7 +657,7 @@ void VerifyResult() // Now this will read from the read cache. functions.expectedReadAddress = Constants.kInvalidAddress; - status = skipReadCacheSession.Read(ref key1, ref input, ref output); + status = skipReadCachebContext.Read(ref key1, ref input, ref output); Assert.IsFalse(status.IsPending); Assert.IsTrue(status.Found); VerifyOutput(); @@ -676,8 +679,8 @@ public void UpsertDefaultsTest([Values] DeviceType deviceType) Assert.AreEqual(0, store.EntryCount); - session.Upsert(ref key1, ref value); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(1, store.EntryCount); @@ -702,8 +705,8 @@ public void UpsertNoRefNoDefaultsTest() var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(key1, value, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(key1, value, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); AssertCompleted(new(StatusCode.Found), status); Assert.AreEqual(value.vfield1, output.value.vfield1); @@ -719,14 +722,16 @@ public static void KVBasicsSampleEndToEndInDocs() { using var log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog.log"), deleteOnClose: false); using var store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log }); - using var s = store.NewSession>(new SimpleFunctions()); + using var s = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = s.BasicContext; + long key = 1, value = 1, input = 10, output = 0; - s.Upsert(ref key, ref value); - s.Read(ref key, ref output); + bContext.Upsert(ref key, ref value); + bContext.Read(ref key, ref output); Assert.AreEqual(value, output); - s.RMW(ref key, ref input); - s.RMW(ref key, ref input); - s.Read(ref key, ref output); + bContext.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); + bContext.Read(ref key, ref output); Assert.AreEqual(10, output); } @@ -748,7 +753,8 @@ public static void UshortKeyByteValueTest() { using var log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog.log"), deleteOnClose: false); using var store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log }); - using var s = store.NewSession>(new SimpleFunctions()); + using var s = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = s.BasicContext; ushort key = 1024; byte value = 1, input = 10, output = 0; @@ -760,11 +766,11 @@ public static void UshortKeyByteValueTest() for (var ii = 0; ii < 5; ++ii, ++key, ++value, ++input) { output = 0; - s.Upsert(ref key, ref value); - s.Read(ref key, ref output); + bContext.Upsert(ref key, ref value); + bContext.Read(ref key, ref output); Assert.AreEqual(value, output); - s.RMW(ref key, ref input); - s.Read(ref key, ref output); + bContext.RMW(ref key, ref input); + bContext.Read(ref key, ref output); Assert.AreEqual(input, output); var tailLogicalAddress = store.hlog.GetTailAddress(); @@ -783,7 +789,9 @@ public static void BasicSyncOperationsTest() { using var log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog.log"), deleteOnClose: false); using var store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log }); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; + const int numRecords = 500; const int valueMult = 1_000_000; @@ -795,9 +803,9 @@ public static void BasicSyncOperationsTest() { long value = key + valueMult; hashes[key] = store.comparer.GetHashCode64(ref key); - status = session.Upsert(key, value); + status = bContext.Upsert(key, value); Assert.IsTrue(status.Record.Created, status.ToString()); - status = session.Read(key, out output); + status = bContext.Read(key, out output); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -810,15 +818,15 @@ void doUpdate(bool useRMW) long value = key + valueMult * 2; if (useRMW) { - status = session.RMW(key, value); + status = bContext.RMW(key, value); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } else { - status = session.Upsert(key, value); + status = bContext.Upsert(key, value); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } - status = session.Read(key, out output); + status = bContext.Read(key, out output); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -830,17 +838,17 @@ void doUpdate(bool useRMW) if (useRMW) { RMWOptions rmwOptions = new() { KeyHash = hashes[key] }; - status = session.RMW(key, value, ref rmwOptions); + status = bContext.RMW(key, value, ref rmwOptions); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } else { UpsertOptions upsertOptions = new() { KeyHash = hashes[key] }; - status = session.Upsert(key, value, ref upsertOptions); + status = bContext.Upsert(key, value, ref upsertOptions); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } ReadOptions readOptions = new() { KeyHash = hashes[key] }; - status = session.Read(key, out output, ref readOptions); + status = bContext.Read(key, out output, ref readOptions); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -852,9 +860,9 @@ void doUpdate(bool useRMW) // Delete without keyHash for (long key = 0; key < numRecords; key++) { - status = session.Delete(key); + status = bContext.Delete(key); Assert.IsTrue(status.Found, status.ToString()); - status = session.Read(key, out _); + status = bContext.Read(key, out _); Assert.IsTrue(status.NotFound, status.ToString()); } @@ -862,9 +870,9 @@ void doUpdate(bool useRMW) for (long key = 0; key < numRecords; key++) { DeleteOptions deleteOptions = new() { KeyHash = hashes[key] }; - status = session.Delete(key, ref deleteOptions); + status = bContext.Delete(key, ref deleteOptions); ReadOptions readOptions = new() { KeyHash = hashes[key] }; - status = session.Read(key, out _, ref readOptions); + status = bContext.Read(key, out _, ref readOptions); Assert.IsTrue(status.NotFound, status.ToString()); } } @@ -875,7 +883,9 @@ public static async Task BasicAsyncOperationsTest() { using var log = Devices.CreateLogDevice(Path.Join(MethodTestDir, "hlog.log"), deleteOnClose: false); using var store = new TsavoriteKV(1L << 20, new LogSettings { LogDevice = log }); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; + const int numRecords = 500; const int valueMult = 1_000_000; @@ -887,9 +897,9 @@ public static async Task BasicAsyncOperationsTest() { long value = key + valueMult; hashes[key] = store.comparer.GetHashCode64(ref key); - status = await CompleteAsync(session.UpsertAsync(key, value)); + status = await CompleteAsync(bContext.UpsertAsync(key, value)); Assert.IsTrue(status.Record.Created, status.ToString()); - (status, output) = await CompleteAsync(session.ReadAsync(key)); + (status, output) = await CompleteAsync(bContext.ReadAsync(key)); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -902,15 +912,15 @@ async void doUpdate(bool useRMW) long value = key + valueMult * 2; if (useRMW) { - status = await CompleteAsync(session.RMWAsync(key, value)); + status = await CompleteAsync(bContext.RMWAsync(key, value)); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } else { - status = await CompleteAsync(session.UpsertAsync(key, value)); + status = await CompleteAsync(bContext.UpsertAsync(key, value)); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } - (status, output) = await CompleteAsync(session.ReadAsync(key)); + (status, output) = await CompleteAsync(bContext.ReadAsync(key)); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -922,17 +932,17 @@ async void doUpdate(bool useRMW) if (useRMW) { RMWOptions rmwOptions = new() { KeyHash = hashes[key] }; - status = await CompleteAsync(session.RMWAsync(key, value, ref rmwOptions)); + status = await CompleteAsync(bContext.RMWAsync(key, value, ref rmwOptions)); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } else { UpsertOptions upsertOptions = new() { KeyHash = hashes[key] }; - status = await CompleteAsync(session.UpsertAsync(key, value, ref upsertOptions)); + status = await CompleteAsync(bContext.UpsertAsync(key, value, ref upsertOptions)); Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); } ReadOptions readOptions = new() { KeyHash = hashes[key] }; - (status, output) = await CompleteAsync(session.ReadAsync(key, ref readOptions)); + (status, output) = await CompleteAsync(bContext.ReadAsync(key, ref readOptions)); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value, output); } @@ -944,9 +954,9 @@ async void doUpdate(bool useRMW) // Delete without keyHash for (long key = 0; key < numRecords; key++) { - status = await CompleteAsync(session.DeleteAsync(key)); + status = await CompleteAsync(bContext.DeleteAsync(key)); Assert.IsTrue(status.Found, status.ToString()); - (status, _) = await CompleteAsync(session.ReadAsync(key)); + (status, _) = await CompleteAsync(bContext.ReadAsync(key)); Assert.IsTrue(status.NotFound, status.ToString()); } @@ -954,9 +964,9 @@ async void doUpdate(bool useRMW) for (long key = 0; key < numRecords; key++) { DeleteOptions deleteOptions = new() { KeyHash = hashes[key] }; - status = await CompleteAsync(session.DeleteAsync(key, ref deleteOptions)); + status = await CompleteAsync(bContext.DeleteAsync(key, ref deleteOptions)); ReadOptions readOptions = new() { KeyHash = hashes[key] }; - (status, _) = await CompleteAsync(session.ReadAsync(key, ref readOptions)); + (status, _) = await CompleteAsync(bContext.ReadAsync(key, ref readOptions)); Assert.IsTrue(status.NotFound, status.ToString()); } } diff --git a/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs b/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs index 4b101d4eb0..96123d3902 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableIterationTests.cs @@ -66,6 +66,8 @@ public void BlittableIterationBasicTest([Values] DeviceType deviceType, [Values] concurrencyControlMode: scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable); using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; + BlittablePushIterationTestFunctions scanIteratorFunctions = new(); const int totalRecords = 500; @@ -92,7 +94,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(1, totalRecords); @@ -100,7 +102,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = 2 * i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(2, totalRecords); @@ -108,7 +110,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(0, totalRecords); @@ -116,14 +118,14 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(0, totalRecords); for (int i = 0; i < totalRecords; i += 2) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; - session.Delete(ref key1); + bContext.Delete(ref key1); } iterateAndVerify(0, totalRecords / 2); @@ -131,7 +133,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = 3 * i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(3, totalRecords); @@ -149,6 +151,7 @@ public void BlittableIterationPushStopTest() (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }); using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; BlittablePushIterationTestFunctions scanIteratorFunctions = new(); const int totalRecords = 2000; @@ -170,7 +173,7 @@ void scanAndVerify(int stopAt, bool useScan) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } scanAndVerify(42, useScan: true); @@ -205,24 +208,26 @@ void LocalScan(int i) void LocalUpdate(int tid) { using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; for (var iteration = 0; iteration < 2; ++iteration) { for (int i = 0; i < totalRecords; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = (tid + 1) * i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } } } { // Initial population using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } } diff --git a/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs index 2e20b8a8c0..2ac297a5dd 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableLogCompactionTests.cs @@ -74,10 +74,11 @@ void VerifyRead(ClientSession(new FunctionsCompaction()); + var bContext = session.BasicContext; const int totalRecords = 2_000; var start = store.Log.TailAddress; @@ -141,7 +143,7 @@ public void BlittableLogCompactionTest1([Values] CompactionType compactionType, var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } store.Log.FlushAndEvict(wait: true); @@ -161,6 +163,7 @@ public void BlittableLogCompactionTest2([Values] CompactionType compactionType, [Values(HashModulo.NoMod, HashModulo.Hundred)] HashModulo hashMod) { using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; const int totalRecords = 2_000; var start = store.Log.TailAddress; @@ -173,7 +176,7 @@ public void BlittableLogCompactionTest2([Values] CompactionType compactionType, var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } store.Log.FlushAndEvict(true); @@ -191,7 +194,7 @@ public void BlittableLogCompactionTest2([Values] CompactionType compactionType, { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } compactUntil = session.Compact(compactUntil, compactionType); @@ -208,6 +211,7 @@ public void BlittableLogCompactionTest2([Values] CompactionType compactionType, public void BlittableLogCompactionTest3([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) { using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; const int totalRecords = 2_000; var start = store.Log.TailAddress; @@ -220,13 +224,13 @@ public void BlittableLogCompactionTest3([Values] CompactionType compactionType, var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); if (i % 8 == 0) { int j = i / 4; key1 = new KeyStruct { kfield1 = j, kfield2 = j + 1 }; - session.Delete(ref key1, 0); + bContext.Delete(ref key1, 0); } } @@ -247,6 +251,7 @@ public void BlittableLogCompactionTest3([Values] CompactionType compactionType, public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType compactionType, [Values(ConcurrencyControlMode.LockTable)] ConcurrencyControlMode concurrencyControlMode) { using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; InputStruct input = default; @@ -261,7 +266,7 @@ public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType c var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } var tail = store.Log.TailAddress; @@ -280,10 +285,10 @@ public void BlittableLogCompactionCustomFunctionsTest1([Values] CompactionType c var ctx = (i < (totalRecords / 2) && (i % 2 != 0)) ? 1 : 0; - var status = session.Read(ref key1, ref input, ref output, ctx); + var status = bContext.Read(ref key1, ref input, ref output, ctx); if (status.IsPending) { - Assert.IsTrue(session.CompletePendingWithOutputs(out var outputs, wait: true)); + Assert.IsTrue(bContext.CompletePendingWithOutputs(out var outputs, wait: true)); (status, output) = GetSinglePendingResult(outputs); } @@ -311,21 +316,22 @@ public void BlittableLogCompactionCustomFunctionsTest2([Values] CompactionType c // This test checks if CopyInPlace returning false triggers call to Copy using var session = store.NewSession(new FunctionsCompaction()); + var bContext = session.BasicContext; var key = new KeyStruct { kfield1 = 100, kfield2 = 101 }; var value = new ValueStruct { vfield1 = 10, vfield2 = 20 }; var input = default(InputStruct); var output = default(OutputStruct); - session.Upsert(ref key, ref value, 0); - var status = session.Read(ref key, ref input, ref output, 0); + bContext.Upsert(ref key, ref value, 0); + var status = bContext.Read(ref key, ref input, ref output, 0); Debug.Assert(status.Found); store.Log.Flush(true); value = new ValueStruct { vfield1 = 11, vfield2 = 21 }; - session.Upsert(ref key, ref value, 0); - status = session.Read(ref key, ref input, ref output, 0); + bContext.Upsert(ref key, ref value, 0); + status = bContext.Read(ref key, ref input, ref output, 0); Debug.Assert(status.Found); if (flushAndEvict) @@ -336,10 +342,10 @@ public void BlittableLogCompactionCustomFunctionsTest2([Values] CompactionType c var compactUntil = session.Compact(store.Log.TailAddress, compactionType); store.Log.Truncate(); - status = session.Read(ref key, ref input, ref output, 0); + status = bContext.Read(ref key, ref input, ref output, 0); if (status.IsPending) { - Assert.IsTrue(session.CompletePendingWithOutputs(out var outputs, wait: true)); + Assert.IsTrue(bContext.CompletePendingWithOutputs(out var outputs, wait: true)); (status, output) = GetSinglePendingResult(outputs); } diff --git a/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs b/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs index 1e6d70065e..0ecc1d24be 100644 --- a/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/BlittableLogScanTests.cs @@ -99,6 +99,7 @@ public void OnStop(bool completed, long numberOfRecords) { } public void BlittableDiskWriteScan([Values] ScanIteratorType scanIteratorType) { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; using var s = store.Log.Subscribe(new LogObserver()); var start = store.Log.TailAddress; @@ -107,7 +108,7 @@ public void BlittableDiskWriteScan([Values] ScanIteratorType scanIteratorType) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } store.Log.FlushAndEvict(true); @@ -139,6 +140,7 @@ void scanAndVerify(ScanBufferingMode sbm) public void BlittableScanJumpToBeginAddressTest() { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; const int numRecords = 200; const int numTailRecords = 10; @@ -153,7 +155,7 @@ public void BlittableScanJumpToBeginAddressTest() } var key = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); } using var iter = store.Log.Scan(store.Log.HeadAddress, store.Log.TailAddress); @@ -200,12 +202,13 @@ public void BlittableScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred var recordSize = BlittableAllocator.RecordSize; using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } var scanCursorFuncs = new ScanCursorFuncs(); @@ -243,7 +246,7 @@ public void BlittableScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred { var key1 = new KeyStruct { kfield1 = i + totalRecords, kfield2 = i + totalRecords + 1 }; var value = new ValueStruct { vfield1 = i + totalRecords, vfield2 = i + totalRecords + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } scanCursorFuncs.Initialize(verifyKeys); Assert.IsFalse(session.ScanCursor(ref cursor, long.MaxValue, scanCursorFuncs, long.MaxValue), "Expected scan to finish and return false, pt 1"); @@ -264,7 +267,7 @@ public void BlittableScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred InputStruct input = default; OutputStruct output = default; ReadOptions readOptions = default; - var readStatus = session.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); + var readStatus = bContext.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); Assert.IsTrue(readStatus.Found, $"Could not read at HeadAddress; {readStatus}"); scanCursorFuncs.Initialize(verifyKeys); @@ -286,12 +289,13 @@ public void BlittableScanCursorFilterTest([Values(HashModulo.NoMod, HashModulo.H var recordSize = BlittableAllocator.RecordSize; using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } var scanCursorFuncs = new ScanCursorFuncs(); diff --git a/libs/storage/Tsavorite/cs/test/CancellationTests.cs b/libs/storage/Tsavorite/cs/test/CancellationTests.cs index ecfa4d8cf1..6e6acb84f5 100644 --- a/libs/storage/Tsavorite/cs/test/CancellationTests.cs +++ b/libs/storage/Tsavorite/cs/test/CancellationTests.cs @@ -23,7 +23,7 @@ internal enum CancelLocation ConcurrentWriter } - public class CancellationFunctions : FunctionsBase + public class CancellationFunctions : SessionFunctionsBase { internal CancelLocation cancelLocation = CancelLocation.None; internal CancelLocation lastFunc = CancelLocation.None; @@ -120,6 +120,7 @@ public override bool ConcurrentWriter(ref int key, ref int input, ref int src, r CancellationFunctions functions; TsavoriteKV store; ClientSession session; + BasicContext bContext; const int NumRecs = 100; @@ -136,6 +137,7 @@ public void Setup() functions = new CancellationFunctions(); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -155,7 +157,7 @@ private unsafe void Populate() // Single alloc outside the loop, to the max length we'll need. for (int ii = 0; ii < NumRecs; ii++) { - session.Upsert(ii, ii * NumRecs * 10); + bContext.Upsert(ii, ii * NumRecs * 10); } } @@ -169,12 +171,12 @@ public void InitialUpdaterTest([Values(Phase.REST, Phase.INTERMEDIATE)] Phase ph int key = NumRecs; functions.cancelLocation = CancelLocation.NeedInitialUpdate; - var status = session.RMW(key, key * NumRecs * 10); + var status = bContext.RMW(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.NeedInitialUpdate, functions.lastFunc); functions.cancelLocation = CancelLocation.InitialUpdater; - status = session.RMW(key, key * NumRecs * 10); + status = bContext.RMW(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.InitialUpdater, functions.lastFunc); } @@ -193,12 +195,12 @@ void do_it() for (int lap = 0; lap < 2; ++lap) { functions.cancelLocation = CancelLocation.NeedCopyUpdate; - var status = session.RMW(key, key * NumRecs * 10); + var status = bContext.RMW(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.NeedCopyUpdate, functions.lastFunc); functions.cancelLocation = CancelLocation.CopyUpdater; - status = session.RMW(key, key * NumRecs * 10); + status = bContext.RMW(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.CopyUpdater, functions.lastFunc); } @@ -223,7 +225,7 @@ public void InPlaceUpdaterTest([Values(Phase.REST, Phase.INTERMEDIATE)] Phase ph // Note: ExpirationTests tests the combination of CancelOperation and DeleteRecord functions.cancelLocation = CancelLocation.InPlaceUpdater; - var status = session.RMW(key, key * NumRecs * 10); + var status = bContext.RMW(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.InPlaceUpdater, functions.lastFunc); } @@ -238,7 +240,7 @@ public void SingleWriterTest([Values(Phase.REST, Phase.INTERMEDIATE)] Phase phas int key = NumRecs + 1; functions.cancelLocation = CancelLocation.SingleWriter; - var status = session.Upsert(key, key * NumRecs * 10); + var status = bContext.Upsert(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.SingleWriter, functions.lastFunc); } @@ -253,7 +255,7 @@ public void ConcurrentWriterTest([Values(Phase.REST, Phase.INTERMEDIATE)] Phase int key = NumRecs / 2; functions.cancelLocation = CancelLocation.ConcurrentWriter; - var status = session.Upsert(key, key * NumRecs * 10); + var status = bContext.Upsert(key, key * NumRecs * 10); Assert.IsTrue(status.IsCanceled); Assert.AreEqual(CancelLocation.ConcurrentWriter, functions.lastFunc); } diff --git a/libs/storage/Tsavorite/cs/test/CheckpointManagerTests.cs b/libs/storage/Tsavorite/cs/test/CheckpointManagerTests.cs index 1e39c50f3c..d523ce0fbe 100644 --- a/libs/storage/Tsavorite/cs/test/CheckpointManagerTests.cs +++ b/libs/storage/Tsavorite/cs/test/CheckpointManagerTests.cs @@ -53,7 +53,8 @@ public async Task CheckpointManagerPurgeCheck([Values] DeviceMode deviceMode) }, checkpointSettings: new CheckpointSettings { CheckpointManager = checkpointManager } ); - using var s = store.NewSession>(new SimpleFunctions()); + using var s = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = s.BasicContext; var logCheckpoints = new Dictionary(); var indexCheckpoints = new Dictionary(); @@ -62,7 +63,7 @@ public async Task CheckpointManagerPurgeCheck([Values] DeviceMode deviceMode) for (var i = 0; i < 10; i++) { // Do some dummy update - s.Upsert(0, random.Next()); + bContext.Upsert(0, random.Next()); var checkpointType = random.Next(5); Guid result = default; diff --git a/libs/storage/Tsavorite/cs/test/CompletePendingTests.cs b/libs/storage/Tsavorite/cs/test/CompletePendingTests.cs index 41074f7aad..db8740e18b 100644 --- a/libs/storage/Tsavorite/cs/test/CompletePendingTests.cs +++ b/libs/storage/Tsavorite/cs/test/CompletePendingTests.cs @@ -130,6 +130,7 @@ internal static void VerifyOneNotFound(CompletedOutputIterator>(new FunctionsWithContext()); + var bContext = session.BasicContext; Assert.IsNull(session.completedOutputs); // Do not instantiate until we need it ProcessPending processPending = new(); @@ -139,7 +140,7 @@ public async ValueTask ReadAndCompleteWithPendingOutput([Values] bool useRMW, [V var keyStruct = NewKeyStruct(key); var valueStruct = NewValueStruct(key); processPending.keyAddressDict[key] = store.Log.TailAddress; - session.Upsert(ref keyStruct, ref valueStruct); + bContext.Upsert(ref keyStruct, ref valueStruct); } // Flush to make reads or RMWs go pending. @@ -158,26 +159,26 @@ public async ValueTask ReadAndCompleteWithPendingOutput([Values] bool useRMW, [V { var ksUnfound = keyStruct; ksUnfound.kfield1 += numRecords * 10; - if (session.Read(ref ksUnfound, ref inputStruct, ref outputStruct, contextStruct).IsPending) + if (bContext.Read(ref ksUnfound, ref inputStruct, ref outputStruct, contextStruct).IsPending) { CompletedOutputIterator completedOutputs; if (isAsync) - completedOutputs = await session.CompletePendingWithOutputsAsync(); + completedOutputs = await bContext.CompletePendingWithOutputsAsync(); else - session.CompletePendingWithOutputs(out completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out completedOutputs, wait: true); ProcessPending.VerifyOneNotFound(completedOutputs, ref ksUnfound); } } // We don't use context (though we verify it), and Read does not use input. var status = useRMW - ? session.RMW(ref keyStruct, ref inputStruct, ref outputStruct, contextStruct) - : session.Read(ref keyStruct, ref inputStruct, ref outputStruct, contextStruct); + ? bContext.RMW(ref keyStruct, ref inputStruct, ref outputStruct, contextStruct) + : bContext.Read(ref keyStruct, ref inputStruct, ref outputStruct, contextStruct); if (status.IsPending) { if (processPending.IsFirst()) { - session.CompletePending(wait: true); // Test that this does not instantiate CompletedOutputIterator + bContext.CompletePending(wait: true); // Test that this does not instantiate CompletedOutputIterator Assert.IsNull(session.completedOutputs); // Do not instantiate until we need it continue; } @@ -186,9 +187,9 @@ public async ValueTask ReadAndCompleteWithPendingOutput([Values] bool useRMW, [V { CompletedOutputIterator completedOutputs; if (isAsync) - completedOutputs = await session.CompletePendingWithOutputsAsync(); + completedOutputs = await bContext.CompletePendingWithOutputsAsync(); else - session.CompletePendingWithOutputs(out completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out completedOutputs, wait: true); processPending.Process(completedOutputs, useRMW ? rmwCopyUpdatedAddresses : null); } continue; @@ -210,7 +211,7 @@ public async ValueTask ReadAndCompleteWithPendingOutput([Values] bool useRMW, [V // This should not be pending since we've not flushed. var localKey = key; - var status = session.Read(ref localKey, ref inputStruct, ref outputStruct, ref readOptions, out RecordMetadata recordMetadata); + var status = bContext.Read(ref localKey, ref inputStruct, ref outputStruct, ref readOptions, out RecordMetadata recordMetadata); Assert.IsFalse(status.IsPending); Assert.AreEqual(address, recordMetadata.Address); } diff --git a/libs/storage/Tsavorite/cs/test/DisposeTests.cs b/libs/storage/Tsavorite/cs/test/DisposeTests.cs index 21a4d8c775..f38bf644d9 100644 --- a/libs/storage/Tsavorite/cs/test/DisposeTests.cs +++ b/libs/storage/Tsavorite/cs/test/DisposeTests.cs @@ -118,7 +118,7 @@ internal DisposeFunctions(DisposeTests tester, bool sut, bool splice = false) void WaitForEvent() { - Assert.IsTrue(tester.store.epoch.ThisInstanceProtected(), "This should only be called from IFunctions methods, which are under epoch protection"); + 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 }; diff --git a/libs/storage/Tsavorite/cs/test/ExpirationTests.cs b/libs/storage/Tsavorite/cs/test/ExpirationTests.cs index 90e0398660..dcc808a273 100644 --- a/libs/storage/Tsavorite/cs/test/ExpirationTests.cs +++ b/libs/storage/Tsavorite/cs/test/ExpirationTests.cs @@ -44,7 +44,7 @@ internal enum ExpirationResult None, // Default value Incremented, // Initial increment was done ExpireDelete, // Record was expired so deleted - ExpireRollover, // Record was expired and reinitialized within the IFunctions call + ExpireRollover, // Record was expired and reinitialized within the ISessionFunctions call Updated, // Record was updated normally NotUpdated, // Record was not updated Deleted, // Record was expired with AndStop (no reinitialization done) @@ -152,7 +152,7 @@ internal enum TestOp #pragma warning restore format }; - public class ExpirationFunctions : FunctionsBase + public class ExpirationFunctions : SessionFunctionsBase { private static unsafe void VerifyValue(int key, ref SpanByte valueSpanByte) { @@ -515,6 +515,7 @@ public override bool ConcurrentWriter(ref SpanByte key, ref ExpirationInput inpu ExpirationFunctions functions; TsavoriteKV store; ClientSession session; + BasicContext bContext; [SetUp] public void Setup() @@ -529,6 +530,7 @@ public void Setup() functions = new ExpirationFunctions(); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -559,7 +561,7 @@ private unsafe void Populate(Random rng) valueSpan[j] = GetValue(i); var valueSpanByte = valueSpan.AsSpanByte(); - session.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); + bContext.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); } } @@ -570,11 +572,11 @@ private unsafe ExpirationOutput GetRecord(int key, Status expectedStatus, FlushM var keySpanByte = keySpan.AsSpanByte(); ExpirationOutput output = new(); - var status = session.Read(ref keySpanByte, ref output, Empty.Default); + var status = bContext.Read(ref keySpanByte, ref output, Empty.Default); if (status.IsPending) { Assert.AreNotEqual(FlushMode.NoFlush, flushMode); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); } @@ -589,11 +591,11 @@ private unsafe ExpirationOutput ExecuteRMW(int key, ref ExpirationInput input, F var keySpanByte = keySpan.AsSpanByte(); ExpirationOutput output = new(); - var status = session.RMW(ref keySpanByte, ref input, ref output); + var status = bContext.RMW(ref keySpanByte, ref input, ref output); if (status.IsPending) { Assert.AreNotEqual(FlushMode.NoFlush, flushMode); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); } diff --git a/libs/storage/Tsavorite/cs/test/FunctionPerSessionTests.cs b/libs/storage/Tsavorite/cs/test/FunctionPerSessionTests.cs index 0d06eaabf4..88a25b7e10 100644 --- a/libs/storage/Tsavorite/cs/test/FunctionPerSessionTests.cs +++ b/libs/storage/Tsavorite/cs/test/FunctionPerSessionTests.cs @@ -15,7 +15,7 @@ public struct RefCountedValue public long Value; } - public class RefCountedAdder : FunctionsBase + public class RefCountedAdder : SessionFunctionsBase { public int InitialCount; public int InPlaceCount; @@ -49,7 +49,7 @@ public override bool CopyUpdater(ref int key, ref long input, ref RefCountedValu } } - public class RefCountedRemover : FunctionsBase + public class RefCountedRemover : SessionFunctionsBase { public int InitialCount; public int InPlaceCount; @@ -86,7 +86,7 @@ public override bool CopyUpdater(ref int key, ref Empty input, ref RefCountedVal } } - public class RefCountedReader : FunctionsBase + public class RefCountedReader : SessionFunctionsBase { public override bool SingleReader(ref int key, ref Empty input, ref RefCountedValue value, ref RefCountedValue dst, ref ReadInfo readInfo) { @@ -146,19 +146,19 @@ public async Task Should_create_multiple_sessions_with_different_callbacks() var key = 101; var input = 1000L; - (await adderSession.RMWAsync(ref key, ref input)).Complete(); - (await adderSession.RMWAsync(ref key, ref input)).Complete(); - (await adderSession.RMWAsync(ref key, ref input)).Complete(); + (await adderSession.BasicContext.RMWAsync(ref key, ref input)).Complete(); + (await adderSession.BasicContext.RMWAsync(ref key, ref input)).Complete(); + (await adderSession.BasicContext.RMWAsync(ref key, ref input)).Complete(); Assert.AreEqual(1, _adder.InitialCount); Assert.AreEqual(2, _adder.InPlaceCount); var empty = default(Empty); - (await removerSession.RMWAsync(ref key, ref empty)).Complete(); + (await removerSession.BasicContext.RMWAsync(ref key, ref empty)).Complete(); Assert.AreEqual(1, _remover.InPlaceCount); - var read = await readerSession.ReadAsync(ref key, ref empty); + var read = await readerSession.BasicContext.ReadAsync(ref key, ref empty); var result = read.Complete(); var actual = result.output; @@ -167,8 +167,8 @@ public async Task Should_create_multiple_sessions_with_different_callbacks() _tsavorite.Log.FlushAndEvict(true); - (await removerSession.RMWAsync(ref key, ref empty)).Complete(); - read = await readerSession.ReadAsync(ref key, ref empty); + (await removerSession.BasicContext.RMWAsync(ref key, ref empty)).Complete(); + read = await readerSession.BasicContext.ReadAsync(ref key, ref empty); result = read.Complete(); actual = result.output; diff --git a/libs/storage/Tsavorite/cs/test/GenericByteArrayTests.cs b/libs/storage/Tsavorite/cs/test/GenericByteArrayTests.cs index e7bc94bc82..3d4f15c1d4 100644 --- a/libs/storage/Tsavorite/cs/test/GenericByteArrayTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericByteArrayTests.cs @@ -15,6 +15,7 @@ internal class GenericByteArrayTests { private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log, objlog; [SetUp] @@ -31,6 +32,7 @@ public void Setup() ); session = store.NewSession(new MyByteArrayFuncs()); + bContext = session.BasicContext; } [TearDown] @@ -63,9 +65,9 @@ public void ByteArrayBasicTest() { var _key = GetByteArray(i); var _value = GetByteArray(i); - session.Upsert(ref _key, ref _value, Empty.Default); + bContext.Upsert(ref _key, ref _value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); for (int i = 0; i < totalRecords; i++) { @@ -74,9 +76,9 @@ public void ByteArrayBasicTest() var key = GetByteArray(i); var value = GetByteArray(i); - if (session.Read(ref key, ref input, ref output, Empty.Default).IsPending) + if (bContext.Read(ref key, ref input, ref output, Empty.Default).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -85,7 +87,7 @@ public void ByteArrayBasicTest() } } - class MyByteArrayFuncs : SimpleFunctions + class MyByteArrayFuncs : SimpleSimpleFunctions { public override void ReadCompletionCallback(ref byte[] key, ref byte[] input, ref byte[] output, Empty ctx, Status status, RecordMetadata recordMetadata) { diff --git a/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs b/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs index 987b17b7cc..6639f87911 100644 --- a/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericDiskDeleteTests.cs @@ -13,6 +13,7 @@ internal class GenericDiskDeleteTests { private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log, objlog; [SetUp] @@ -28,6 +29,7 @@ public void Setup() serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, concurrencyControlMode: ConcurrencyControlMode.None); session = store.NewSession(new MyFunctionsDelete()); + bContext = session.BasicContext; } [TearDown] @@ -57,7 +59,7 @@ public void DiskDeleteBasicTest1() { var _key = new MyKey { key = i }; var _value = new MyValue { value = i }; - session.Upsert(ref _key, ref _value, 0); + bContext.Upsert(ref _key, ref _value, 0); } for (int i = 0; i < totalRecords; i++) @@ -67,9 +69,9 @@ public void DiskDeleteBasicTest1() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - if (session.Read(ref key1, ref input, ref output, 0).IsPending) + if (bContext.Read(ref key1, ref input, ref output, 0).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -80,7 +82,7 @@ public void DiskDeleteBasicTest1() for (int i = 0; i < totalRecords; i++) { var key1 = new MyKey { key = i }; - session.Delete(ref key1); + bContext.Delete(ref key1); } for (int i = 0; i < totalRecords; i++) @@ -89,11 +91,11 @@ public void DiskDeleteBasicTest1() var output = new MyOutput(); var key1 = new MyKey { key = i }; - var status = session.Read(ref key1, ref input, ref output, 1); + var status = bContext.Read(ref key1, ref input, ref output, 1); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, _) = GetSinglePendingResult(outputs); } Assert.IsFalse(status.Found); @@ -120,58 +122,58 @@ public void DiskDeleteBasicTest2() { var _key = new MyKey { key = i }; var _value = new MyValue { value = i }; - session.Upsert(ref _key, ref _value, 0); + bContext.Upsert(ref _key, ref _value, 0); } var key100 = new MyKey { key = 100 }; var value100 = new MyValue { value = 100 }; var key200 = new MyKey { key = 200 }; - session.Delete(ref key100); + bContext.Delete(ref key100); var input = new MyInput { value = 1000 }; var output = new MyOutput(); - var status = session.Read(ref key100, ref input, ref output, 1); + var status = bContext.Read(ref key100, ref input, ref output, 1); Assert.IsFalse(status.Found, status.ToString()); - status = session.Upsert(ref key100, ref value100, 0); + status = bContext.Upsert(ref key100, ref value100, 0); Assert.IsTrue(!status.Found, status.ToString()); - status = session.Read(ref key100, ref input, ref output, 0); + status = bContext.Read(ref key100, ref input, ref output, 0); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(value100.value, output.value.value); - session.Delete(ref key100); - session.Delete(ref key200); + bContext.Delete(ref key100); + bContext.Delete(ref key200); // This RMW should create new initial value, since item is deleted - status = session.RMW(ref key200, ref input, 1); + status = bContext.RMW(ref key200, ref input, 1); Assert.IsFalse(status.Found); - status = session.Read(ref key200, ref input, ref output, 0); + status = bContext.Read(ref key200, ref input, ref output, 0); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(input.value, output.value.value); // Delete key 200 again - session.Delete(ref key200); + bContext.Delete(ref key200); // Eliminate all records from memory for (int i = 201; i < 2000; i++) { var _key = new MyKey { key = i }; var _value = new MyValue { value = i }; - session.Upsert(ref _key, ref _value, 0); + bContext.Upsert(ref _key, ref _value, 0); } - status = session.Read(ref key100, ref input, ref output, 1); + status = bContext.Read(ref key100, ref input, ref output, 1); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); // This RMW should create new initial value, since item is deleted - status = session.RMW(ref key200, ref input, 1); + status = bContext.RMW(ref key200, ref input, 1); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); - status = session.Read(ref key200, ref input, ref output, 0); + status = bContext.Read(ref key200, ref input, ref output, 0); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(input.value, output.value.value); } diff --git a/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs b/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs index 8d2a749572..c5cdfc94a4 100644 --- a/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericIterationTests.cs @@ -16,6 +16,7 @@ internal class GenericIterationTests { private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log, objlog; [SetUp] @@ -44,6 +45,7 @@ private void InternalSetup(ConcurrencyControlMode concurrencyControlMode, bool l serializerSettings: new SerializerSettings { keySerializer = () => new MyKeySerializer(), valueSerializer = () => new MyValueSerializer() }, concurrencyControlMode: concurrencyControlMode); session = store.NewSession(new MyFunctionsDelete()); + bContext = session.BasicContext; } [TearDown] @@ -115,7 +117,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(1, totalRecords); @@ -123,7 +125,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new MyKey { key = i }; var value = new MyValue { value = 2 * i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(2, totalRecords); @@ -131,7 +133,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(0, totalRecords); @@ -139,14 +141,14 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(0, totalRecords); for (int i = 0; i < totalRecords; i += 2) { var key1 = new MyKey { key = i }; - session.Delete(ref key1); + bContext.Delete(ref key1); } iterateAndVerify(0, totalRecords / 2); @@ -154,7 +156,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { var key1 = new MyKey { key = i }; var value = new MyValue { value = 3 * i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } iterateAndVerify(3, totalRecords); @@ -190,7 +192,7 @@ void scanAndVerify(int stopAt, bool useScan) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } scanAndVerify(42, useScan: true); @@ -226,7 +228,7 @@ void LocalUpdate(int tid) { var key1 = new MyKey { key = i }; var value = new MyValue { value = (tid + 1) * i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } } @@ -235,7 +237,7 @@ void LocalUpdate(int tid) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } } diff --git a/libs/storage/Tsavorite/cs/test/GenericLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/GenericLogCompactionTests.cs index 0b620385e7..bde16a559f 100644 --- a/libs/storage/Tsavorite/cs/test/GenericLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericLogCompactionTests.cs @@ -13,6 +13,7 @@ internal class GenericLogCompactionTests { private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log, objlog; [SetUp] @@ -49,6 +50,7 @@ public void Setup() ); } session = store.NewSession(new MyFunctionsDelete()); + bContext = session.BasicContext; } [TearDown] @@ -84,7 +86,7 @@ public void LogCompactBasicTest([Values] CompactionType compactionType) var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } compactUntil = session.Compact(compactUntil, compactionType); @@ -99,10 +101,10 @@ public void LogCompactBasicTest([Values] CompactionType compactionType) var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, 0); + var status = bContext.Read(ref key1, ref input, ref output, 0); if (status.IsPending) { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); Assert.IsTrue(completedOutputs.Next()); Assert.IsTrue(completedOutputs.Current.Status.Found); output = completedOutputs.Current.Output; @@ -131,7 +133,7 @@ public void LogCompactTestNewEntries([Values] CompactionType compactionType) var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } // Put fresh entries for 1000 records @@ -139,7 +141,7 @@ public void LogCompactTestNewEntries([Values] CompactionType compactionType) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } store.Log.Flush(true); @@ -157,9 +159,9 @@ public void LogCompactTestNewEntries([Values] CompactionType compactionType) var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, 0); + var status = bContext.Read(ref key1, ref input, ref output, 0); if (status.IsPending) - session.CompletePending(true); + bContext.CompletePending(true); else { Assert.IsTrue(status.Found); @@ -186,13 +188,13 @@ public void LogCompactAfterDeleteTest([Values] CompactionType compactionType) var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); if (i % 8 == 0) { int j = i / 4; key1 = new MyKey { key = j }; - session.Delete(ref key1); + bContext.Delete(ref key1); } } @@ -209,9 +211,9 @@ public void LogCompactAfterDeleteTest([Values] CompactionType compactionType) int ctx = ((i < 500) && (i % 2 == 0)) ? 1 : 0; - var status = session.Read(ref key1, ref input, ref output, ctx); + var status = bContext.Read(ref key1, ref input, ref output, ctx); if (status.IsPending) - session.CompletePending(true); + bContext.CompletePending(true); else { if (ctx == 0) @@ -245,7 +247,7 @@ public void LogCompactBasicCustomFctnTest([Values] CompactionType compactionType var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value, 0); + bContext.Upsert(ref key1, ref value, 0); } compactUntil = session.Compact(compactUntil, compactionType, default(EvenCompactionFunctions)); @@ -261,10 +263,10 @@ public void LogCompactBasicCustomFctnTest([Values] CompactionType compactionType var ctx = (i < (totalRecords / 2) && (i % 2 != 0)) ? 1 : 0; - var status = session.Read(ref key1, ref input, ref output, ctx); + var status = bContext.Read(ref key1, ref input, ref output, ctx); if (status.IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -295,12 +297,12 @@ public void LogCompactCopyInPlaceCustomFctnTest([Values] CompactionType compacti var key = new MyKey { key = 100 }; var value = new MyValue { value = 20 }; - session.Upsert(ref key, ref value, 0); + bContext.Upsert(ref key, ref value, 0); store.Log.Flush(true); value = new MyValue { value = 21 }; - session.Upsert(ref key, ref value, 0); + bContext.Upsert(ref key, ref value, 0); store.Log.Flush(true); @@ -310,10 +312,10 @@ public void LogCompactCopyInPlaceCustomFctnTest([Values] CompactionType compacti var input = default(MyInput); var output = default(MyOutput); - var status = session.Read(ref key, ref input, ref output); + var status = bContext.Read(ref key, ref input, ref output); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } Assert.IsTrue(status.Found); diff --git a/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs b/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs index 81585c95db..fc746b43b5 100644 --- a/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericLogScanTests.cs @@ -102,6 +102,8 @@ public void DiskWriteScanBasicTest([Values] DeviceType deviceType, [Values] Scan ); using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; + using var s = store.Log.Subscribe(new LogObserver()); var start = store.Log.TailAddress; @@ -109,7 +111,7 @@ public void DiskWriteScanBasicTest([Values] DeviceType deviceType, [Values] Scan { var _key = new MyKey { key = i }; var _value = new MyValue { value = i }; - session.Upsert(ref _key, ref _value, Empty.Default); + bContext.Upsert(ref _key, ref _value, Empty.Default); if (i % 100 == 0) store.Log.FlushAndEvict(true); } @@ -175,6 +177,7 @@ public void BlittableScanJumpToBeginAddressTest() concurrencyControlMode: ConcurrencyControlMode.None); using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; const int numRecords = 200; const int numTailRecords = 10; @@ -189,7 +192,7 @@ public void BlittableScanJumpToBeginAddressTest() } var key = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); } using var iter = store.Log.Scan(store.Log.HeadAddress, store.Log.TailAddress); @@ -244,12 +247,13 @@ public void GenericScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred)] concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } var scanCursorFuncs = new ScanCursorFuncs(); @@ -287,7 +291,7 @@ public void GenericScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred)] { var key1 = new MyKey { key = i + totalRecords }; var value = new MyValue { value = i + totalRecords }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } scanCursorFuncs.Initialize(verifyKeys); Assert.IsFalse(session.ScanCursor(ref cursor, long.MaxValue, scanCursorFuncs, long.MaxValue), "Expected scan to finish and return false, pt 1"); @@ -308,7 +312,7 @@ public void GenericScanCursorTest([Values(HashModulo.NoMod, HashModulo.Hundred)] MyInput input = new(); MyOutput output = new(); ReadOptions readOptions = default; - var readStatus = session.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); + var readStatus = bContext.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); Assert.IsTrue(readStatus.Found, $"Could not read at HeadAddress; {readStatus}"); scanCursorFuncs.Initialize(verifyKeys); @@ -335,12 +339,13 @@ public void GenericScanCursorFilterTest([Values(HashModulo.NoMod, HashModulo.Hun concurrencyControlMode: ConcurrencyControlMode.None, comparer: comparer); using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key1, ref value); + bContext.Upsert(ref key1, ref value); } var scanCursorFuncs = new ScanCursorFuncs(); diff --git a/libs/storage/Tsavorite/cs/test/GenericStringTests.cs b/libs/storage/Tsavorite/cs/test/GenericStringTests.cs index 48563015ad..2ba8947ba7 100644 --- a/libs/storage/Tsavorite/cs/test/GenericStringTests.cs +++ b/libs/storage/Tsavorite/cs/test/GenericStringTests.cs @@ -54,15 +54,16 @@ public void StringBasicTest([Values] DeviceType deviceType) ); session = store.NewSession(new MyFuncs()); + var bContext = session.BasicContext; const int totalRecords = 200; for (int i = 0; i < totalRecords; i++) { var _key = $"{i}"; var _value = $"{i}"; ; - session.Upsert(ref _key, ref _value, Empty.Default); + bContext.Upsert(ref _key, ref _value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); Assert.AreEqual(totalRecords, store.EntryCount); for (int i = 0; i < totalRecords; i++) @@ -72,10 +73,10 @@ public void StringBasicTest([Values] DeviceType deviceType) var key = $"{i}"; var value = $"{i}"; - var status = session.Read(ref key, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } Assert.IsTrue(status.Found); @@ -83,7 +84,7 @@ public void StringBasicTest([Values] DeviceType deviceType) } } - class MyFuncs : SimpleFunctions + class MyFuncs : SimpleSimpleFunctions { public override void ReadCompletionCallback(ref string key, ref string input, ref string output, Empty ctx, Status status, RecordMetadata recordMetadata) { diff --git a/libs/storage/Tsavorite/cs/test/InputOutputParameterTests.cs b/libs/storage/Tsavorite/cs/test/InputOutputParameterTests.cs index a9a782f38c..adc964ff5d 100644 --- a/libs/storage/Tsavorite/cs/test/InputOutputParameterTests.cs +++ b/libs/storage/Tsavorite/cs/test/InputOutputParameterTests.cs @@ -17,9 +17,10 @@ class InputOutputParameterTests private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; - internal class UpsertInputFunctions : FunctionsBase + internal class UpsertInputFunctions : SessionFunctionsBase { internal long lastWriteAddress; @@ -84,6 +85,7 @@ public void Setup() store = new TsavoriteKV (128, new LogSettings { LogDevice = log, MemorySizeBits = 22, SegmentSizeBits = 22, PageSizeBits = 10 }); session = store.NewSession(new UpsertInputFunctions()); + bContext = session.BasicContext; } [TearDown] @@ -119,7 +121,7 @@ async Task doWrites() { if (useRMW) { - var r = await session.RMWAsync(ref key, ref input); + var r = await bContext.RMWAsync(ref key, ref input); if ((key & 0x1) == 0) { while (r.Status.IsPending) @@ -135,7 +137,7 @@ async Task doWrites() } else { - var r = await session.UpsertAsync(ref key, ref input, ref key); + var r = await bContext.UpsertAsync(ref key, ref input, ref key); if ((key & 0x1) == 0) { while (r.Status.IsPending) @@ -153,8 +155,8 @@ async Task doWrites() else { status = useRMW - ? session.RMW(ref key, ref input, ref output, out recordMetadata) - : session.Upsert(ref key, ref input, ref key, ref output, out recordMetadata); + ? bContext.RMW(ref key, ref input, ref output, out recordMetadata) + : bContext.Upsert(ref key, ref input, ref key, ref output, out recordMetadata); } if (loading) { @@ -176,7 +178,7 @@ void doReads() { for (int key = 0; key < NumRecs; ++key) { - session.Read(ref key, ref input, ref output); + bContext.Read(ref key, ref input, ref output); Assert.AreEqual(key * input + AddValue, output); } } diff --git a/libs/storage/Tsavorite/cs/test/LargeObjectTests.cs b/libs/storage/Tsavorite/cs/test/LargeObjectTests.cs index 0270d54423..20f6123ae9 100644 --- a/libs/storage/Tsavorite/cs/test/LargeObjectTests.cs +++ b/libs/storage/Tsavorite/cs/test/LargeObjectTests.cs @@ -49,12 +49,14 @@ public void LargeObjectTest(CheckpointType checkpointType) int numOps = 5000; var session1 = store1.NewSession(new MyLargeFunctions()); + var bContext1 = session1.BasicContext; + Random r = new Random(33); for (int key = 0; key < numOps; key++) { var mykey = new MyKey { key = key }; var value = new MyLargeValue(1 + r.Next(maxSize)); - session1.Upsert(ref mykey, ref value, Empty.Default); + bContext1.Upsert(ref mykey, ref value, Empty.Default); } session1.Dispose(); @@ -77,13 +79,15 @@ public void LargeObjectTest(CheckpointType checkpointType) store2.Recover(token); var session2 = store2.NewSession(new MyLargeFunctions()); + var bContext2 = session2.BasicContext; + for (int keycnt = 0; keycnt < numOps; keycnt++) { var key = new MyKey { key = keycnt }; - var status = session2.Read(ref key, ref input, ref output, Empty.Default); + var status = bContext2.Read(ref key, ref input, ref output, Empty.Default); if (status.IsPending) - session2.CompletePending(true); + bContext2.CompletePending(true); else { for (int i = 0; i < output.value.value.Length; i++) diff --git a/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs b/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs index fe0b1dc4bd..85e6075719 100644 --- a/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs +++ b/libs/storage/Tsavorite/cs/test/LockableUnsafeContextTests.cs @@ -19,7 +19,7 @@ namespace Tsavorite.test.LockableUnsafeContext // Functions for the "Simple lock transaction" case, e.g.: // - Lock key1, key2, key3, keyResult // - Do some operation on value1, value2, value3 and write the result to valueResult - internal class LockableUnsafeFunctions : SimpleFunctions + internal class LockableUnsafeFunctions : SimpleSimpleFunctions { internal long recordAddress; @@ -147,6 +147,7 @@ class LockableUnsafeContextTests private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; [SetUp] @@ -184,6 +185,7 @@ public void Setup(bool forRecovery) checkpointSettings: checkpointSettings, comparer: comparer, concurrencyControlMode: ConcurrencyControlMode.LockTable); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -207,7 +209,7 @@ public void TearDown(bool forRecovery) void Populate() { for (int key = 0; key < numRecords; key++) - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); } void AssertIsLocked(FixedLengthLockableKeyStruct key, bool xlock, bool slock) @@ -233,7 +235,7 @@ static void ClearCountsOnError(ClientSession(ClientSession luContext) - where TFunctions : IFunctions + where TFunctions : ISessionFunctions { // If we already have an exception, clear these counts so "Run" will not report them spuriously. luContext.sharedLockCount = 0; @@ -599,8 +601,8 @@ public void InMemorySimpleLockTxnTest([Values] ResultLockTarget resultLockTarget luContext.EndUnsafe(); } - // Verify reading the destination from the full session. - status = session.Read(resultKey, out resultValue); + // Verify reading the destination from the BasicContext. + status = bContext.Read(resultKey, out resultValue); Assert.IsFalse(status.IsPending, status.ToString()); Assert.AreEqual(expectedResult, resultValue); AssertTotalLockCounts(0, 0); @@ -728,8 +730,8 @@ public void InMemoryLongLockTest([Values] ResultLockTarget resultLockTarget, [Va luContext.EndUnsafe(); } - // Verify from the full session. - status = session.Read(resultKey, out resultValue); + // Verify from the full Basic Context + status = bContext.Read(resultKey, out resultValue); Assert.IsFalse(status.IsPending, status.ToString()); Assert.AreEqual(expectedResult, resultValue); AssertTotalLockCounts(0, 0); @@ -793,8 +795,8 @@ public void InMemoryDeleteTest([Values] ResultLockTarget resultLockTarget, [Valu luContext.EndUnsafe(); } - // Verify reading the destination from the full session. - status = session.Read(resultKey, out var _); + // Verify reading the destination from the full Basic Context + status = bContext.Read(resultKey, out var _); Assert.IsFalse(status.Found, status.ToString()); AssertTotalLockCounts(0, 0); } @@ -889,7 +891,7 @@ void runLTransientLockOpThread(int tid) } FixedLengthLockableKeyStruct AddLockTableEntry(LockableUnsafeContext luContext, long key) - where TFunctions : IFunctions + where TFunctions : ISessionFunctions { var keyVec = new[] { new FixedLengthLockableKeyStruct(key, LockType.Exclusive, luContext) }; luContext.Lock(keyVec); @@ -905,7 +907,7 @@ FixedLengthLockableKeyStruct AddLockTableEntry(LockableUnsafeC } void VerifyAndUnlockSplicedInKey(LockableUnsafeContext luContext, long expectedKey) - where TFunctions : IFunctions + where TFunctions : ISessionFunctions { // Scan to the end of the readcache chain and verify we inserted the value. var (_, pa) = ChainTests.SkipReadCacheChain(store, expectedKey); @@ -924,7 +926,7 @@ public void VerifyLocksAfterReadAndCTTTest() Populate(); store.Log.FlushAndEvict(wait: true); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; long input = 0, output = 0, key = 24; ReadOptions readOptions = new() { CopyOptions = new(ReadCopyFrom.AllImmutable, ReadCopyTo.MainLog) }; @@ -965,7 +967,7 @@ public void VerifyCountsAfterFlushAndEvict() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; BucketLockTracker blt = new(); long key = 24; @@ -1017,7 +1019,7 @@ public void VerifyCountAfterUpsertToTailTest([Values] ChainTests.RecordRegion re { PopulateAndEvict(recordRegion == ChainTests.RecordRegion.Immutable); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; BucketLockTracker blt = new(); luContext.BeginUnsafe(); @@ -1057,7 +1059,7 @@ public void VerifyCountAfterRMWToTailTest([Values] ChainTests.RecordRegion recor { PopulateAndEvict(recordRegion == ChainTests.RecordRegion.Immutable); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; BucketLockTracker blt = new(); luContext.BeginUnsafe(); @@ -1104,7 +1106,7 @@ public void VerifyCountAfterDeleteToTailTest([Values] ChainTests.RecordRegion re { PopulateAndEvict(recordRegion == ChainTests.RecordRegion.Immutable); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; BucketLockTracker blt = new(); luContext.BeginUnsafe(); @@ -1152,7 +1154,7 @@ public void VerifyCountAfterDeleteToTailTest([Values] ChainTests.RecordRegion re public void LockAndUnlockInLockTableOnlyTest() { // For this, just don't load anything, and it will happen in lock table. - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; BucketLockTracker blt = new(); @@ -1257,7 +1259,7 @@ public void LockNewRecordThenUpdateAndUnlockTest([Values] UpdateOp updateOp) { const int numNewRecords = 100; - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; int getValue(int key) => key + valueMult; @@ -1266,7 +1268,7 @@ public void LockNewRecordThenUpdateAndUnlockTest([Values] UpdateOp updateOp) if (updateOp == UpdateOp.Delete) { for (var key = numRecords; key < numRecords + numNewRecords; ++key) - Assert.IsFalse(this.session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(this.bContext.Upsert(key, key * valueMult).IsPending); store.Log.FlushAndEvict(wait: true); } @@ -1359,10 +1361,10 @@ public void LockNewRecordThenUnlockThenUpdateTest([Values] UpdateOp updateOp) const int numNewRecords = 50; - using var lockSession = store.NewSession>(new SimpleFunctions()); + using var lockSession = store.NewSession>(new SimpleSimpleFunctions()); var lockLuContext = lockSession.LockableUnsafeContext; - using var updateSession = store.NewSession>(new SimpleFunctions()); + using var updateSession = store.NewSession>(new SimpleSimpleFunctions()); var basicContext = updateSession.BasicContext; int getValue(int key) => key + valueMult; @@ -1371,7 +1373,7 @@ public void LockNewRecordThenUnlockThenUpdateTest([Values] UpdateOp updateOp) if (updateOp == UpdateOp.Delete) { for (var key = numRecords; key < numRecords + numNewRecords; ++key) - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); store.Log.FlushAndEvict(wait: true); } @@ -1498,7 +1500,7 @@ public void MultiSharedLockTest() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; const int key = 42; @@ -1545,7 +1547,7 @@ public void TryLockTimeSpanLimitTest() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); @@ -1602,7 +1604,7 @@ public void TryLockCancellationTest() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); @@ -1661,7 +1663,7 @@ public void TryPromoteLockTimeSpanLimitTest() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); @@ -1704,7 +1706,7 @@ public void TryPromoteLockCancellationTest() { Populate(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); diff --git a/libs/storage/Tsavorite/cs/test/LowMemAsyncTests.cs b/libs/storage/Tsavorite/cs/test/LowMemAsyncTests.cs index d7f172d455..108ee4b3c4 100644 --- a/libs/storage/Tsavorite/cs/test/LowMemAsyncTests.cs +++ b/libs/storage/Tsavorite/cs/test/LowMemAsyncTests.cs @@ -38,12 +38,13 @@ public void TearDown() TestUtils.DeleteDirectory(TestUtils.MethodTestDir); } - private static async Task Populate(ClientSession> s1) + private static async Task Populate(ClientSession> s1) { + var bContext1 = s1.BasicContext; var tasks = new ValueTask.UpsertAsyncResult>[numOps]; for (long key = 0; key < numOps; key++) { - tasks[key] = s1.UpsertAsync(ref key, ref key); + tasks[key] = bContext1.UpsertAsync(ref key, ref key); } for (var done = false; !done; /* set in loop */) @@ -61,7 +62,7 @@ private static async Task Populate(ClientSession>(new SimpleFunctions((a, b) => a + b)); + using var s1 = store1.NewSession>(new SimpleSimpleFunctions((a, b) => a + b)); + var bContext1 = s1.BasicContext; await Populate(s1).ConfigureAwait(false); // Read all keys var readtasks = new ValueTask.ReadAsyncResult>[numOps]; for (long key = 0; key < numOps; key++) - readtasks[key] = s1.ReadAsync(ref key, ref key); + readtasks[key] = bContext1.ReadAsync(ref key, ref key); for (long key = 0; key < numOps; key++) { @@ -93,14 +95,15 @@ public async Task LowMemConcurrentUpsertReadAsyncTest() public async Task LowMemConcurrentUpsertRMWReadAsyncTest([Values] bool completeSync) { await Task.Yield(); - using var s1 = store1.NewSession>(new SimpleFunctions((a, b) => a + b)); + using var s1 = store1.NewSession>(new SimpleSimpleFunctions((a, b) => a + b)); + var bContext1 = s1.BasicContext; await Populate(s1).ConfigureAwait(false); // RMW all keys var rmwtasks = new ValueTask.RmwAsyncResult>[numOps]; for (long key = 0; key < numOps; key++) - rmwtasks[key] = s1.RMWAsync(ref key, ref key); + rmwtasks[key] = bContext1.RMWAsync(ref key, ref key); for (var done = false; !done; /* set in loop */) { @@ -124,7 +127,7 @@ public async Task LowMemConcurrentUpsertRMWReadAsyncTest([Values] bool completeS // Then Read all keys var readtasks = new ValueTask.ReadAsyncResult>[numOps]; for (long key = 0; key < numOps; key++) - readtasks[key] = s1.ReadAsync(ref key, ref key); + readtasks[key] = bContext1.ReadAsync(ref key, ref key); for (long key = 0; key < numOps; key++) { diff --git a/libs/storage/Tsavorite/cs/test/MiscTests.cs b/libs/storage/Tsavorite/cs/test/MiscTests.cs index 5153ae41df..cf7361559e 100644 --- a/libs/storage/Tsavorite/cs/test/MiscTests.cs +++ b/libs/storage/Tsavorite/cs/test/MiscTests.cs @@ -47,21 +47,22 @@ public void TearDown() public void MixedTest1() { using var session = store.NewSession(new MixedFunctions()); + var bContext = session.BasicContext; int key = 8999998; var input1 = new MyInput { value = 23 }; MyOutput output = new(); - session.RMW(ref key, ref input1, Empty.Default); + bContext.RMW(ref key, ref input1, Empty.Default); int key2 = 8999999; var input2 = new MyInput { value = 24 }; - session.RMW(ref key2, ref input2, Empty.Default); + bContext.RMW(ref key2, ref input2, Empty.Default); - session.Read(ref key, ref input1, ref output, Empty.Default); + bContext.Read(ref key, ref input1, ref output, Empty.Default); Assert.AreEqual(input1.value, output.value.value); - session.Read(ref key2, ref input2, ref output, Empty.Default); + bContext.Read(ref key2, ref input2, ref output, Empty.Default); Assert.AreEqual(input2.value, output.value.value); } @@ -70,21 +71,22 @@ public void MixedTest1() public void MixedTest2() { using var session = store.NewSession(new MixedFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < 2000; i++) { var value = new MyValue { value = i }; - session.Upsert(ref i, ref value, Empty.Default); + bContext.Upsert(ref i, ref value, Empty.Default); } var key2 = 23; MyInput input = new(); MyOutput g1 = new(); - var status = session.Read(ref key2, ref input, ref g1, Empty.Default); + var status = bContext.Read(ref key2, ref input, ref g1, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, _) = GetSinglePendingResult(outputs); } Assert.IsTrue(status.Found); @@ -92,11 +94,11 @@ public void MixedTest2() Assert.AreEqual(23, g1.value.value); key2 = 99999; - status = session.Read(ref key2, ref input, ref g1, Empty.Default); + status = bContext.Read(ref key2, ref input, ref g1, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, _) = GetSinglePendingResult(outputs); } Assert.IsFalse(status.Found); @@ -123,6 +125,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update concurrencyControlMode: ConcurrencyControlMode.None); session = store.NewSession(copyOnWrite); + var bContext = session.BasicContext; var key = default(KeyStruct); var value = default(ValueStruct); @@ -132,7 +135,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update key = new KeyStruct() { kfield1 = 1, kfield2 = 2 }; value = new ValueStruct() { vfield1 = 1000, vfield2 = 2000 }; - var status = session.Upsert(ref key, ref input, ref value, ref output, out RecordMetadata recordMetadata1); + var status = bContext.Upsert(ref key, ref input, ref value, ref output, out RecordMetadata recordMetadata1); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); // ConcurrentWriter and InPlaceUpater return false, so we create a new record. @@ -140,13 +143,13 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update value = new ValueStruct() { vfield1 = 1001, vfield2 = 2002 }; if (updateOp == UpdateOp.Upsert) { - status = session.Upsert(ref key, ref input, ref value, ref output, out recordMetadata2); + status = bContext.Upsert(ref key, ref input, ref value, ref output, out recordMetadata2); Assert.AreEqual(1, copyOnWrite.ConcurrentWriterCallCount); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } else { - status = session.RMW(ref key, ref input, ref output, out recordMetadata2); + status = bContext.RMW(ref key, ref input, ref output, out recordMetadata2); Assert.AreEqual(1, copyOnWrite.InPlaceUpdaterCallCount); Assert.IsTrue(status.Found && status.Record.CopyUpdated, status.ToString()); } @@ -157,7 +160,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update Assert.True(iterator.GetNext(out var info)); // We should only get the new record... Assert.False(iterator.GetNext(out info)); // ... the old record was elided, so was Sealed and invalidated. } - status = session.Read(ref key, ref output); + status = bContext.Read(ref key, ref output); Assert.IsTrue(status.Found, status.ToString()); store.TryInitiateFullCheckpoint(out Guid token, CheckpointType.Snapshot); @@ -179,7 +182,7 @@ public void ForceRCUAndRecover([Values(UpdateOp.Upsert, UpdateOp.Delete)] Update Assert.True(iterator.GetNext(out var info)); // We should only get one record... Assert.False(iterator.GetNext(out info)); // ... the old record was Unsealed by Recovery, but remains invalid. } - status = session.Read(ref key, ref output); + status = bContext.Read(ref key, ref output); Assert.IsTrue(status.Found, status.ToString()); } finally diff --git a/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs b/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs index bfb4005250..318b8c2d4f 100644 --- a/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs +++ b/libs/storage/Tsavorite/cs/test/ModifiedBitTests.cs @@ -27,7 +27,8 @@ class ModifiedBitTests ModifiedBitTestComparer comparer; private TsavoriteKV store; - private ClientSession> session; + private ClientSession> session; + private BasicContext> bContext; private IDevice log; [SetUp] @@ -36,7 +37,8 @@ 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); - session = store.NewSession>(new SimpleFunctions()); + session = store.NewSession>(new SimpleSimpleFunctions()); + bContext = session.BasicContext; } [TearDown] @@ -53,24 +55,24 @@ public void TearDown() void Populate() { for (int key = 0; key < numRecords; key++) - Assert.IsFalse(session.Upsert(key, key * valueMult).IsPending); + Assert.IsFalse(bContext.Upsert(key, key * valueMult).IsPending); } - void AssertLockandModified(LockableUnsafeContext> luContext, int key, bool xlock, bool slock, bool modified = false) + void AssertLockandModified(LockableUnsafeContext> luContext, int key, bool xlock, bool slock, bool modified = false) { OverflowBucketLockTableTests.AssertLockCounts(store, ref key, xlock, slock); var isM = luContext.IsModified(key); Assert.AreEqual(modified, isM, "modified mismatch"); } - void AssertLockandModified(LockableContext> luContext, int key, bool xlock, bool slock, bool modified = false) + void AssertLockandModified(LockableContext> luContext, int key, bool xlock, bool slock, bool modified = false) { OverflowBucketLockTableTests.AssertLockCounts(store, ref key, xlock, slock); var isM = luContext.IsModified(key); Assert.AreEqual(modified, isM, "modified mismatch"); } - void AssertLockandModified(ClientSession> session, int key, bool xlock, bool slock, bool modified = false) + void AssertLockandModified(ClientSession> session, int key, bool xlock, bool slock, bool modified = false) { var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); @@ -89,7 +91,7 @@ public void LockAndNotModify() Populate(); Random r = new(100); int key = r.Next(numRecords); - session.ResetModified(key); + bContext.ResetModified(key); var lContext = session.LockableContext; lContext.BeginLockable(); @@ -119,7 +121,7 @@ public void ResetModifyForNonExistingKey() { Populate(); int key = numRecords + 100; - session.ResetModified(key); + bContext.ResetModified(key); AssertLockandModified(session, key, xlock: false, slock: false, modified: false); } @@ -131,7 +133,7 @@ public void ModifyClientSession([Values(true, false)] bool flushToDisk, [Values] int key = numRecords - 500; int value = 14; - session.ResetModified(key); + bContext.ResetModified(key); AssertLockandModified(session, key, xlock: false, slock: false, modified: false); if (flushToDisk) @@ -141,13 +143,13 @@ public void ModifyClientSession([Values(true, false)] bool flushToDisk, [Values] switch (updateOp) { case UpdateOp.Upsert: - status = session.Upsert(key, value); + status = bContext.Upsert(key, value); break; case UpdateOp.RMW: - status = session.RMW(key, value); + status = bContext.RMW(key, value); break; case UpdateOp.Delete: - status = session.Delete(key); + status = bContext.Delete(key); break; default: break; @@ -158,13 +160,13 @@ public void ModifyClientSession([Values(true, false)] bool flushToDisk, [Values] { case UpdateOp.RMW: Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); break; default: Assert.IsTrue(status.NotFound); break; } - (status, var _) = session.Read(key); + (status, var _) = bContext.Read(key); Assert.IsTrue(status.Found || updateOp == UpdateOp.Delete); } @@ -182,7 +184,7 @@ public void ModifyLUC([Values(true, false)] bool flushToDisk, [Values] UpdateOp int key = numRecords - 500; int value = 14; - session.ResetModified(key); + bContext.ResetModified(key); var luContext = session.LockableUnsafeContext; luContext.BeginUnsafe(); luContext.BeginLockable(); @@ -256,7 +258,7 @@ public void ModifyUC([Values(true, false)] bool flushToDisk, [Values] UpdateOp u int key = numRecords - 500; int value = 14; - session.ResetModified(key); + bContext.ResetModified(key); AssertLockandModified(session, key, xlock: false, slock: false, modified: false); if (flushToDisk) @@ -308,7 +310,7 @@ public void ModifyLC([Values(true, false)] bool flushToDisk, [Values] UpdateOp u int key = numRecords - 500; int value = 14; - session.ResetModified(key); + bContext.ResetModified(key); var lContext = session.LockableContext; lContext.BeginLockable(); AssertLockandModified(lContext, key, xlock: false, slock: false, modified: false); diff --git a/libs/storage/Tsavorite/cs/test/MoreLogCompactionTests.cs b/libs/storage/Tsavorite/cs/test/MoreLogCompactionTests.cs index 86614d0897..f8693b38dd 100644 --- a/libs/storage/Tsavorite/cs/test/MoreLogCompactionTests.cs +++ b/libs/storage/Tsavorite/cs/test/MoreLogCompactionTests.cs @@ -39,7 +39,8 @@ public void TearDown() public void DeleteCompactLookup([Values] CompactionType compactionType) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; const int totalRecords = 2000; var start = store.Log.TailAddress; @@ -49,25 +50,26 @@ public void DeleteCompactLookup([Values] CompactionType compactionType) { if (i == 1010) compactUntil = store.Log.TailAddress; - session.Upsert(i, i); + bContext.Upsert(i, i); } for (int i = 0; i < totalRecords / 2; i++) - session.Delete(i); + bContext.Delete(i); compactUntil = session.Compact(compactUntil, compactionType); Assert.AreEqual(compactUntil, store.Log.BeginAddress); - using var session2 = store.NewSession>(new SimpleFunctions()); + using var session2 = store.NewSession>(new SimpleSimpleFunctions()); + var bContext2 = session2.BasicContext; // Verify records by reading for (int i = 0; i < totalRecords; i++) { - (var status, var output) = session2.Read(i); + (var status, var output) = bContext2.Read(i); if (status.IsPending) { - session2.CompletePendingWithOutputs(out var completedOutputs, true); + bContext2.CompletePendingWithOutputs(out var completedOutputs, true); Assert.IsTrue(completedOutputs.Next()); (status, output) = (completedOutputs.Current.Status, completedOutputs.Current.Output); Assert.IsFalse(completedOutputs.Next()); diff --git a/libs/storage/Tsavorite/cs/test/NativeReadCacheTests.cs b/libs/storage/Tsavorite/cs/test/NativeReadCacheTests.cs index 24da14e893..ae517f9f38 100644 --- a/libs/storage/Tsavorite/cs/test/NativeReadCacheTests.cs +++ b/libs/storage/Tsavorite/cs/test/NativeReadCacheTests.cs @@ -39,6 +39,7 @@ public void TearDown() public void NativeDiskWriteReadCache() { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; InputStruct input = default; @@ -46,9 +47,9 @@ public void NativeDiskWriteReadCache() { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); // Evict all records from main memory of hybrid log store.Log.FlushAndEvict(true); @@ -60,9 +61,9 @@ public void NativeDiskWriteReadCache() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read last 100 keys - all should be served from cache @@ -72,7 +73,7 @@ public void NativeDiskWriteReadCache() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); @@ -88,9 +89,9 @@ public void NativeDiskWriteReadCache() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read 100 keys - all should be served from cache @@ -100,7 +101,7 @@ public void NativeDiskWriteReadCache() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); @@ -111,7 +112,7 @@ public void NativeDiskWriteReadCache() { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i + 1, vfield2 = i + 2 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } // RMW to overwrite the read cache @@ -120,10 +121,10 @@ public void NativeDiskWriteReadCache() OutputStruct output = default; var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; input = new InputStruct { ifield1 = 1, ifield2 = 1 }; - var status = session.RMW(ref key1, ref input, ref output, Empty.Default); + var status = bContext.RMW(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -139,7 +140,7 @@ public void NativeDiskWriteReadCache() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i + 1, vfield2 = i + 2 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); @@ -151,6 +152,7 @@ public void NativeDiskWriteReadCache() public void NativeDiskWriteReadCache2() { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; InputStruct input = default; @@ -158,9 +160,9 @@ public void NativeDiskWriteReadCache2() { var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); // Dispose the hybrid log from memory entirely store.Log.DisposeFromMemory(); @@ -172,9 +174,9 @@ public void NativeDiskWriteReadCache2() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read last 100 keys - all should be served from cache @@ -184,7 +186,7 @@ public void NativeDiskWriteReadCache2() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); @@ -200,9 +202,9 @@ public void NativeDiskWriteReadCache2() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read 100 keys - all should be served from cache @@ -212,7 +214,7 @@ public void NativeDiskWriteReadCache2() var key1 = new KeyStruct { kfield1 = i, kfield2 = i + 1 }; var value = new ValueStruct { vfield1 = i, vfield2 = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.vfield1, output.value.vfield1); Assert.AreEqual(value.vfield2, output.value.vfield2); diff --git a/libs/storage/Tsavorite/cs/test/NeedCopyUpdateTests.cs b/libs/storage/Tsavorite/cs/test/NeedCopyUpdateTests.cs index d7698c0bad..d7db81c2d2 100644 --- a/libs/storage/Tsavorite/cs/test/NeedCopyUpdateTests.cs +++ b/libs/storage/Tsavorite/cs/test/NeedCopyUpdateTests.cs @@ -47,6 +47,7 @@ public void TryAddTest() { TryAddTestFunctions functions = new(); using var session = store.NewSession(functions); + var bContext = session.BasicContext; Status status; var key = 1; @@ -54,43 +55,43 @@ public void TryAddTest() var value2 = new RMWValue { value = 2 }; functions.noNeedInitialUpdater = true; - status = session.RMW(ref key, ref value1); // needInitialUpdater false + NOTFOUND + status = bContext.RMW(ref key, ref value1); // needInitialUpdater false + NOTFOUND Assert.IsFalse(status.Found, status.ToString()); Assert.IsFalse(value1.flag); // InitialUpdater is not called functions.noNeedInitialUpdater = false; - status = session.RMW(ref key, ref value1); // InitialUpdater + NotFound + status = bContext.RMW(ref key, ref value1); // InitialUpdater + NotFound Assert.IsFalse(status.Found, status.ToString()); Assert.IsTrue(value1.flag); // InitialUpdater is called - status = session.RMW(ref key, ref value2); // InPlaceUpdater + Found + status = bContext.RMW(ref key, ref value2); // InPlaceUpdater + Found Assert.IsTrue(status.Record.InPlaceUpdated, status.ToString()); store.Log.Flush(true); - status = session.RMW(ref key, ref value2); // NeedCopyUpdate returns false, so RMW returns simply Found + status = bContext.RMW(ref key, ref value2); // NeedCopyUpdate returns false, so RMW returns simply Found Assert.IsTrue(status.Found, status.ToString()); store.Log.FlushAndEvict(true); - status = session.RMW(ref key, ref value2, new(StatusCode.Found)); // PENDING + NeedCopyUpdate + Found + status = bContext.RMW(ref key, ref value2, new(StatusCode.Found)); // PENDING + NeedCopyUpdate + Found Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var outputs, true); + bContext.CompletePendingWithOutputs(out var outputs, true); var output = new RMWValue(); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(status.Found, status.ToString()); // NeedCopyUpdate returns false, so RMW returns simply Found // Test stored value. Should be value1 - status = session.Read(ref key, ref value1, ref output, new(StatusCode.Found)); + status = bContext.Read(ref key, ref value1, ref output, new(StatusCode.Found)); Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(true); + bContext.CompletePending(true); - status = session.Delete(ref key); + status = bContext.Delete(ref key); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); - session.CompletePending(true); + bContext.CompletePending(true); store.Log.FlushAndEvict(true); - status = session.RMW(ref key, ref value2, new(StatusCode.NotFound | StatusCode.CreatedRecord)); // PENDING + InitialUpdater + NOTFOUND + status = bContext.RMW(ref key, ref value2, new(StatusCode.NotFound | StatusCode.CreatedRecord)); // PENDING + InitialUpdater + NOTFOUND Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(true); + bContext.CompletePending(true); } internal class RMWValue @@ -188,6 +189,7 @@ public void CopyUpdateFromHeadReadOnlyPageTest() { RMWSinglePageFunctions functions = new(); using var session = store.NewSession(functions); + var bContext = session.BasicContext; // Two records is the most that can "fit" into the first Constants.kFirstValueAddress "range"; therefore when we close pages // after flushing, ClosedUntilAddress will be aligned with the end of the page, so we will succeed in the allocation that @@ -196,7 +198,7 @@ public void CopyUpdateFromHeadReadOnlyPageTest() for (int key = 0; key < recsPerPage - padding; key++) { - var status = session.RMW(key, key << 32 + key); + var status = bContext.RMW(key, key << 32 + key); Assert.IsTrue(status.IsCompletedSuccessfully, status.ToString()); } @@ -205,13 +207,13 @@ public void CopyUpdateFromHeadReadOnlyPageTest() // This should trigger CopyUpdater, after flushing the oldest page (closest to HeadAddress). for (int key = 0; key < recsPerPage - padding; key++) { - var status = session.RMW(key, key << 32 + key); + var status = bContext.RMW(key, key << 32 + key); if (status.IsPending) - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); } } - internal class RMWSinglePageFunctions : SimpleFunctions + internal class RMWSinglePageFunctions : SimpleSimpleFunctions { } } diff --git a/libs/storage/Tsavorite/cs/test/ObjectReadCacheTests.cs b/libs/storage/Tsavorite/cs/test/ObjectReadCacheTests.cs index 6b95ad3f23..8ccd83fbf7 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectReadCacheTests.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectReadCacheTests.cs @@ -46,6 +46,7 @@ public void TearDown() public void ObjectDiskWriteReadCache() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; MyInput input = default; @@ -53,9 +54,9 @@ public void ObjectDiskWriteReadCache() { var key = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); // Evict all records from main memory of hybrid log store.Log.FlushAndEvict(true); @@ -67,9 +68,9 @@ public void ObjectDiskWriteReadCache() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read last 100 keys - all should be served from cache @@ -79,7 +80,7 @@ public void ObjectDiskWriteReadCache() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.value, output.value.value); } @@ -94,9 +95,9 @@ public void ObjectDiskWriteReadCache() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read 100 keys - all should be served from cache @@ -106,7 +107,7 @@ public void ObjectDiskWriteReadCache() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.value, output.value.value); } @@ -117,7 +118,7 @@ public void ObjectDiskWriteReadCache() { var key1 = new MyKey { key = i }; var value = new MyValue { value = i + 1 }; - session.Upsert(ref key1, ref value, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); } // RMW to overwrite the read cache @@ -125,9 +126,9 @@ public void ObjectDiskWriteReadCache() { var key1 = new MyKey { key = i }; input = new MyInput { value = 1 }; - var status = session.RMW(ref key1, ref input, Empty.Default); + var status = bContext.RMW(ref key1, ref input, Empty.Default); if (status.IsPending) - session.CompletePending(true); + bContext.CompletePending(true); } // Read the 100 keys @@ -137,7 +138,7 @@ public void ObjectDiskWriteReadCache() var key1 = new MyKey { key = i }; var value = new MyValue { value = i + 1 }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found, $"key = {key1.key}"); Assert.AreEqual(value.value, output.value.value); } @@ -148,6 +149,7 @@ public void ObjectDiskWriteReadCache() public void ObjectDiskWriteReadCache2() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; MyInput input = default; @@ -155,9 +157,9 @@ public void ObjectDiskWriteReadCache2() { var key = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); // Dispose the hybrid log from memory entirely store.Log.DisposeFromMemory(); @@ -169,9 +171,9 @@ public void ObjectDiskWriteReadCache2() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read last 100 keys - all should be served from cache @@ -181,7 +183,7 @@ public void ObjectDiskWriteReadCache2() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.value, output.value.value); } @@ -196,9 +198,9 @@ public void ObjectDiskWriteReadCache2() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.IsPending); - session.CompletePending(true); + bContext.CompletePending(true); } // Read 100 keys - all should be served from cache @@ -208,7 +210,7 @@ public void ObjectDiskWriteReadCache2() MyKey key1 = new() { key = i }; MyValue value = new() { value = i }; - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); Assert.AreEqual(value.value, output.value.value); } diff --git a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest.cs b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest.cs index b4298fc826..5cd12b844a 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest.cs @@ -99,12 +99,13 @@ public unsafe void Populate() // Register thread with Tsavorite var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; // Process the batch of input data bool first = true; for (int i = 0; i < numOps; i++) { - session.RMW(ref inputArray[i].Item1, ref inputArray[i].Item2, Empty.Default); + bContext.RMW(ref inputArray[i].Item1, ref inputArray[i].Item2, Empty.Default); if ((i + 1) % checkpointInterval == 0) { @@ -120,13 +121,13 @@ public unsafe void Populate() if (i % completePendingInterval == 0) { - session.CompletePending(false, false); + bContext.CompletePending(false, false); } } // Make sure operations are completed - session.CompletePending(true); + bContext.CompletePending(true); session.Dispose(); } @@ -144,17 +145,18 @@ public unsafe void Verify(Guid cprVersion, Guid indexVersion) } var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; Input input = default; // Issue read requests for (var i = 0; i < numUniqueKeys; i++) { Output output = new(); - session.Read(ref inputArray[i].Item1, ref input, ref output, Empty.Default); + bContext.Read(ref inputArray[i].Item1, ref input, ref output, Empty.Default); } // Complete all pending requests - session.CompletePending(true); + bContext.CompletePending(true); // Release session.Dispose(); diff --git a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest2.cs b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest2.cs index 09b2e5525d..3988891cf1 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest2.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest2.cs @@ -96,11 +96,13 @@ private static void Destroy(IDevice log, IDevice objlog, TsavoriteKV session, MyContext context, TsavoriteKV store, CheckpointType checkpointType) { + var bContext = session.BasicContext; + for (int i = 0; i < iterations; i++) { var _key = new MyKey { key = i, name = i.ToString() }; var value = new MyValue { value = i.ToString() }; - session.Upsert(ref _key, ref value, context); + bContext.Upsert(ref _key, ref value, context); if (i % 100 == 0) { @@ -112,16 +114,18 @@ private void Write(ClientSession session, MyContext context, bool delete) { + var bContext = session.BasicContext; + for (int i = 0; i < iterations; i++) { MyKey key = new() { key = i, name = i.ToString() }; MyInput input = default; MyOutput g1 = new(); - var status = session.Read(ref key, ref input, ref g1, context); + var status = bContext.Read(ref key, ref input, ref g1, context); if (status.IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); context.FinalizeRead(ref status, ref g1); } @@ -134,12 +138,12 @@ private void Read(ClientSession + public class MyFunctions : SessionFunctionsBase { public override bool InitialUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { 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; diff --git a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest3.cs b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest3.cs index de39014db6..d05374f3d9 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest3.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectRecoveryTest3.cs @@ -98,12 +98,14 @@ private static void Destroy(IDevice log, IDevice objlog, TsavoriteKV Write(ClientSession session, MyContext context, TsavoriteKV store, CheckpointType checkpointType) { + var bContext = session.BasicContext; + var tokens = new List<(int, Guid)>(); for (int i = 0; i < iterations; i++) { var _key = new MyKey { key = i, name = string.Concat(Enumerable.Repeat(i.ToString(), 100)) }; var value = new MyValue { value = i.ToString() }; - session.Upsert(ref _key, ref value, context); + bContext.Upsert(ref _key, ref value, context); if (i % 1000 == 0 && i > 0) { @@ -117,16 +119,18 @@ private static void Destroy(IDevice log, IDevice objlog, TsavoriteKV session, MyContext context, bool delete, int iter) { + var bContext = session.BasicContext; + for (int i = 0; i < iter; i++) { var key = new MyKey { key = i, name = string.Concat(Enumerable.Repeat(i.ToString(), 100)) }; MyInput input = default; MyOutput g1 = new(); - var status = session.Read(ref key, ref input, ref g1, context); + var status = bContext.Read(ref key, ref input, ref g1, context); if (status.IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); context.FinalizeRead(ref status, ref g1); } @@ -139,12 +143,12 @@ private void Read(ClientSession + public class Functions : SessionFunctionsBase { // Read functions public override bool SingleReader(ref AdId key, ref Input input, ref NumClicks value, ref Output dst, ref ReadInfo readInfo) diff --git a/libs/storage/Tsavorite/cs/test/ObjectTestTypes.cs b/libs/storage/Tsavorite/cs/test/ObjectTestTypes.cs index e911f718e0..0ad63b3e4b 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectTestTypes.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectTestTypes.cs @@ -56,7 +56,7 @@ public class MyOutput public override string ToString() => value.ToString(); } - public class MyFunctions : FunctionsBase + public class MyFunctions : SessionFunctionsBase { public override bool InitialUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { @@ -119,7 +119,7 @@ public override bool SingleWriter(ref MyKey key, ref MyInput input, ref MyValue } } - public class MyFunctions2 : FunctionsBase + public class MyFunctions2 : SessionFunctionsBase { public override bool InitialUpdater(ref MyValue key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { @@ -182,7 +182,7 @@ public override bool SingleWriter(ref MyValue key, ref MyInput input, ref MyValu } } - public class MyFunctionsDelete : FunctionsBase + public class MyFunctionsDelete : SessionFunctionsBase { public override bool InitialUpdater(ref MyKey key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { @@ -255,7 +255,7 @@ public override bool SingleWriter(ref MyKey key, ref MyInput input, ref MyValue } } - public class MixedFunctions : FunctionsBase + public class MixedFunctions : SessionFunctionsBase { public override bool InitialUpdater(ref int key, ref MyInput input, ref MyValue value, ref MyOutput output, ref RMWInfo rmwInfo, ref RecordInfo recordInfo) { @@ -341,7 +341,7 @@ public class MyLargeOutput public MyLargeValue value; } - public class MyLargeFunctions : FunctionsBase + public class MyLargeFunctions : SessionFunctionsBase { public override void ReadCompletionCallback(ref MyKey key, ref MyInput input, ref MyLargeOutput output, Empty ctx, Status status, RecordMetadata recordMetadata) { diff --git a/libs/storage/Tsavorite/cs/test/ObjectTests.cs b/libs/storage/Tsavorite/cs/test/ObjectTests.cs index 926b08251a..580d1f3dd8 100644 --- a/libs/storage/Tsavorite/cs/test/ObjectTests.cs +++ b/libs/storage/Tsavorite/cs/test/ObjectTests.cs @@ -47,6 +47,7 @@ public void TearDown() public void ObjectInMemWriteRead() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; MyKey key1 = new() { key = 9999999 }; MyValue value = new() { value = 23 }; @@ -54,8 +55,8 @@ public void ObjectInMemWriteRead() MyInput input = null; MyOutput output = new(); - session.Upsert(ref key1, ref value, Empty.Default); - session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + bContext.Read(ref key1, ref input, ref output, Empty.Default); Assert.AreEqual(value.value, output.value.value); } @@ -64,22 +65,23 @@ public void ObjectInMemWriteRead() public void ObjectInMemWriteRead2() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; MyKey key1 = new() { key = 8999998 }; MyInput input1 = new() { value = 23 }; MyOutput output = new(); - session.RMW(ref key1, ref input1, Empty.Default); + bContext.RMW(ref key1, ref input1, Empty.Default); MyKey key2 = new() { key = 8999999 }; MyInput input2 = new() { value = 24 }; - session.RMW(ref key2, ref input2, Empty.Default); + bContext.RMW(ref key2, ref input2, Empty.Default); - session.Read(ref key1, ref input1, ref output, Empty.Default); + bContext.Read(ref key1, ref input1, ref output, Empty.Default); Assert.AreEqual(input1.value, output.value.value); - session.Read(ref key2, ref input2, ref output, Empty.Default); + bContext.Read(ref key2, ref input2, ref output, Empty.Default); Assert.AreEqual(input2.value, output.value.value); } @@ -91,23 +93,24 @@ public void ObjectInMemWriteRead2() public void ObjectDiskWriteRead() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < 2000; i++) { var key = new MyKey { key = i }; var value = new MyValue { value = i }; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); // store.ShiftReadOnlyAddress(store.LogTailAddress); } MyKey key2 = new() { key = 23 }; MyInput input = new(); MyOutput g1 = new(); - var status = session.Read(ref key2, ref input, ref g1, Empty.Default); + var status = bContext.Read(ref key2, ref input, ref g1, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, g1) = GetSinglePendingResult(outputs); } @@ -115,11 +118,11 @@ public void ObjectDiskWriteRead() Assert.AreEqual(23, g1.value.value); key2 = new MyKey { key = 99999 }; - status = session.Read(ref key2, ref input, ref g1, Empty.Default); + status = bContext.Read(ref key2, ref input, ref g1, Empty.Default); if (status.IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -131,9 +134,9 @@ public void ObjectDiskWriteRead() { var key1 = new MyKey { key = i }; input = new MyInput { value = 1 }; - status = session.RMW(ref key1, ref input, Empty.Default); + status = bContext.RMW(ref key1, ref input, Empty.Default); if (status.IsPending) - session.CompletePending(true); + bContext.CompletePending(true); } for (int i = 0; i < 2000; i++) @@ -142,9 +145,9 @@ public void ObjectDiskWriteRead() var key1 = new MyKey { key = i }; var value = new MyValue { value = i }; - if (session.Read(ref key1, ref input, ref output, Empty.Default).IsPending) + if (bContext.Read(ref key1, ref input, ref output, Empty.Default).IsPending) { - session.CompletePending(true); + bContext.CompletePending(true); } else { @@ -168,33 +171,34 @@ public void ObjectDiskWriteRead() public async Task ReadAsyncObjectDiskWriteRead() { using var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < 2000; i++) { var key = new MyKey { key = i }; var value = new MyValue { value = i }; - var r = await session.UpsertAsync(ref key, ref value); + var r = await bContext.UpsertAsync(ref key, ref value); while (r.Status.IsPending) r = await r.CompleteAsync(); // test async version of Upsert completion } var key1 = new MyKey { key = 1989 }; var input = new MyInput(); - var readResult = await session.ReadAsync(ref key1, ref input, Empty.Default); + var readResult = await bContext.ReadAsync(ref key1, ref input, Empty.Default); var result = readResult.Complete(); Assert.IsTrue(result.status.Found); Assert.AreEqual(1989, result.output.value.value); var key2 = new MyKey { key = 23 }; - readResult = await session.ReadAsync(ref key2, ref input, Empty.Default); + readResult = await bContext.ReadAsync(ref key2, ref input, Empty.Default); result = readResult.Complete(); Assert.IsTrue(result.status.Found); Assert.AreEqual(23, result.output.value.value); var key3 = new MyKey { key = 9999 }; - readResult = await session.ReadAsync(ref key3, ref input, Empty.Default); + readResult = await bContext.ReadAsync(ref key3, ref input, Empty.Default); result = readResult.Complete(); Assert.IsFalse(result.status.Found); @@ -204,7 +208,7 @@ public async Task ReadAsyncObjectDiskWriteRead() { var key = new MyKey { key = i }; input = new MyInput { value = 1 }; - var r = await session.RMWAsync(ref key, ref input, Empty.Default); + var r = await bContext.RMWAsync(ref key, ref input, Empty.Default); while (r.Status.IsPending) { r = await r.CompleteAsync(); // test async version of RMW completion @@ -216,7 +220,7 @@ public async Task ReadAsyncObjectDiskWriteRead() { var key = new MyKey { key = i }; input = new MyInput { value = 1 }; - (await session.RMWAsync(ref key, ref input, Empty.Default)).Complete(); + (await bContext.RMWAsync(ref key, ref input, Empty.Default)).Complete(); } for (int i = 0; i < 2000; i++) @@ -225,7 +229,7 @@ public async Task ReadAsyncObjectDiskWriteRead() var key = new MyKey { key = i }; var value = new MyValue { value = i }; - readResult = await session.ReadAsync(ref key, ref input, Empty.Default); + readResult = await bContext.ReadAsync(ref key, ref input, Empty.Default); result = readResult.Complete(); Assert.IsTrue(result.status.Found); if (i < 100 || i >= 1900) diff --git a/libs/storage/Tsavorite/cs/test/PostOperationsTests.cs b/libs/storage/Tsavorite/cs/test/PostOperationsTests.cs index b3cd41a751..668ff7713a 100644 --- a/libs/storage/Tsavorite/cs/test/PostOperationsTests.cs +++ b/libs/storage/Tsavorite/cs/test/PostOperationsTests.cs @@ -10,7 +10,7 @@ namespace Tsavorite.test [TestFixture] internal class PostOperationsTests { - class PostFunctions : SimpleFunctions + class PostFunctions : SimpleSimpleFunctions { internal long pswAddress; internal long piuAddress; @@ -46,6 +46,7 @@ internal PostFunctions() : base() { } private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; const int numRecords = 100; @@ -62,6 +63,7 @@ public void Setup() store = new TsavoriteKV (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 10 }); session = store.NewSession(new PostFunctions()); + bContext = session.BasicContext; Populate(); } @@ -82,7 +84,7 @@ void Populate() for (var key = 0; key < numRecords; ++key) { expectedAddress = store.Log.TailAddress; - session.Upsert(key, key * 100); + bContext.Upsert(key, key * 100); Assert.AreEqual(expectedAddress, session.functions.pswAddress); } @@ -93,7 +95,7 @@ void Populate() internal void CompletePendingAndVerifyInsertedAddress() { // Note: Only Read and RMW have Pending results. - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); TestUtils.GetSinglePendingResult(completedOutputs, out var recordMetadata); Assert.AreEqual(expectedAddress, recordMetadata.Address); } @@ -107,8 +109,8 @@ public void PostSingleWriterTest() // Execute the ReadOnly (InternalInsert) test store.Log.FlushAndEvict(wait: true); - session.Upsert(targetKey, targetKey * 1000); - session.CompletePending(wait: true); + bContext.Upsert(targetKey, targetKey * 1000); + bContext.CompletePending(wait: true); Assert.AreEqual(expectedAddress, session.functions.pswAddress); } @@ -118,21 +120,21 @@ public void PostSingleWriterTest() public void PostInitialUpdaterTest() { // Execute the not-found test (InternalRMW). - session.RMW(numRecords + 1, (numRecords + 1) * 1000); + bContext.RMW(numRecords + 1, (numRecords + 1) * 1000); Assert.AreEqual(expectedAddress, session.functions.piuAddress); session.functions.Clear(); // Now cause an attempt at InPlaceUpdater, which we've set to fail, so CopyUpdater is done (InternalInsert). expectedAddress = store.Log.TailAddress; - session.RMW(targetKey, targetKey * 1000); + bContext.RMW(targetKey, targetKey * 1000); Assert.AreEqual(expectedAddress, session.functions.pcuAddress); // Execute the not-in-memory test (InternalContinuePendingRMW). First delete the record so it has a tombstone; this will go to InitialUpdater. - session.Delete(targetKey); + bContext.Delete(targetKey); store.Log.FlushAndEvict(wait: true); expectedAddress = store.Log.TailAddress; - session.RMW(targetKey, targetKey * 1000); + bContext.RMW(targetKey, targetKey * 1000); CompletePendingAndVerifyInsertedAddress(); Assert.AreEqual(expectedAddress, session.functions.piuAddress); } @@ -144,13 +146,13 @@ public void PostCopyUpdaterTest() { // First try to modify in-memory, readonly (InternalRMW). store.Log.ShiftReadOnlyAddress(store.Log.ReadOnlyAddress, wait: true); - session.RMW(targetKey, targetKey * 1000); + bContext.RMW(targetKey, targetKey * 1000); Assert.AreEqual(expectedAddress, session.functions.pcuAddress); // Execute the not-in-memory test (InternalContinuePendingRMW). store.Log.FlushAndEvict(wait: true); expectedAddress = store.Log.TailAddress; - session.RMW(targetKey, targetKey * 1000); + bContext.RMW(targetKey, targetKey * 1000); CompletePendingAndVerifyInsertedAddress(); Assert.AreEqual(expectedAddress, session.functions.pcuAddress); } @@ -161,13 +163,13 @@ public void PostCopyUpdaterTest() public void PostSingleDeleterTest() { // Execute the not-in-memory test (InternalDelete); ConcurrentDeleter returns false to force a new record to be added. - session.Delete(targetKey); + bContext.Delete(targetKey); Assert.AreEqual(expectedAddress, session.functions.psdAddress); // Execute the not-in-memory test (InternalDelete). store.Log.FlushAndEvict(wait: true); expectedAddress = store.Log.TailAddress; - session.Delete(targetKey + 1); + bContext.Delete(targetKey + 1); Assert.AreEqual(expectedAddress, session.functions.psdAddress); } } diff --git a/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs b/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs index 831a473932..b28d6673d6 100644 --- a/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs +++ b/libs/storage/Tsavorite/cs/test/ReadAddressTests.cs @@ -58,7 +58,7 @@ public struct Output public enum UseReadCache { NoReadCache, ReadCache } - internal class Functions : FunctionsBase + internal class Functions : SessionFunctionsBase { internal long lastWriteAddress = Constants.kInvalidAddress; readonly bool useReadCache; @@ -190,6 +190,7 @@ internal async Task Populate(bool useRMW, bool useAsync, bool preserveCopyUpdate { var functions = new Functions(preserveCopyUpdaterSource); using var session = store.NewSession(functions); + var bContext = session.BasicContext; var prevLap = 0; for (int ii = 0; ii < numKeys; ii++) @@ -208,19 +209,19 @@ internal async Task Populate(bool useRMW, bool useAsync, bool preserveCopyUpdate var status = useRMW ? useAsync - ? (await session.RMWAsync(ref key, ref value)).Complete().status - : session.RMW(ref key, ref value) - : session.Upsert(ref key, ref value); + ? (await bContext.RMWAsync(ref key, ref value)).Complete().status + : bContext.RMW(ref key, ref value) + : bContext.Upsert(ref key, ref value); if (status.IsPending) - await session.CompletePendingAsync(); + await bContext.CompletePendingAsync(); InsertAddresses[ii] = functions.lastWriteAddress; //Assert.IsTrue(session.ctx.HasNoPendingRequests); // Illustrate that deleted records can be shown as well (unless overwritten by in-place operations, which are not done here) if (lap == deleteLap) - session.Delete(ref key); + bContext.Delete(ref key); } await Flush(); @@ -277,6 +278,7 @@ public void VersionedReadSyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); testStore.Populate(updateOp == UpdateOp.RMW, useAsync: false).GetAwaiter().GetResult(); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -292,13 +294,13 @@ public void VersionedReadSyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, { // We need a non-AtAddress read to start the loop of returning the previous address to read at. var status = readAtAddress == 0 - ? session.Read(ref key, ref input, ref output, ref readOptions, out _) - : session.ReadAtAddress(readAtAddress, ref key, ref input, ref output, ref readOptions, out _); + ? bContext.Read(ref key, ref input, ref output, ref readOptions, out _) + : bContext.ReadAtAddress(readAtAddress, ref key, ref input, ref output, ref readOptions, out _); if (status.IsPending) { // This will wait for each retrieved record; not recommended for performance-critical code or when retrieving multiple records unless necessary. - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs, out recordMetadata); } if (!testStore.ProcessChainRecord(status, recordMetadata, lap, ref output)) @@ -389,6 +391,7 @@ public async Task VersionedReadAsyncTests(UseReadCache urc, ReadCopyFrom readCop using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); await testStore.Populate(updateOp == UpdateOp.RMW, useAsync: true); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -403,8 +406,8 @@ public async Task VersionedReadAsyncTests(UseReadCache urc, ReadCopyFrom readCop { // We need a non-AtAddress read to start the loop of returning the previous address to read at. var readAsyncResult = readAtAddress == 0 - ? await session.ReadAsync(ref key, ref input, ref readOptions, default) - : await session.ReadAtAddressAsync(readAtAddress, ref key, ref input, ref readOptions, default); + ? await bContext.ReadAsync(ref key, ref input, ref readOptions, default) + : await bContext.ReadAtAddressAsync(readAtAddress, ref key, ref input, ref readOptions, default); var (status, output) = readAsyncResult.Complete(out recordMetadata); if (!testStore.ProcessChainRecord(status, recordMetadata, lap, ref output)) @@ -429,6 +432,7 @@ public void ReadAtAddressSyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); testStore.Populate(updateOp == UpdateOp.RMW, useAsync: false).GetAwaiter().GetResult(); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -443,12 +447,12 @@ public void ReadAtAddressSyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, for (int lap = maxLap - 1; /* tested in loop */; --lap) { var status = readAtAddress == 0 - ? session.Read(ref key, ref input, ref output, ref readOptions, out recordMetadata) - : session.ReadAtAddress(readAtAddress, ref input, ref output, ref readOptions, out recordMetadata); + ? bContext.Read(ref key, ref input, ref output, ref readOptions, out recordMetadata) + : bContext.ReadAtAddress(readAtAddress, ref input, ref output, ref readOptions, out recordMetadata); if (status.IsPending) { // This will wait for each retrieved record; not recommended for performance-critical code or when retrieving multiple records unless necessary. - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs, out recordMetadata); } @@ -474,6 +478,7 @@ public async Task ReadAtAddressAsyncTests(UseReadCache urc, ReadCopyFrom readCop using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); await testStore.Populate(updateOp == UpdateOp.RMW, useAsync: true); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -487,8 +492,8 @@ public async Task ReadAtAddressAsyncTests(UseReadCache urc, ReadCopyFrom readCop for (int lap = maxLap - 1; /* tested in loop */; --lap) { var readAsyncResult = readAtAddress == 0 - ? await session.ReadAsync(ref key, ref input, ref readOptions, default) - : await session.ReadAtAddressAsync(readAtAddress, ref input, ref readOptions, default); + ? await bContext.ReadAsync(ref key, ref input, ref readOptions, default) + : await bContext.ReadAtAddressAsync(readAtAddress, ref input, ref readOptions, default); var (status, output) = readAsyncResult.Complete(out recordMetadata); if (!testStore.ProcessChainRecord(status, recordMetadata, lap, ref output)) @@ -513,6 +518,7 @@ public async Task ReadAtAddressAsyncCopyOptNoRcTest(UseReadCache urc, ReadCopyFr using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); await testStore.Populate(updateOp == UpdateOp.RMW, useAsync: true); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -526,8 +532,8 @@ public async Task ReadAtAddressAsyncCopyOptNoRcTest(UseReadCache urc, ReadCopyFr for (int lap = maxLap - 1; /* tested in loop */; --lap) { var readAsyncResult = readAtAddress == 0 - ? await session.ReadAsync(ref key, ref input, ref readOptions, default) - : await session.ReadAtAddressAsync(readAtAddress, ref input, ref readOptions, default); + ? await bContext.ReadAsync(ref key, ref input, ref readOptions, default) + : await bContext.ReadAtAddressAsync(readAtAddress, ref input, ref readOptions, default); var (status, output) = readAsyncResult.Complete(out recordMetadata); if (!testStore.ProcessChainRecord(status, recordMetadata, lap, ref output)) @@ -552,6 +558,7 @@ public void ReadNoKeySyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, Read using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); testStore.Populate(updateOp == UpdateOp.RMW, useAsync: false).GetAwaiter().GetResult(); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -568,11 +575,11 @@ public void ReadNoKeySyncTests(UseReadCache urc, ReadCopyFrom readCopyFrom, Read { CopyOptions = session.functions.readCopyOptions }; - var status = session.ReadAtAddress(testStore.InsertAddresses[keyOrdinal], ref input, ref output, ref readOptions, out RecordMetadata recordMetadata); + var status = bContext.ReadAtAddress(testStore.InsertAddresses[keyOrdinal], ref input, ref output, ref readOptions, out RecordMetadata recordMetadata); if (status.IsPending) { // This will wait for each retrieved record; not recommended for performance-critical code or when retrieving multiple records unless necessary. - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); } @@ -598,6 +605,7 @@ public async Task ReadNoKeyAsyncTests(UseReadCache urc, ReadCopyFrom readCopyFro using var testStore = new TestStore(useReadCache, readCopyOptions, flushMode == FlushMode.OnDisk, concurrencyControlMode); await testStore.Populate(updateOp == UpdateOp.RMW, useAsync: true); using var session = testStore.store.NewSession(new Functions()); + var bContext = session.BasicContext; // Two iterations to ensure no issues due to read-caching or copying to tail. for (int iteration = 0; iteration < 2; ++iteration) @@ -615,7 +623,7 @@ public async Task ReadNoKeyAsyncTests(UseReadCache urc, ReadCopyFrom readCopyFro CopyOptions = session.functions.readCopyOptions }; - var readAsyncResult = await session.ReadAtAddressAsync(testStore.InsertAddresses[keyOrdinal], ref input, ref readOptions, default); + var readAsyncResult = await bContext.ReadAtAddressAsync(testStore.InsertAddresses[keyOrdinal], ref input, ref readOptions, default); var (status, output) = readAsyncResult.Complete(out recordMetadata); TestStore.ProcessNoKeyRecord(updateOp == UpdateOp.RMW, status, recordMetadata.RecordInfo, ref output, keyOrdinal); diff --git a/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs b/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs index 3061f389af..78553733e5 100644 --- a/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs +++ b/libs/storage/Tsavorite/cs/test/ReadCacheChainTests.cs @@ -87,13 +87,14 @@ public enum RecordRegion { Immutable, OnDisk, Mutable }; void PopulateAndEvict(RecordRegion recordRegion = RecordRegion.OnDisk) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; if (recordRegion != RecordRegion.Immutable) { for (int key = 0; key < numKeys; key++) - session.Upsert(key, key + valueAdd); - session.CompletePending(true); + bContext.Upsert(key, key + valueAdd); + bContext.CompletePending(true); if (recordRegion == RecordRegion.OnDisk) store.Log.FlushAndEvict(true); return; @@ -101,19 +102,21 @@ void PopulateAndEvict(RecordRegion recordRegion = RecordRegion.OnDisk) // Two parts, so we can have some evicted (and bring them into the readcache), and some in immutable (readonly). for (int key = 0; key < immutableSplitKey; key++) - session.Upsert(key, key + valueAdd); - session.CompletePending(true); + bContext.Upsert(key, key + valueAdd); + bContext.CompletePending(true); store.Log.FlushAndEvict(true); for (long key = immutableSplitKey; key < numKeys; key++) - session.Upsert(key, key + valueAdd); - session.CompletePending(true); + bContext.Upsert(key, key + valueAdd); + bContext.CompletePending(true); store.Log.ShiftReadOnlyAddress(store.Log.TailAddress, wait: true); } void CreateChain(RecordRegion recordRegion = RecordRegion.OnDisk) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; + long output = -1; bool expectPending(long key) => recordRegion == RecordRegion.OnDisk || (recordRegion == RecordRegion.Immutable && key < immutableSplitKey); @@ -121,11 +124,11 @@ void CreateChain(RecordRegion recordRegion = RecordRegion.OnDisk) for (long ii = 0; ii < chainLen; ++ii) { var key = lowChainKey + ii * mod; - var status = session.Read(key, out _); + var status = bContext.Read(key, out _); if (expectPending(key)) { Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(status.Record.CopiedToReadCache, status.ToString()); } @@ -137,7 +140,7 @@ void CreateChain(RecordRegion recordRegion = RecordRegion.OnDisk) // Pass2: non-PENDING reads from the cache for (var ii = 0; ii < chainLen; ++ii) { - var status = session.Read(lowChainKey + ii * mod, out _); + var status = bContext.Read(lowChainKey + ii * mod, out _); Assert.IsTrue(!status.IsPending && status.Found, status.ToString()); } @@ -146,16 +149,16 @@ void CreateChain(RecordRegion recordRegion = RecordRegion.OnDisk) { if ((key % mod) != 0) { - var status = session.Read(key, out _); + var status = bContext.Read(key, out _); if (expectPending(key)) { Assert.IsTrue(status.IsPending); - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(status.Record.CopiedToReadCache, status.ToString()); } Assert.IsTrue(status.Found, status.ToString()); - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); } } } @@ -267,7 +270,7 @@ void VerifySplicedInKey(long expectedKey) Assert.AreEqual(expectedKey, storedKey); } - static void ClearCountsOnError(ClientSession> luContext) + static void ClearCountsOnError(ClientSession> luContext) { // If we already have an exception, clear these counts so "Run" will not report them spuriously. luContext.sharedLockCount = 0; @@ -296,14 +299,15 @@ public void DeleteCacheRecordTest([Values] ConcurrencyControlMode concurrencyCon { PopulateAndEvict(); CreateChain(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; void doTest(long key) { - var status = session.Delete(key); + var status = bContext.Delete(key); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); - status = session.Read(key, out var value); + status = bContext.Read(key, out var value); Assert.IsFalse(status.Found, status.ToString()); } @@ -324,14 +328,15 @@ public void DeleteHalfOfAllReadCacheRecordsTest([Values] ConcurrencyControlMode { PopulateAndEvict(); CreateChain(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; void doTest(long key) { - var status = session.Delete(key); + var status = bContext.Delete(key); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); - status = session.Read(key, out var value); + status = bContext.Read(key, out var value); Assert.IsFalse(status.Found, status.ToString()); } @@ -390,17 +395,18 @@ void DoUpdateTest(bool useRMW) { PopulateAndEvict(); CreateChain(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; void doTest(long key) { - var status = session.Read(key, out var value); + var status = bContext.Read(key, out var value); Assert.IsTrue(status.Found, status.ToString()); if (useRMW) { // RMW will use the readcache entry for its source and then invalidate it. - status = session.RMW(key, value + valueAdd); + status = bContext.RMW(key, value + valueAdd); Assert.IsTrue(status.Found && status.Record.CopyUpdated, status.ToString()); Assert.IsTrue(FindRecordInReadCache(key, out bool invalid, out _, out _)); @@ -408,11 +414,11 @@ void doTest(long key) } else { - status = session.Upsert(key, value + valueAdd); + status = bContext.Upsert(key, value + valueAdd); Assert.IsTrue(status.Record.Created, status.ToString()); } - status = session.Read(key, out value); + status = bContext.Read(key, out value); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(key + valueAdd * 2, value); } @@ -435,13 +441,15 @@ public void SpliceInFromCTTTest([Values] ConcurrencyControlMode concurrencyContr PopulateAndEvict(); CreateChain(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; + long input = 0, output = 0, key = lowChainKey - mod; // key must be in evicted region for this test ReadOptions readOptions = new() { CopyOptions = new(ReadCopyFrom.AllImmutable, ReadCopyTo.MainLog) }; - var status = session.Read(ref key, ref input, ref output, ref readOptions, out _); + var status = bContext.Read(ref key, ref input, ref output, ref readOptions, out _); Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); VerifySplicedInKey(key); } @@ -455,19 +463,21 @@ public void SpliceInFromUpsertTest([Values] RecordRegion recordRegion, [Values] PopulateAndEvict(recordRegion); CreateChain(recordRegion); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; + long key = -1; if (recordRegion == RecordRegion.Immutable || recordRegion == RecordRegion.OnDisk) { key = spliceInExistingKey; - var status = session.Upsert(key, key + valueAdd); + var status = bContext.Upsert(key, key + valueAdd); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } else { key = spliceInNewKey; - var status = session.Upsert(key, key + valueAdd); + var status = bContext.Upsert(key, key + valueAdd); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } @@ -483,14 +493,15 @@ public void SpliceInFromRMWTest([Values] RecordRegion recordRegion, [Values] Con PopulateAndEvict(recordRegion); CreateChain(recordRegion); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; long key = -1, output = -1; if (recordRegion == RecordRegion.Immutable || recordRegion == RecordRegion.OnDisk) { // Existing key key = spliceInExistingKey; - var status = session.RMW(key, key + valueAdd); + var status = bContext.RMW(key, key + valueAdd); // If OnDisk, this used the readcache entry for its source and then invalidated it. Assert.IsTrue(status.Found && status.Record.CopyUpdated, status.ToString()); @@ -502,11 +513,11 @@ public void SpliceInFromRMWTest([Values] RecordRegion recordRegion, [Values] Con { // New key key = spliceInNewKey; - status = session.RMW(key, key + valueAdd); + status = bContext.RMW(key, key + valueAdd); // This NOTFOUND key will return PENDING because we have to trace back through the collisions. Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } @@ -514,7 +525,7 @@ public void SpliceInFromRMWTest([Values] RecordRegion recordRegion, [Values] Con else { key = spliceInNewKey; - var status = session.RMW(key, key + valueAdd); + var status = bContext.RMW(key, key + valueAdd); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } @@ -530,19 +541,20 @@ public void SpliceInFromDeleteTest([Values] RecordRegion recordRegion, [Values] PopulateAndEvict(recordRegion); CreateChain(recordRegion); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; long key = -1; if (recordRegion == RecordRegion.Immutable || recordRegion == RecordRegion.OnDisk) { key = spliceInExistingKey; - var status = session.Delete(key); + var status = bContext.Delete(key); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } else { key = spliceInNewKey; - var status = session.Delete(key); + var status = bContext.Delete(key); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } @@ -558,7 +570,7 @@ public void VerifyLockCountsAfterReadCacheEvict([Values(ConcurrencyControlMode.L PopulateAndEvict(); CreateChain(); - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); var luContext = session.LockableUnsafeContext; var keys = new[] @@ -691,7 +703,7 @@ public void TearDown() DeleteDirectory(MethodTestDir); } - internal class RmwLongFunctions : SimpleFunctions + internal class RmwLongFunctions : SimpleSessionFunctions { /// public override bool ConcurrentWriter(ref long key, ref long input, ref long src, ref long dst, ref long output, ref UpsertInfo upsertInfo, ref RecordInfo recordInfo) @@ -731,16 +743,17 @@ public override bool InitialUpdater(ref long key, ref long input, ref long value unsafe void PopulateAndEvict() { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSessionFunctions()); + var bContext = session.BasicContext; for (long ii = 0; ii < numKeys; ii++) { long key = ii; - var status = session.Upsert(ref key, ref key); + var status = bContext.Upsert(ref key, ref key); Assert.IsFalse(status.IsPending); Assert.IsTrue(status.Record.Created, $"key {key}, status {status}"); } - session.CompletePending(true); + bContext.CompletePending(true); store.Log.FlushAndEvict(true); } @@ -766,7 +779,8 @@ public void LongRcMultiThreadTest([Values] HashModulo modRange, [Values(0, 1, 2, const int numIterations = 1; unsafe void runReadThread(int tid) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSessionFunctions()); + var bContext = session.BasicContext; Random rng = new(tid * 101); for (var iteration = 0; iteration < numIterations; ++iteration) @@ -774,11 +788,11 @@ unsafe void runReadThread(int tid) for (var ii = 0; ii < numKeys; ++ii) { long key = ii, output = 0; - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); bool wasPending = status.IsPending; if (wasPending) { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs, out var recordMetadata); Assert.AreEqual(recordMetadata.Address == Constants.kInvalidAddress, status.Record.CopiedToReadCache, $"key {ii}: {status}"); } @@ -791,6 +805,7 @@ unsafe void runReadThread(int tid) unsafe void runUpdateThread(int tid) { using var session = store.NewSession(new RmwLongFunctions()); + var bContext = session.BasicContext; Random rng = new(tid * 101); for (var iteration = 0; iteration < numIterations; ++iteration) @@ -799,13 +814,13 @@ unsafe void runUpdateThread(int tid) { long key = ii, input = ii + valueAdd * tid, output = 0; var status = updateOp == UpdateOp.RMW - ? session.RMW(ref key, ref input, ref output) - : session.Upsert(ref key, ref input, ref input, ref output); + ? bContext.RMW(ref key, ref input, ref output) + : bContext.Upsert(ref key, ref input, ref input, ref output); bool wasPending = status.IsPending; if (wasPending) { Assert.AreNotEqual(UpdateOp.Upsert, updateOp, "Upsert should not go pending"); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); // Record may have been updated in-place if a CTT was done during the pending operation. @@ -945,6 +960,7 @@ public override bool InitialUpdater(ref SpanByte key, ref SpanByte input, ref Sp unsafe void PopulateAndEvict() { using var session = store.NewSession>(new SpanByteFunctions()); + var bContext = session.BasicContext; Span keyVec = stackalloc byte[sizeof(long)]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -952,14 +968,14 @@ unsafe void PopulateAndEvict() for (long ii = 0; ii < numKeys; ii++) { Assert.IsTrue(BitConverter.TryWriteBytes(keyVec, ii)); - var status = session.Upsert(ref key, ref key); + var status = bContext.Upsert(ref key, ref key); Assert.IsTrue(status.Record.Created, status.ToString()); } - session.CompletePending(true); + bContext.CompletePending(true); store.Log.FlushAndEvict(true); } - static void ClearCountsOnError(ClientSession> luContext) + static void ClearCountsOnError(ClientSession> luContext) { // If we already have an exception, clear these counts so "Run" will not report them spuriously. luContext.sharedLockCount = 0; @@ -987,6 +1003,7 @@ public void SpanByteRcMultiThreadTest([Values] HashModulo modRange, [Values(0, 1 unsafe void runReadThread(int tid) { using var session = store.NewSession>(new SpanByteFunctions()); + var bContext = session.BasicContext; Span keyVec = stackalloc byte[sizeof(long)]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -999,11 +1016,11 @@ unsafe void runReadThread(int tid) SpanByteAndMemory output = default; Assert.IsTrue(BitConverter.TryWriteBytes(keyVec, ii)); - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); bool wasPending = status.IsPending; if (wasPending) { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs, out var recordMetadata); Assert.AreEqual(recordMetadata.Address == Constants.kInvalidAddress, status.Record.CopiedToReadCache, $"key {ii}: {status}"); } @@ -1022,6 +1039,7 @@ unsafe void runReadThread(int tid) unsafe void runUpdateThread(int tid) { using var session = store.NewSession>(new RmwSpanByteFunctions()); + var bContext = session.BasicContext; Span keyVec = stackalloc byte[sizeof(long)]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -1038,13 +1056,13 @@ unsafe void runUpdateThread(int tid) Assert.IsTrue(BitConverter.TryWriteBytes(keyVec, ii)); Assert.IsTrue(BitConverter.TryWriteBytes(inputVec, ii + valueAdd)); var status = updateOp == UpdateOp.RMW - ? session.RMW(ref key, ref input, ref output) - : session.Upsert(ref key, ref input, ref input, ref output); + ? bContext.RMW(ref key, ref input, ref output) + : bContext.Upsert(ref key, ref input, ref input, ref output); bool wasPending = status.IsPending; if (wasPending) { Assert.AreNotEqual(UpdateOp.Upsert, updateOp, "Upsert should not go pending"); - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); (status, output) = GetSinglePendingResult(completedOutputs); // Record may have been updated in-place if a CTT was done during the pending operation. diff --git a/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs b/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs index bf8e9f8129..905c4cc7a8 100644 --- a/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs +++ b/libs/storage/Tsavorite/cs/test/RecoveryChecks.cs @@ -3,7 +3,6 @@ using System; using System.IO; -using System.Linq; using System.Threading.Tasks; using NUnit.Framework; using Tsavorite.core; @@ -43,7 +42,7 @@ protected void BaseTearDown() TestUtils.DeleteDirectory(TestUtils.MethodTestDir); } - public class MyFunctions : SimpleFunctions + public class MyFunctions : SimpleSimpleFunctions { public override void ReadCompletionCallback(ref long key, ref long input, ref long output, Empty ctx, Status status, RecordMetadata recordMetadata) { @@ -52,7 +51,7 @@ public override void ReadCompletionCallback(ref long key, ref long input, ref lo } } - public class MyFunctions2 : SimpleFunctions + public class MyFunctions2 : SimpleSimpleFunctions { public override void ReadCompletionCallback(ref long key, ref long input, ref long output, Empty ctx, Status status, RecordMetadata recordMetadata) { @@ -93,9 +92,11 @@ public async ValueTask RecoveryCheck1([Values] CheckpointType checkpointType, [V ); using var s1 = store1.NewSession(new MyFunctions()); + var bc1 = s1.BasicContext; + for (long key = 0; key < 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } if (useReadCache) @@ -104,14 +105,14 @@ public async ValueTask RecoveryCheck1([Values] CheckpointType checkpointType, [V for (long key = 0; key < 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); } var task = store1.TakeFullCheckpointAsync(checkpointType); @@ -138,17 +139,18 @@ public async ValueTask RecoveryCheck1([Values] CheckpointType checkpointType, [V Assert.AreEqual(store1.Log.TailAddress, store2.Log.TailAddress); using var s2 = store2.NewSession(new MyFunctions()); + var bc2 = s2.BasicContext; for (long key = 0; key < 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -172,7 +174,8 @@ public async ValueTask RecoveryCheck2([Values] CheckpointType checkpointType, [V checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir } ); - using var s1 = store1.NewSession>(new SimpleFunctions()); + using var s1 = store1.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; using var store2 = new TsavoriteKV (size, @@ -184,7 +187,7 @@ public async ValueTask RecoveryCheck2([Values] CheckpointType checkpointType, [V { for (long key = 1000 * i; key < 1000 * i + 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } if (useReadCache) @@ -193,14 +196,14 @@ public async ValueTask RecoveryCheck2([Values] CheckpointType checkpointType, [V for (long key = 1000 * i; key < 1000 * i + 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); } var task = store1.TakeHybridLogCheckpointAsync(checkpointType); @@ -220,18 +223,19 @@ public async ValueTask RecoveryCheck2([Values] CheckpointType checkpointType, [V Assert.AreEqual(store1.Log.ReadOnlyAddress, store2.Log.ReadOnlyAddress); Assert.AreEqual(store1.Log.TailAddress, store2.Log.TailAddress); - using var s2 = store2.NewSession>(new SimpleFunctions()); + using var s2 = store2.NewSession>(new SimpleSimpleFunctions()); + var bc2 = s2.BasicContext; for (long key = 0; key < 1000 * i + 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -252,11 +256,12 @@ public void RecoveryCheck2Repeated([Values] CheckpointType checkpointType) if (i > 0) store.Recover(default, token); - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; for (long key = 1000 * i; key < 1000 * i + 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } var task = store.TakeHybridLogCheckpointAsync(checkpointType); @@ -264,18 +269,20 @@ public void RecoveryCheck2Repeated([Values] CheckpointType checkpointType) (success, token) = task.AsTask().GetAwaiter().GetResult(); Assert.IsTrue(success); - using var s2 = store.NewSession>(new SimpleFunctions()); + using var s2 = store.NewSession>(new SimpleSimpleFunctions()); + var bc2 = s2.BasicContext; + for (long key = 0; key < 1000 * i + 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -289,11 +296,12 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir } ); - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; for (long key = 0; key < 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } var task = store.TakeHybridLogCheckpointAsync(checkpointType); @@ -303,14 +311,14 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 0; key < 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePendingWithOutputs(out var completedOutputs, true); + bc1.CompletePendingWithOutputs(out var completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.Found); @@ -320,7 +328,7 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 1000; key < 2000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } // Reset store to empty state @@ -329,13 +337,13 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 0; key < 2000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.NotFound, $"status = {status}"); } } - s1.CompletePendingWithOutputs(out completedOutputs, true); + bc1.CompletePendingWithOutputs(out completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.NotFound); @@ -348,14 +356,14 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 0; key < 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePendingWithOutputs(out completedOutputs, true); + bc1.CompletePendingWithOutputs(out completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.Found); @@ -366,13 +374,13 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 1000; key < 2000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.NotFound, $"status = {status}"); } } - s1.CompletePendingWithOutputs(out completedOutputs, true); + bc1.CompletePendingWithOutputs(out completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.NotFound); @@ -381,13 +389,13 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) for (long key = 1000; key < 2000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } for (long key = 0; key < 2000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); @@ -395,7 +403,7 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) } else { - s1.CompletePendingWithOutputs(out completedOutputs, true); + bc1.CompletePendingWithOutputs(out completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.Found); @@ -404,7 +412,7 @@ public void RecoveryRollback([Values] CheckpointType checkpointType) completedOutputs.Dispose(); } } - s1.CompletePendingWithOutputs(out completedOutputs, true); + bc1.CompletePendingWithOutputs(out completedOutputs, true); while (completedOutputs.Next()) { Assert.IsTrue(completedOutputs.Current.Status.Found); @@ -433,7 +441,8 @@ public async ValueTask RecoveryCheck3([Values] CheckpointType checkpointType, [V checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir } ); - using var s1 = store1.NewSession>(new SimpleFunctions()); + using var s1 = store1.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; using var store2 = new TsavoriteKV (size, @@ -445,7 +454,7 @@ public async ValueTask RecoveryCheck3([Values] CheckpointType checkpointType, [V { for (long key = 1000 * i; key < 1000 * i + 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } if (useReadCache) @@ -454,14 +463,14 @@ public async ValueTask RecoveryCheck3([Values] CheckpointType checkpointType, [V for (long key = 1000 * i; key < 1000 * i + 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); } var task = store1.TakeFullCheckpointAsync(checkpointType); @@ -481,18 +490,19 @@ public async ValueTask RecoveryCheck3([Values] CheckpointType checkpointType, [V Assert.AreEqual(store1.Log.ReadOnlyAddress, store2.Log.ReadOnlyAddress); Assert.AreEqual(store1.Log.TailAddress, store2.Log.TailAddress); - using var s2 = store2.NewSession>(new SimpleFunctions()); + using var s2 = store2.NewSession>(new SimpleSimpleFunctions()); + var bc2 = s2.BasicContext; for (long key = 0; key < 1000 * i + 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -517,7 +527,8 @@ public async ValueTask RecoveryCheck4([Values] CheckpointType checkpointType, [V checkpointSettings: new CheckpointSettings { CheckpointDir = TestUtils.MethodTestDir } ); - using var s1 = store1.NewSession>(new SimpleFunctions()); + using var s1 = store1.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; using var store2 = new TsavoriteKV (size, @@ -529,7 +540,7 @@ public async ValueTask RecoveryCheck4([Values] CheckpointType checkpointType, [V { for (long key = 1000 * i; key < 1000 * i + 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } if (useReadCache) @@ -538,14 +549,14 @@ public async ValueTask RecoveryCheck4([Values] CheckpointType checkpointType, [V for (long key = 1000 * i; key < 1000 * i + 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); } if (i == 0) @@ -568,18 +579,19 @@ public async ValueTask RecoveryCheck4([Values] CheckpointType checkpointType, [V Assert.AreEqual(store1.Log.ReadOnlyAddress, store2.Log.ReadOnlyAddress); Assert.AreEqual(store1.Log.TailAddress, store2.Log.TailAddress); - using var s2 = store2.NewSession>(new SimpleFunctions()); + using var s2 = store2.NewSession>(new SimpleSimpleFunctions()); + var bc2 = s2.BasicContext; for (long key = 0; key < 1000 * i + 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -607,9 +619,10 @@ public async ValueTask RecoveryCheck5([Values] CheckpointType checkpointType, [V ); using var s1 = store1.NewSession(new MyFunctions()); + var bc1 = s1.BasicContext; for (long key = 0; key < 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } if (useReadCache) @@ -618,14 +631,14 @@ public async ValueTask RecoveryCheck5([Values] CheckpointType checkpointType, [V for (long key = 0; key < 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); } store1.GrowIndex(); @@ -633,14 +646,14 @@ public async ValueTask RecoveryCheck5([Values] CheckpointType checkpointType, [V for (long key = 0; key < 1000; key++) { long output = default; - var status = s1.Read(ref key, ref output); + var status = bc1.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s1.CompletePending(true); + bc1.CompletePending(true); var task = store1.TakeFullCheckpointAsync(checkpointType); @@ -666,17 +679,19 @@ public async ValueTask RecoveryCheck5([Values] CheckpointType checkpointType, [V Assert.AreEqual(store1.Log.TailAddress, store2.Log.TailAddress); using var s2 = store2.NewSession(new MyFunctions()); + var bc2 = s2.BasicContext; + for (long key = 0; key < 1000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { Assert.IsTrue(status.Found, $"status = {status}"); Assert.AreEqual(key, output, $"output = {output}"); } } - s2.CompletePending(true); + bc2.CompletePending(true); } } @@ -724,9 +739,10 @@ private async ValueTask IncrSnapshotRecoveryCheck(ICheckpointManager checkpointM ); using var s1 = store1.NewSession(new MyFunctions2()); + var bc1 = s1.BasicContext; for (long key = 0; key < 1000; key++) { - s1.Upsert(ref key, ref key); + bc1.Upsert(ref key, ref key); } var task = store1.TakeHybridLogCheckpointAsync(CheckpointType.Snapshot); @@ -734,7 +750,7 @@ private async ValueTask IncrSnapshotRecoveryCheck(ICheckpointManager checkpointM for (long key = 950; key < 1000; key++) { - s1.Upsert(key, key + 1); + bc1.Upsert(key, key + 1); } var version1 = store1.CurrentVersion; @@ -746,7 +762,7 @@ private async ValueTask IncrSnapshotRecoveryCheck(ICheckpointManager checkpointM for (long key = 1000; key < 2000; key++) { - s1.Upsert(key, key + 1); + bc1.Upsert(key, key + 1); } var version2 = store1.CurrentVersion; @@ -768,16 +784,18 @@ private async ValueTask IncrSnapshotRecoveryCheck(ICheckpointManager checkpointM Assert.AreEqual(store2.Log.TailAddress, store1.Log.TailAddress); using var s2 = store2.NewSession(new MyFunctions2()); + var bc2 = s2.BasicContext; + for (long key = 0; key < 2000; key++) { long output = default; - var status = s2.Read(ref key, ref output); + var status = bc2.Read(ref key, ref output); if (!status.IsPending) { MyFunctions2.Verify(status, key, output); } } - s2.CompletePending(true); + bc2.CompletePending(true); // Test that we can recover to earlier version using var store3 = new TsavoriteKV @@ -790,16 +808,17 @@ private async ValueTask IncrSnapshotRecoveryCheck(ICheckpointManager checkpointM Assert.IsTrue(store3.EntryCount == 1000); using var s3 = store3.NewSession(new MyFunctions2()); + var bc3 = s3.BasicContext; for (long key = 0; key < 1000; key++) { long output = default; - var status = s3.Read(ref key, ref output); + var status = bc3.Read(ref key, ref output); if (!status.IsPending) { MyFunctions2.Verify(status, key, output); } } - s3.CompletePending(true); + bc3.CompletePending(true); } } } \ No newline at end of file diff --git a/libs/storage/Tsavorite/cs/test/RecoveryTestTypes.cs b/libs/storage/Tsavorite/cs/test/RecoveryTestTypes.cs index ee8d488b84..7a9c4662f9 100644 --- a/libs/storage/Tsavorite/cs/test/RecoveryTestTypes.cs +++ b/libs/storage/Tsavorite/cs/test/RecoveryTestTypes.cs @@ -39,7 +39,7 @@ public struct Output public override string ToString() => value.ToString(); } - public class Functions : FunctionsBase + public class Functions : SessionFunctionsBase { // Read functions public override bool SingleReader(ref AdId key, ref AdInput input, ref NumClicks value, ref Output dst, ref ReadInfo readInfo) diff --git a/libs/storage/Tsavorite/cs/test/RecoveryTests.cs b/libs/storage/Tsavorite/cs/test/RecoveryTests.cs index a1a86952c1..879cc03042 100644 --- a/libs/storage/Tsavorite/cs/test/RecoveryTests.cs +++ b/libs/storage/Tsavorite/cs/test/RecoveryTests.cs @@ -139,20 +139,21 @@ private void Populate(Action checkpointAction) // Register thread with Tsavorite using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; // Process the batch of input data for (int i = 0; i < numOps; i++) { - session.RMW(ref inputArray[i].adId, ref inputArray[i], Empty.Default); + bContext.RMW(ref inputArray[i].adId, ref inputArray[i], Empty.Default); checkpointAction(i); if (i % completePendingInterval == 0) - session.CompletePending(false); + bContext.CompletePending(false); } // Make sure operations are completed - session.CompletePending(true); + bContext.CompletePending(true); } private async ValueTask RecoverAndTestAsync(int tokenIndex, bool isAsync) @@ -175,7 +176,8 @@ private async ValueTask RecoverAndTestAsync(int tokenIndex, bool isAsync) } // Register with thread - var session = store.NewSession(new Functions()); + using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; AdInput input = default; Output output = default; @@ -183,16 +185,13 @@ private async ValueTask RecoverAndTestAsync(int tokenIndex, bool isAsync) // Issue read requests for (var i = 0; i < numUniqueKeys; i++) { - var status = session.Read(ref inputArray[i].adId, ref input, ref output, Empty.Default); + var status = bContext.Read(ref inputArray[i].adId, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found, $"At tokenIndex {tokenIndex}, keyIndex {i}, AdId {inputArray[i].adId.adId}"); inputArray[i].numClicks = output.value; } // Complete all pending requests - session.CompletePending(true); - - // Release - session.Dispose(); + bContext.CompletePending(true); } } @@ -328,11 +327,12 @@ private async ValueTask RunTest(Action> populat private void Populate(TsavoriteKV store) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < DeviceTypeRecoveryTests.numOps; i++) - session.Upsert(i % DeviceTypeRecoveryTests.numUniqueKeys, i); - session.CompletePending(true); + bContext.Upsert(i % DeviceTypeRecoveryTests.numUniqueKeys, i); + bContext.CompletePending(true); } static int GetRandomLength(Random r) => r.Next(StackAllocMax) + 1; // +1 to remain in range 1..StackAllocMax @@ -340,6 +340,8 @@ private void Populate(TsavoriteKV store) private unsafe void Populate(TsavoriteKV store) { using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; + Random rng = new(RandSeed); // Single alloc outside the loop, to the max length we'll need. @@ -361,22 +363,23 @@ private unsafe void Populate(TsavoriteKV store) valueSpan[j] = i; var valueSpanByte = valueSpan.Slice(0, len).AsSpanByte(); - session.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); + bContext.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); } - session.CompletePending(true); + bContext.CompletePending(true); } private unsafe void Populate(TsavoriteKV store) { using var session = store.NewSession(new MyFunctions2()); + var bContext = session.BasicContext; for (int i = 0; i < DeviceTypeRecoveryTests.numOps; i++) { var key = new MyValue { value = i % (int)DeviceTypeRecoveryTests.numUniqueKeys }; var value = new MyValue { value = i }; - session.Upsert(key, value); + bContext.Upsert(key, value); } - session.CompletePending(true); + bContext.CompletePending(true); } private async ValueTask Checkpoint(TsavoriteKV store, bool isAsync) @@ -403,11 +406,12 @@ private async ValueTask RecoverAndReadTest(TsavoriteKV store, bool i private static void Read(TsavoriteKV store) { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSimpleFunctions()); + var bContext = session.BasicContext; for (var i = 0; i < DeviceTypeRecoveryTests.numUniqueKeys; i++) { - var status = session.Read(i % DeviceTypeRecoveryTests.numUniqueKeys, default, out long output); + var status = bContext.Read(i % DeviceTypeRecoveryTests.numUniqueKeys, default, out long output); Assert.IsTrue(status.Found, $"keyIndex {i}"); Assert.AreEqual(ExpectedValue(i), output); } @@ -422,6 +426,7 @@ private async ValueTask RecoverAndReadTest(TsavoriteKV store private static void Read(TsavoriteKV store) { using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; Random rng = new(RandSeed); @@ -435,7 +440,7 @@ private static void Read(TsavoriteKV store) var len = GetRandomLength(rng); int[] output = null; - var status = session.Read(ref keySpanByte, ref output, Empty.Default); + var status = bContext.Read(ref keySpanByte, ref output, Empty.Default); Assert.IsTrue(status.Found); for (int j = 0; j < len; j++) @@ -452,11 +457,12 @@ private async ValueTask RecoverAndReadTest(TsavoriteKV store, private static void Read(TsavoriteKV store) { using var session = store.NewSession(new MyFunctions2()); + var bContext = session.BasicContext; for (var i = 0; i < DeviceTypeRecoveryTests.numUniqueKeys; i++) { var key = new MyValue { value = i }; - var status = session.Read(key, default, out MyOutput output); + var status = bContext.Read(key, default, out MyOutput output); Assert.IsTrue(status.Found, $"keyIndex {i}"); Assert.AreEqual(ExpectedValue(i), output.value.value); } diff --git a/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs b/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs index c997b508c4..3ddce48763 100644 --- a/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs +++ b/libs/storage/Tsavorite/cs/test/ReproReadCacheTest.cs @@ -176,6 +176,7 @@ void LocalRun(int startKey, int endKey) { // Write the values first (single-threaded, all keys) var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; for (int i = 0; i < MaxKeys; i++) { var keyString = $"{i}"; @@ -186,7 +187,7 @@ void LocalRun(int startKey, int endKey) { var sbKey = SpanByte.FromPinnedSpan(key); var sbValue = SpanByte.FromPinnedSpan(value); - var status = session.Upsert(sbKey, sbValue); + var status = bContext.Upsert(sbKey, sbValue); Assert.IsTrue(!status.Found && status.Record.Created, status.ToString()); } } diff --git a/libs/storage/Tsavorite/cs/test/RevivificationTests.cs b/libs/storage/Tsavorite/cs/test/RevivificationTests.cs index 9df022cef5..1f1d5a7e36 100644 --- a/libs/storage/Tsavorite/cs/test/RevivificationTests.cs +++ b/libs/storage/Tsavorite/cs/test/RevivificationTests.cs @@ -170,7 +170,7 @@ internal RevivificationSpanByteComparer(CollisionRange range) [TestFixture] class RevivificationFixedLenTests { - internal class RevivificationFixedLenFunctions : SimpleFunctions + internal class RevivificationFixedLenFunctions : SimpleSimpleFunctions { } @@ -181,6 +181,7 @@ internal class RevivificationFixedLenFunctions : SimpleFunctions private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; [SetUp] @@ -220,6 +221,7 @@ public void Setup() concurrencyControlMode: concurrencyControlMode, revivificationSettings: revivificationSettings); functions = new RevivificationFixedLenFunctions(); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -239,7 +241,7 @@ void Populate() { for (int key = 0; key < numRecords; key++) { - var status = session.Upsert(key, key * valueMult); + var status = bContext.Upsert(key, key * valueMult); Assert.IsTrue(status.Record.Created, status.ToString()); } } @@ -262,7 +264,7 @@ public void SimpleFixedLenTest([Values] DeleteDest deleteDest, [Values(UpdateOp. RevivificationTestUtils.AssertElidable(store, deleteKey); var tailAddress = store.Log.TailAddress; - session.Delete(deleteKey); + bContext.Delete(deleteKey); Assert.AreEqual(tailAddress, store.Log.TailAddress); var updateKey = deleteDest == DeleteDest.InChain ? deleteKey : numRecords + 1; @@ -275,9 +277,9 @@ public void SimpleFixedLenTest([Values] DeleteDest deleteDest, [Values(UpdateOp. } if (updateOp == UpdateOp.Upsert) - session.Upsert(updateKey, updateValue); + bContext.Upsert(updateKey, updateValue); else if (updateOp == UpdateOp.RMW) - session.RMW(updateKey, updateValue); + bContext.RMW(updateKey, updateValue); if (!stayInChain) RevivificationTestUtils.WaitForRecords(store, want: false); @@ -298,7 +300,7 @@ public void UnelideTest([Values] RecordElision elision, [Values(UpdateOp.Upsert, // First delete all keys. This will overflow the bin. for (var key = 0; key < numRecords; ++key) { - session.Delete(key); + bContext.Delete(key); Assert.AreEqual(tailAddress, store.Log.TailAddress); } @@ -310,9 +312,9 @@ public void UnelideTest([Values] RecordElision elision, [Values(UpdateOp.Upsert, { var value = key + valueMult; if (updateOp == UpdateOp.Upsert) - session.Upsert(key, value); + bContext.Upsert(key, value); else if (updateOp == UpdateOp.RMW) - session.RMW(key, value); + bContext.RMW(key, value); } // Now re-add the keys. For the elision case, we should see tailAddress grow sharply as only the records in the bin are available @@ -338,11 +340,11 @@ public void SimpleMinAddressAddTest([Values] RevivifiableFraction revivifiableFr Populate(); // This should not go to FreeList because it's below the RevivifiableFraction - Assert.IsTrue(session.Delete(2).Found); + Assert.IsTrue(bContext.Delete(2).Found); Assert.AreEqual(0, RevivificationTestUtils.GetFreeRecordCount(store)); // This should go to FreeList because it's above the RevivifiableFraction - Assert.IsTrue(session.Delete(numRecords - 1).Found); + Assert.IsTrue(bContext.Delete(numRecords - 1).Found); Assert.AreEqual(1, RevivificationTestUtils.GetFreeRecordCount(store)); } @@ -356,7 +358,7 @@ public void SimpleMinAddressTakeTest([Values] RevivifiableFraction revivifiableF Populate(); // This should go to FreeList because it's above the RevivifiableFraction - Assert.IsTrue(session.Delete(numRecords - 1).Found); + Assert.IsTrue(bContext.Delete(numRecords - 1).Found); Assert.AreEqual(1, RevivificationTestUtils.GetFreeRecordCount(store)); RevivificationTestUtils.WaitForRecords(store, want: true); @@ -367,7 +369,7 @@ public void SimpleMinAddressTakeTest([Values] RevivifiableFraction revivifiableF int maxRecord = numRecords * 2; for (int key = numRecords; key < maxRecord; key++) { - var status = session.Upsert(key, key * valueMult); + var status = bContext.Upsert(key, key * valueMult); Assert.IsTrue(status.Record.Created, status.ToString()); } @@ -377,9 +379,9 @@ public void SimpleMinAddressTakeTest([Values] RevivifiableFraction revivifiableF var tailAddress = store.Log.TailAddress; if (updateOp == UpdateOp.Upsert) - session.Upsert(maxRecord, maxRecord * valueMult); + bContext.Upsert(maxRecord, maxRecord * valueMult); else if (updateOp == UpdateOp.RMW) - session.RMW(maxRecord, maxRecord * valueMult); + bContext.RMW(maxRecord, maxRecord * valueMult); Assert.Less(tailAddress, store.Log.TailAddress, "Expected tail address to grow (record was not revivified)"); } @@ -608,6 +610,7 @@ public override void RMWCompletionCallback(ref SpanByte key, ref SpanByte input, private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; [SetUp] @@ -650,6 +653,7 @@ public void Setup() functions = new RevivificationSpanByteFunctions(store); session = store.NewSession(functions); + bContext = session.BasicContext; functions.session = session; } @@ -685,7 +689,7 @@ void Populate(int from, int to) keyVec.Fill((byte)ii); inputVec.Fill((byte)ii); functions.expectedUsedValueLengths.Enqueue(input.TotalSize); - var status = session.Upsert(ref key, ref input, ref input, ref output); + var status = bContext.Upsert(ref key, ref input, ref input, ref output); Assert.IsTrue(status.Record.Created, status.ToString()); Assert.IsEmpty(functions.expectedUsedValueLengths); } @@ -734,9 +738,9 @@ public void SpanByteNoRevivLengthTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] Up SpanByteAndMemory output = new(); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.IsEmpty(functions.expectedUsedValueLengths); if (growth == Growth.Shrink) @@ -755,9 +759,9 @@ public void SpanByteNoRevivLengthTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] Up functions.expectedUsedValueLengths.Enqueue(input.TotalSize); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.IsEmpty(functions.expectedUsedValueLengths); } } @@ -779,7 +783,7 @@ public void SpanByteSimpleTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp var key = SpanByte.FromPinnedSpan(keyVec); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(tailAddress, store.Log.TailAddress); @@ -799,9 +803,9 @@ public void SpanByteSimpleTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] UpdateOp RevivificationTestUtils.WaitForRecords(store, want: true); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.AreEqual(tailAddress, store.Log.TailAddress); } @@ -839,12 +843,12 @@ public void SpanByteIPUGrowAndRevivifyTest([Values(UpdateOp.Upsert, UpdateOp.RMW // Get a free record from a failed IPU. if (updateOp == UpdateOp.Upsert) { - var status = session.Upsert(ref key, ref input, ref input, ref output); + var status = bContext.Upsert(ref key, ref input, ref input, ref output); Assert.IsTrue(status.Record.Created, status.ToString()); } else if (updateOp == UpdateOp.RMW) { - var status = session.RMW(ref key, ref input); + var status = bContext.RMW(ref key, ref input); Assert.IsTrue(status.Record.CopyUpdated, status.ToString()); } @@ -868,12 +872,12 @@ public void SpanByteIPUGrowAndRevivifyTest([Values(UpdateOp.Upsert, UpdateOp.RMW if (updateOp == UpdateOp.Upsert) { - var status = session.Upsert(ref key, ref input, ref input, ref output); + var status = bContext.Upsert(ref key, ref input, ref input, ref output); Assert.IsTrue(status.Record.Created, status.ToString()); } else if (updateOp == UpdateOp.RMW) { - var status = session.RMW(ref key, ref input); + var status = bContext.RMW(ref key, ref input); Assert.IsTrue(status.Record.Created, status.ToString()); } @@ -899,7 +903,7 @@ public void SpanByteReadOnlyMinAddressTest([Values(UpdateOp.Upsert, UpdateOp.RMW var key = SpanByte.FromPinnedSpan(keyVec); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); Assert.AreEqual(tailAddress, store.Log.TailAddress); @@ -918,9 +922,9 @@ public void SpanByteReadOnlyMinAddressTest([Values(UpdateOp.Upsert, UpdateOp.RMW functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.Greater(store.Log.TailAddress, tailAddress); } @@ -944,7 +948,7 @@ private long PrepareDeletes(bool stayInChain, byte delAboveRO, FlushMode flushMo var delKeyBelowRO = SpanByte.FromPinnedSpan(keyVecDelBelowRO); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref delKeyBelowRO); + var status = bContext.Delete(ref delKeyBelowRO); Assert.IsTrue(status.Found, status.ToString()); if (flushMode == FlushMode.ReadOnly) @@ -966,7 +970,7 @@ private long PrepareDeletes(bool stayInChain, byte delAboveRO, FlushMode flushMo RevivificationTestUtils.AssertElidable(store, ref delKeyAboveRO); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - status = session.Delete(ref delKeyAboveRO); + status = bContext.Delete(ref delKeyAboveRO); Assert.IsTrue(status.Found, status.ToString()); if (stayInChain) @@ -1058,9 +1062,9 @@ public void SpanByteUpdateRevivifyTest([Values] DeleteDest deleteDest, [Values] functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref keyToTest, ref input, ref input, ref output); + bContext.Upsert(ref keyToTest, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref keyToTest, ref input); + bContext.RMW(ref keyToTest, ref input); if (expectReviv) Assert.AreEqual(tailAddress, store.Log.TailAddress); @@ -1090,7 +1094,7 @@ public void SimpleRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp. RevivificationTestUtils.AssertElidable(store, ref key); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); var tailAddress = store.Log.TailAddress; @@ -1107,9 +1111,9 @@ public void SimpleRevivifyTest([Values] DeleteDest deleteDest, [Values(UpdateOp. // Revivify in the chain. Because this stays in the chain, the expectedFullValueLength is roundup(InitialLength) functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.AreEqual(tailAddress, store.Log.TailAddress); } @@ -1139,7 +1143,7 @@ public void DeleteEntireChainAndRevivifyTest([Values(CollisionRange.Ten)] Collis continue; functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); if (ii > RevivificationTestUtils.GetMinRevivifiableKey(store, numRecords)) deletedSlots.Add((byte)ii); @@ -1165,9 +1169,9 @@ public void DeleteEntireChainAndRevivifyTest([Values(CollisionRange.Ten)] Collis functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.AreEqual(tailAddress, store.Log.TailAddress); } } @@ -1196,7 +1200,7 @@ public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] Collis keyVec.Fill((byte)ii); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); } Assert.AreEqual(tailAddress, store.Log.TailAddress); @@ -1223,15 +1227,15 @@ public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] Collis functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); if (ii < revivifiableKeyCount) Assert.AreEqual(tailAddress, store.Log.TailAddress, $"unexpected new record for key {ii}"); else Assert.Less(tailAddress, store.Log.TailAddress, $"unexpected revivified record for key {ii}"); - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); Assert.IsTrue(status.Found, $"Expected to find key {ii}; status == {status}"); } @@ -1242,7 +1246,7 @@ public void DeleteAllRecordsAndRevivifyTest([Values(CollisionRange.None)] Collis for (var ii = 0; ii < numRecords; ++ii) { keyVec.Fill((byte)ii); - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); Assert.IsTrue(status.Found, $"Expected to find key {ii}; status == {status}"); } } @@ -1264,7 +1268,7 @@ public void DeleteAllRecordsAndTakeSnapshotTest([Values(ConcurrencyControlMode.L keyVec.Fill((byte)ii); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); } Assert.AreEqual(RevivificationTestUtils.GetRevivifiableRecordCount(store, numRecords), RevivificationTestUtils.GetFreeRecordCount(store), $"Expected numRecords ({numRecords}) free records"); @@ -1291,7 +1295,7 @@ public void DeleteAllRecordsAndIterateTest([Values(ConcurrencyControlMode.LockTa RevivificationTestUtils.AssertElidable(store, ref key); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); } Assert.AreEqual(RevivificationTestUtils.GetRevivifiableRecordCount(store, numRecords), RevivificationTestUtils.GetFreeRecordCount(store), $"Expected numRecords ({numRecords}) free records"); @@ -1418,7 +1422,7 @@ public unsafe void LiveBinWrappingTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U inputVec.Fill((byte)ii); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, $"{status} for key {ii}"); //Assert.AreEqual(ii + 1, RevivificationTestUtils.GetFreeRecordCount(store), $"mismatched free record count for key {ii}, pt 1"); } @@ -1443,9 +1447,9 @@ public unsafe void LiveBinWrappingTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U SpanByteAndMemory output = new(); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); output.Memory?.Dispose(); if (deleteDest == DeleteDest.FreeList && waitMode == WaitMode.Wait && tailAddress != store.Log.TailAddress) @@ -1495,7 +1499,7 @@ public void LiveBinWrappingNoRevivTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U inputVec.Fill((byte)ii); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(iter == 0 ? InitialLength : InitialLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, $"{status} for key {ii}, iter {iter}"); } @@ -1508,9 +1512,9 @@ public void LiveBinWrappingNoRevivTest([Values(UpdateOp.Upsert, UpdateOp.RMW)] U SpanByteAndMemory output = new(); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); output.Memory?.Dispose(); } } @@ -1552,13 +1556,13 @@ public void SimpleOversizeRevivifyTest([Values] DeleteDest deleteDest, [Values(U // Initial insert of the oversize record if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); // Delete it functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(OversizeLength)); - var status = session.Delete(ref key); + var status = bContext.Delete(ref key); Assert.IsTrue(status.Found, status.ToString()); if (!stayInChain) RevivificationTestUtils.WaitForRecords(store, want: true); @@ -1568,9 +1572,9 @@ public void SimpleOversizeRevivifyTest([Values] DeleteDest deleteDest, [Values(U // Revivify in the chain. Because this is oversize, the expectedFullValueLength remains the same functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(OversizeLength)); if (updateOp == UpdateOp.Upsert) - session.Upsert(ref key, ref input, ref input, ref output); + bContext.Upsert(ref key, ref input, ref input, ref output); else if (updateOp == UpdateOp.RMW) - session.RMW(ref key, ref input); + bContext.RMW(ref key, ref input); Assert.AreEqual(tailAddress, store.Log.TailAddress); } @@ -1620,9 +1624,9 @@ public void SimplePendingOpsRevivifyTest([Values(CollisionRange.None)] Collision var inputSlice = SpanByte.FromPinnedSpan(spanSlice); functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - var status = session.Read(ref key, ref inputSlice, ref output); + var status = bContext.Read(ref key, ref inputSlice, ref output); Assert.IsTrue(status.IsPending, status.ToString()); - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); Assert.IsTrue(functions.readCcCalled); } else if (pendingOp == PendingOp.RMW) @@ -1635,8 +1639,8 @@ public void SimplePendingOpsRevivifyTest([Values(CollisionRange.None)] Collision functions.expectedUsedValueLengths.Enqueue(SpanByteTotalSize(InitialLength)); - session.RMW(ref key, ref input); - session.CompletePending(wait: true); + bContext.RMW(ref key, ref input); + bContext.CompletePending(wait: true); Assert.IsTrue(functions.rmwCcCalled); } Assert.AreEqual(tailAddress, store.Log.TailAddress); @@ -1652,6 +1656,7 @@ class RevivificationObjectTests private MyFunctions functions; private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; private IDevice objlog; @@ -1680,6 +1685,7 @@ public void Setup() functions = new MyFunctions(); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -1703,7 +1709,7 @@ void Populate() { var keyObj = new MyKey { key = key }; var valueObj = new MyValue { value = key + valueMult }; - var status = session.Upsert(keyObj, valueObj); + var status = bContext.Upsert(keyObj, valueObj); Assert.IsTrue(status.Record.Created, status.ToString()); } } @@ -1719,7 +1725,7 @@ public void SimpleObjectTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Up var deleteKey = RevivificationTestUtils.GetMinRevivifiableKey(store, numRecords); var tailAddress = store.Log.TailAddress; - session.Delete(new MyKey { key = deleteKey }); + bContext.Delete(new MyKey { key = deleteKey }); Assert.AreEqual(tailAddress, store.Log.TailAddress); var updateKey = deleteDest == DeleteDest.InChain ? deleteKey : numRecords + 1; @@ -1732,9 +1738,9 @@ public void SimpleObjectTest([Values] DeleteDest deleteDest, [Values(UpdateOp.Up Assert.IsTrue(RevivificationTestUtils.HasRecords(store.RevivificationManager.FreeRecordPool), "Expected a free record after delete and WaitForRecords"); if (updateOp == UpdateOp.Upsert) - session.Upsert(key, value); + bContext.Upsert(key, value); else if (updateOp == UpdateOp.RMW) - session.RMW(key, input); + bContext.RMW(key, input); RevivificationTestUtils.WaitForRecords(store, want: false); Assert.AreEqual(tailAddress, store.Log.TailAddress, "Expected tail address not to grow (record was revivified)"); @@ -1826,6 +1832,7 @@ public override unsafe bool ConcurrentDeleter(ref SpanByte key, ref SpanByte val private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; [SetUp] @@ -1856,6 +1863,7 @@ public void Setup() functions = new RevivificationStressFunctions(keyComparer: null); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -1886,7 +1894,7 @@ unsafe void Populate() keyVec.Fill((byte)ii); inputVec.Fill((byte)ii); - var status = session.Upsert(ref key, ref input, ref input, ref output); + var status = bContext.Upsert(ref key, ref input, ref input, ref output); Assert.IsTrue(status.Record.Created, status.ToString()); } } @@ -2225,6 +2233,7 @@ unsafe void runDeleteThread(int tid) Random rng = new(tid * 101); using var localSession = store.NewSession(new RevivificationStressFunctions(keyComparer: null)); + var localbContext = localSession.BasicContext; Span keyVec = stackalloc byte[KeyLength]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -2235,7 +2244,7 @@ unsafe void runDeleteThread(int tid) { var kk = rng.Next(keyRange); keyVec.Fill((byte)kk); - localSession.Delete(key); + localbContext.Delete(key); } } } @@ -2252,6 +2261,7 @@ unsafe void runUpdateThread(int tid) RevivificationStressFunctions localFunctions = new(keyComparer: store.comparer); using var localSession = store.NewSession(localFunctions); + var localbContext = localSession.BasicContext; for (var iteration = 0; iteration < numIterations; ++iteration) { @@ -2263,15 +2273,15 @@ unsafe void runUpdateThread(int tid) localSession.functions.expectedKey = key; if (updateOp == UpdateOp.Upsert) - localSession.Upsert(key, input); + localbContext.Upsert(key, input); else - localSession.RMW(key, input); + localbContext.RMW(key, input); localSession.functions.expectedKey = default; } // Clear keyComparer so it does not try to validate during CompletePending (when it doesn't have an expectedKey) localFunctions.keyComparer = null; - localSession.CompletePending(wait: true); + localbContext.CompletePending(wait: true); localFunctions.keyComparer = store.comparer; } } @@ -2311,6 +2321,7 @@ unsafe void runDeleteThread(int tid) Random rng = new(tid * 101); using var localSession = store.NewSession(new RevivificationStressFunctions(keyComparer: null)); + var localbContext = localSession.BasicContext; Span keyVec = stackalloc byte[KeyLength]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -2321,7 +2332,7 @@ unsafe void runDeleteThread(int tid) { var kk = threadingPattern == ThreadingPattern.RandomKeys ? rng.Next(numRecords) : ii; keyVec.Fill((byte)kk); - localSession.Delete(key); + localbContext.Delete(key); } } } @@ -2338,6 +2349,7 @@ unsafe void runUpdateThread(int tid) RevivificationStressFunctions localFunctions = new(keyComparer: store.comparer); using var localSession = store.NewSession(localFunctions); + var localbContext = localSession.BasicContext; for (var iteration = 0; iteration < numIterations; ++iteration) { @@ -2349,15 +2361,15 @@ unsafe void runUpdateThread(int tid) localSession.functions.expectedKey = key; if (updateOp == UpdateOp.Upsert) - localSession.Upsert(key, input); + localbContext.Upsert(key, input); else - localSession.RMW(key, input); + localbContext.RMW(key, input); localSession.functions.expectedKey = default; } // Clear keyComparer so it does not try to validate during CompletePending (when it doesn't have an expectedKey) localFunctions.keyComparer = null; - localSession.CompletePending(wait: true); + localbContext.CompletePending(wait: true); localFunctions.keyComparer = store.comparer; } } @@ -2396,6 +2408,7 @@ public void LiveInChainThreadStressTest([Values(CollisionRange.Ten)] CollisionRa unsafe void runDeleteThread(int tid) { using var localSession = store.NewSession(new RevivificationStressFunctions(keyComparer: null)); + var localbContext = localSession.BasicContext; Span keyVec = stackalloc byte[KeyLength]; var key = SpanByte.FromPinnedSpan(keyVec); @@ -2405,7 +2418,7 @@ unsafe void runDeleteThread(int tid) for (var ii = tid; ii < numRecords; ii += numDeleteThreads) { keyVec.Fill((byte)ii); - localSession.Delete(key); + localbContext.Delete(key); } } } @@ -2420,6 +2433,7 @@ unsafe void runUpdateThread(int tid) RevivificationStressFunctions localFunctions = new RevivificationStressFunctions(keyComparer: null); using var localSession = store.NewSession(localFunctions); + var localbContext = localSession.BasicContext; for (var iteration = 0; iteration < numIterations; ++iteration) { @@ -2430,15 +2444,15 @@ unsafe void runUpdateThread(int tid) localSession.functions.expectedKey = key; if (updateOp == UpdateOp.Upsert) - localSession.Upsert(key, input); + localbContext.Upsert(key, input); else - localSession.RMW(key, input); + localbContext.RMW(key, input); localSession.functions.expectedKey = default; } // Clear keyComparer so it does not try to validate during CompletePending (when it doesn't have an expectedKey) localFunctions.keyComparer = null; - localSession.CompletePending(wait: true); + localbContext.CompletePending(wait: true); localFunctions.keyComparer = store.comparer; } } diff --git a/libs/storage/Tsavorite/cs/test/SessionTests.cs b/libs/storage/Tsavorite/cs/test/SessionTests.cs index e86d42e0c0..b206cf196d 100644 --- a/libs/storage/Tsavorite/cs/test/SessionTests.cs +++ b/libs/storage/Tsavorite/cs/test/SessionTests.cs @@ -40,18 +40,20 @@ public void TearDown() public void SessionTest1() { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; + InputStruct input = default; OutputStruct output = default; var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -65,7 +67,9 @@ public void SessionTest1() public void SessionTest2() { using var session1 = store.NewSession(new Functions()); + var bContext1 = session1.BasicContext; using var session2 = store.NewSession(new Functions()); + var bContext2 = session2.BasicContext; InputStruct input = default; OutputStruct output = default; @@ -74,14 +78,14 @@ public void SessionTest2() var key2 = new KeyStruct { kfield1 = 15, kfield2 = 16 }; var value2 = new ValueStruct { vfield1 = 25, vfield2 = 26 }; - session1.Upsert(ref key1, ref value1, Empty.Default); - session2.Upsert(ref key2, ref value2, Empty.Default); + bContext1.Upsert(ref key1, ref value1, Empty.Default); + bContext2.Upsert(ref key2, ref value2, Empty.Default); - var status = session1.Read(ref key1, ref input, ref output, Empty.Default); + var status = bContext1.Read(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session1.CompletePendingWithOutputs(out var outputs, wait: true); + bContext1.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -89,11 +93,11 @@ public void SessionTest2() Assert.AreEqual(value1.vfield1, output.value.vfield1); Assert.AreEqual(value1.vfield2, output.value.vfield2); - status = session2.Read(ref key2, ref input, ref output, Empty.Default); + status = bContext2.Read(ref key2, ref input, ref output, Empty.Default); if (status.IsPending) { - session2.CompletePendingWithOutputs(out var outputs, wait: true); + bContext2.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -107,6 +111,8 @@ public void SessionTest2() public void SessionTest3() { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; + Task.CompletedTask.ContinueWith((t) => { InputStruct input = default; @@ -115,12 +121,12 @@ public void SessionTest3() var key1 = new KeyStruct { kfield1 = 13, kfield2 = 14 }; var value = new ValueStruct { vfield1 = 23, vfield2 = 24 }; - session.Upsert(ref key1, ref value, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -135,7 +141,9 @@ public void SessionTest3() public void SessionTest4() { using var session1 = store.NewSession(new Functions()); + var bContext1 = session1.BasicContext; using var session2 = store.NewSession(new Functions()); + var bContext2 = session2.BasicContext; var t1 = Task.CompletedTask.ContinueWith((t) => { InputStruct input = default; @@ -144,12 +152,12 @@ public void SessionTest4() var key1 = new KeyStruct { kfield1 = 14, kfield2 = 15 }; var value1 = new ValueStruct { vfield1 = 24, vfield2 = 25 }; - session1.Upsert(ref key1, ref value1, Empty.Default); - var status = session1.Read(ref key1, ref input, ref output, Empty.Default); + bContext1.Upsert(ref key1, ref value1, Empty.Default); + var status = bContext1.Read(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session1.CompletePendingWithOutputs(out var outputs, wait: true); + bContext1.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -166,13 +174,13 @@ public void SessionTest4() var key2 = new KeyStruct { kfield1 = 15, kfield2 = 16 }; var value2 = new ValueStruct { vfield1 = 25, vfield2 = 26 }; - session2.Upsert(ref key2, ref value2, Empty.Default); + bContext2.Upsert(ref key2, ref value2, Empty.Default); - var status = session2.Read(ref key2, ref input, ref output, Empty.Default); + var status = bContext2.Read(ref key2, ref input, ref output, Empty.Default); if (status.IsPending) { - session2.CompletePendingWithOutputs(out var outputs, wait: true); + bContext2.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -189,7 +197,9 @@ public void SessionTest4() [Category("TsavoriteKV")] public void SessionTest5() { + // Not 'using' as we Dispose and recreate var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; InputStruct input = default; OutputStruct output = default; @@ -197,12 +207,12 @@ public void SessionTest5() var key1 = new KeyStruct { kfield1 = 16, kfield2 = 17 }; var value1 = new ValueStruct { vfield1 = 26, vfield2 = 27 }; - session.Upsert(ref key1, ref value1, Empty.Default); - var status = session.Read(ref key1, ref input, ref output, Empty.Default); + bContext.Upsert(ref key1, ref value1, Empty.Default); + var status = bContext.Read(ref key1, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -213,26 +223,27 @@ public void SessionTest5() session.Dispose(); session = store.NewSession(new Functions()); + bContext = session.BasicContext; var key2 = new KeyStruct { kfield1 = 17, kfield2 = 18 }; var value2 = new ValueStruct { vfield1 = 27, vfield2 = 28 }; - session.Upsert(ref key2, ref value2, Empty.Default); + bContext.Upsert(ref key2, ref value2, Empty.Default); - status = session.Read(ref key2, ref input, ref output, Empty.Default); + status = bContext.Read(ref key2, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } Assert.IsTrue(status.Found); - status = session.Read(ref key2, ref input, ref output, Empty.Default); + status = bContext.Read(ref key2, ref input, ref output, Empty.Default); if (status.IsPending) { - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } diff --git a/libs/storage/Tsavorite/cs/test/SharedDirectoryTests.cs b/libs/storage/Tsavorite/cs/test/SharedDirectoryTests.cs index 1259fb8999..4b7d6f7483 100644 --- a/libs/storage/Tsavorite/cs/test/SharedDirectoryTests.cs +++ b/libs/storage/Tsavorite/cs/test/SharedDirectoryTests.cs @@ -158,6 +158,7 @@ public void TearDown() private void Populate(TsavoriteKV store) { using var session = store.NewSession(new Functions()); + var bContext = session.BasicContext; // Prepare the dataset var inputArray = new AdInput[numOps]; @@ -170,16 +171,16 @@ private void Populate(TsavoriteKV store) // Process the batch of input data for (int i = 0; i < numOps; i++) { - session.RMW(ref inputArray[i].adId, ref inputArray[i], Empty.Default); + bContext.RMW(ref inputArray[i].adId, ref inputArray[i], Empty.Default); if (i % completePendingInterval == 0) { - session.CompletePending(false); + bContext.CompletePending(false); } } // Make sure operations are completed - session.CompletePending(true); + bContext.CompletePending(true); } private void Test(TsavoriteTestInstance tsavoriteInstance, Guid checkpointToken) @@ -203,16 +204,18 @@ private void Test(TsavoriteTestInstance tsavoriteInstance, Guid checkpointToken) var output = default(Output); using var session = tsavoriteInstance.Store.NewSession(new Functions()); + var bContext = session.BasicContext; + // Issue read requests for (var i = 0; i < numUniqueKeys; i++) { - var status = session.Read(ref inputArray[i].adId, ref input, ref output, Empty.Default); + var status = bContext.Read(ref inputArray[i].adId, ref input, ref output, Empty.Default); Assert.IsTrue(status.Found); inputArray[i].numClicks = output.value; } // Complete all pending requests - session.CompletePending(true); + bContext.CompletePending(true); session.Dispose(); } diff --git a/libs/storage/Tsavorite/cs/test/SimpleAsyncTests.cs b/libs/storage/Tsavorite/cs/test/SimpleAsyncTests.cs index 01935dd53f..6fd48616db 100644 --- a/libs/storage/Tsavorite/cs/test/SimpleAsyncTests.cs +++ b/libs/storage/Tsavorite/cs/test/SimpleAsyncTests.cs @@ -52,17 +52,19 @@ public void TearDown() [Category("Smoke")] public async Task ReadAsyncMinParamTest() { - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; + for (long key = 0; key < numOps; key++) { - var r = await s1.UpsertAsync(ref key, ref key); + var r = await bc1.UpsertAsync(ref key, ref key); while (r.Status.IsPending) r = await r.CompleteAsync(); // test async version of Upsert completion } for (long key = 0; key < numOps; key++) { - var (status, output) = (await s1.ReadAsync(ref key)).Complete(); + var (status, output) = (await bc1.ReadAsync(ref key)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } @@ -75,16 +77,18 @@ public async Task ReadAsyncMinParamTestNoDefaultTest() { CancellationToken cancellationToken = default; - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; + for (long key = 0; key < numOps; key++) { - var r = await s1.UpsertAsync(ref key, ref key); + var r = await bc1.UpsertAsync(ref key, ref key); r.Complete(); // test sync version of Upsert completion } for (long key = 0; key < numOps; key++) { - var (status, output) = (await s1.ReadAsync(ref key, Empty.Default, cancellationToken)).Complete(); + var (status, output) = (await bc1.ReadAsync(ref key, Empty.Default, cancellationToken)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } @@ -96,16 +100,17 @@ public async Task ReadAsyncMinParamTestNoDefaultTest() [Category("Smoke")] public async Task ReadAsyncNoRefKeyTest() { - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; for (long key = 0; key < numOps; key++) { - var r = await s1.UpsertAsync(ref key, ref key); + var r = await bc1.UpsertAsync(ref key, ref key); r.Complete(); // test sync version of Upsert completion } for (long key = 0; key < numOps; key++) { - var (status, output) = (await s1.ReadAsync(key, Empty.Default)).Complete(); + var (status, output) = (await bc1.ReadAsync(key, Empty.Default)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } @@ -119,28 +124,29 @@ public async Task ReadAsyncRefKeyRefInputTest() Status status; long key = default, input = default, output = default; - using var s1 = store.NewSession>(new SimpleFunctions((a, b) => a + b)); + using var s1 = store.NewSession>(new SimpleSimpleFunctions((a, b) => a + b)); + var bc1 = s1.BasicContext; for (key = 0; key < numOps; key++) { - (await s1.RMWAsync(ref key, ref key)).Complete(); + (await bc1.RMWAsync(ref key, ref key)).Complete(); } for (key = 0; key < numOps; key++) { - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } key = 0; input = 35; - var t1 = s1.RMWAsync(ref key, ref input); - var t2 = s1.RMWAsync(ref key, ref input); + var t1 = bc1.RMWAsync(ref key, ref input); + var t2 = bc1.RMWAsync(ref key, ref input); (await t1).Complete(); (await t2).Complete(); // should trigger RMW re-do - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key + input + input, output); } @@ -155,29 +161,30 @@ public async Task ReadAsyncNoRefKeyNoRefInputTest() long key = default, input = default, output = default; using var s1 = store.NewSession>(new RMWSimpleFunctions((a, b) => a + b)); + var bc1 = s1.BasicContext; for (key = 0; key < numOps; key++) { - (status, output) = (await s1.RMWAsync(ref key, ref key, Empty.Default)).Complete(); + (status, output) = (await bc1.RMWAsync(ref key, ref key, Empty.Default)).Complete(); Assert.IsFalse(status.IsPending); Assert.AreEqual(key, output); } for (key = 0; key < numOps; key++) { - (status, output) = (await s1.ReadAsync(key, output)).Complete(); + (status, output) = (await bc1.ReadAsync(key, output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } key = 0; input = 9912; - var t1 = s1.RMWAsync(ref key, ref input); - var t2 = s1.RMWAsync(ref key, ref input); + var t1 = bc1.RMWAsync(ref key, ref input); + var t2 = bc1.RMWAsync(ref key, ref input); (await t1).Complete(); (await t2).Complete(); // should trigger RMW re-do - (status, output) = (await s1.ReadAsync(key, output, Empty.Default)).Complete(); + (status, output) = (await bc1.ReadAsync(key, output, Empty.Default)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key + input + input, output); } @@ -188,10 +195,11 @@ public async Task ReadAsyncNoRefKeyNoRefInputTest() [Category("Smoke")] public async Task UpsertReadDeleteReadAsyncMinParamByRefTest() { - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; for (long key = 0; key < numOps; key++) { - var r = await s1.UpsertAsync(ref key, ref key); + var r = await bc1.UpsertAsync(ref key, ref key); while (r.Status.IsPending) r = await r.CompleteAsync(); // test async version of Upsert completion } @@ -200,18 +208,18 @@ public async Task UpsertReadDeleteReadAsyncMinParamByRefTest() for (long key = 0; key < numOps; key++) { - var (status, output) = (await s1.ReadAsync(ref key)).Complete(); + var (status, output) = (await bc1.ReadAsync(ref key)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } { // Scope for variables long deleteKey = 99; - var r = await s1.DeleteAsync(ref deleteKey); + var r = await bc1.DeleteAsync(ref deleteKey); while (r.Status.IsPending) r = await r.CompleteAsync(); // test async version of Delete completion - var (status, _) = (await s1.ReadAsync(ref deleteKey)).Complete(); + var (status, _) = (await bc1.ReadAsync(ref deleteKey)).Complete(); Assert.IsFalse(status.Found); } } @@ -222,10 +230,11 @@ public async Task UpsertReadDeleteReadAsyncMinParamByRefTest() [Category("Smoke")] public async Task UpsertReadDeleteReadAsyncMinParamByValueTest() { - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; for (long key = 0; key < numOps; key++) { - var status = (await s1.UpsertAsync(key, key)).Complete(); // test sync version of Upsert completion + var status = (await bc1.UpsertAsync(key, key)).Complete(); // test sync version of Upsert completion Assert.IsFalse(status.IsPending); } @@ -233,17 +242,17 @@ public async Task UpsertReadDeleteReadAsyncMinParamByValueTest() for (long key = 0; key < numOps; key++) { - var (status, output) = (await s1.ReadAsync(key)).Complete(); + var (status, output) = (await bc1.ReadAsync(key)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } { // Scope for variables long deleteKey = 99; - var status = (await s1.DeleteAsync(deleteKey)).Complete(); // test sync version of Delete completion + var status = (await bc1.DeleteAsync(deleteKey)).Complete(); // test sync version of Delete completion Assert.IsFalse(status.IsPending); - (status, _) = (await s1.ReadAsync(deleteKey)).Complete(); + (status, _) = (await bc1.ReadAsync(deleteKey)).Complete(); Assert.IsFalse(status.Found); } } @@ -261,13 +270,15 @@ public async Task AsyncStartAddressParamTest() long recordSize = store.Log.FixedRecordSize; using var s1 = store.NewSession>(new RMWSimpleFunctions((a, b) => a + b)); + var bc1 = s1.BasicContext; + for (key = 0; key < numOps; key++) { // We can predict the address as TailAddress because we're single-threaded, *unless* a page was allocated; // in that case the new address is at the start of the newly-allocated page. Since we can't predict that, // we take advantage of knowing we have fixed-length records and that TailAddress is open-ended, so we // subtract after the insert to get record start address. - (status, output) = (await s1.RMWAsync(ref key, ref key)).Complete(); + (status, output) = (await bc1.RMWAsync(ref key, ref key)).Complete(); addresses[key] = store.Log.TailAddress - recordSize; Assert.IsFalse(status.IsPending); Assert.AreEqual(key, output); @@ -277,15 +288,15 @@ public async Task AsyncStartAddressParamTest() for (key = 0; key < numOps; key++) { readOptions = default; - (status, output) = (await s1.ReadAtAddressAsync(addresses[key], ref key, ref output, ref readOptions)).Complete(); + (status, output) = (await bc1.ReadAtAddressAsync(addresses[key], ref key, ref output, ref readOptions)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } key = 0; input = 22; - var t1 = s1.RMWAsync(ref key, ref input); - var t2 = s1.RMWAsync(ref key, ref input); + var t1 = bc1.RMWAsync(ref key, ref input); + var t2 = bc1.RMWAsync(ref key, ref input); (await t1).Complete(); (await t2).Complete(); // should trigger RMW re-do @@ -295,7 +306,7 @@ public async Task AsyncStartAddressParamTest() addresses[key] = store.Log.TailAddress - recordSize; readOptions = default; - (status, output) = (await s1.ReadAtAddressAsync(addresses[key], ref key, ref output, ref readOptions, Empty.Default)).Complete(); + (status, output) = (await bc1.ReadAtAddressAsync(addresses[key], ref key, ref output, ref readOptions, Empty.Default)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key + input + input, output); } @@ -309,29 +320,31 @@ public async Task ReadAsyncRMWAsyncNoRefTest() long key = default, input = default, output = default; using var s1 = store.NewSession>(new RMWSimpleFunctions((a, b) => a + b)); + var bc1 = s1.BasicContext; + for (key = 0; key < numOps; key++) { - var asyncResult = await (await s1.RMWAsync(key, key)).CompleteAsync(); + var asyncResult = await (await bc1.RMWAsync(key, key)).CompleteAsync(); Assert.IsFalse(asyncResult.Status.IsPending); Assert.AreEqual(key, asyncResult.Output); } for (key = 0; key < numOps; key++) { - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } key = 0; input = 35; - var t1 = s1.RMWAsync(key, input); - var t2 = s1.RMWAsync(key, input); + var t1 = bc1.RMWAsync(key, input); + var t2 = bc1.RMWAsync(key, input); (await t1).Complete(); (await t2).Complete(); // should trigger RMW re-do - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key + input + input, output); } @@ -345,30 +358,31 @@ public async Task ReadyToCompletePendingAsyncTest() Status status; long key = default, input = default, output = default; - using var s1 = store.NewSession>(new SimpleFunctions((a, b) => a + b)); + using var s1 = store.NewSession>(new SimpleSimpleFunctions((a, b) => a + b)); + var bc1 = s1.BasicContext; for (key = 0; key < numOps; key++) { - (await s1.RMWAsync(key, key)).Complete(); + (await bc1.RMWAsync(key, key)).Complete(); await s1.ReadyToCompletePendingAsync(); } for (key = 0; key < numOps; key++) { - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key, output); } key = 0; input = 35; - var t1 = s1.RMWAsync(key, input); - var t2 = s1.RMWAsync(key, input); + var t1 = bc1.RMWAsync(key, input); + var t2 = bc1.RMWAsync(key, input); (await t1).Complete(); (await t2).Complete(); // should trigger RMW re-do - (status, output) = (await s1.ReadAsync(ref key, ref output)).Complete(); + (status, output) = (await bc1.ReadAsync(ref key, ref output)).Complete(); Assert.IsTrue(status.Found); Assert.AreEqual(key + input + input, output); } @@ -380,7 +394,8 @@ public async Task ReadyToCompletePendingAsyncTest() public async Task UpsertAsyncAndRMWAsyncTest([Values] bool useRMW, [Values] bool doFlush, [Values] bool completeAsync) { - using var s1 = store.NewSession>(new SimpleFunctions()); + using var s1 = store.NewSession>(new SimpleSimpleFunctions()); + var bc1 = s1.BasicContext; async ValueTask completeRmw(TsavoriteKV.RmwAsyncResult ar) { @@ -407,9 +422,9 @@ async ValueTask completeUpsert(TsavoriteKV.UpsertAsyncResult.UpsertAsyncResult(new AdSimpleFunctions()); + var bContext1 = session1.BasicContext; + for (int key = 0; key < numOps; key++) { value.numClicks = key; - session1.Upsert(ref inputArray[key], ref value, Empty.Default); + bContext1.Upsert(ref inputArray[key], ref value, Empty.Default); } if (testCommitCookie) @@ -136,15 +138,16 @@ private async ValueTask SimpleRecoveryTest1_Worker(CheckpointType checkpointType Assert.Null(store2.RecoveredCommitCookie); var session2 = store2.NewSession(new AdSimpleFunctions()); + var bContext2 = session2.BasicContext; Assert.AreEqual(1, session2.ID); // This is the first session on the recovered store for (int key = 0; key < numOps; key++) { - var status = session2.Read(ref inputArray[key], ref inputArg, ref output, Empty.Default); + var status = bContext2.Read(ref inputArray[key], ref inputArg, ref output, Empty.Default); if (status.IsPending) { - session2.CompletePendingWithOutputs(out var outputs, wait: true); + bContext2.CompletePendingWithOutputs(out var outputs, wait: true); Assert.IsTrue(outputs.Next()); output = outputs.Current.Output; Assert.IsFalse(outputs.Next()); @@ -180,10 +183,12 @@ public async ValueTask SimpleRecoveryTest2([Values] CheckpointType checkpointTyp Output output = default; var session1 = store1.NewSession(new AdSimpleFunctions()); + var bContext1 = session1.BasicContext; + for (int key = 0; key < numOps; key++) { value.numClicks = key; - session1.Upsert(ref inputArray[key], ref value, Empty.Default); + bContext1.Upsert(ref inputArray[key], ref value, Empty.Default); } store1.TryInitiateFullCheckpoint(out Guid token, checkpointType); store1.CompleteCheckpointAsync().AsTask().GetAwaiter().GetResult(); @@ -195,12 +200,14 @@ public async ValueTask SimpleRecoveryTest2([Values] CheckpointType checkpointTyp store2.Recover(token); var session2 = store2.NewSession(new AdSimpleFunctions()); + var bContext2 = session1.BasicContext; + for (int key = 0; key < numOps; key++) { - var status = session2.Read(ref inputArray[key], ref inputArg, ref output, Empty.Default); + var status = bContext2.Read(ref inputArray[key], ref inputArg, ref output, Empty.Default); if (status.IsPending) - session2.CompletePending(true); + bContext2.CompletePending(true); else { Assert.AreEqual(key, output.value.numClicks); @@ -229,11 +236,13 @@ public async ValueTask ShouldRecoverBeginAddress([Values] bool isAsync) NumClicks value; var session1 = store1.NewSession(new AdSimpleFunctions()); + var bContext1 = session1.BasicContext; + var address = 0L; for (int key = 0; key < numOps; key++) { value.numClicks = key; - session1.Upsert(ref inputArray[key], ref value, Empty.Default); + bContext1.Upsert(ref inputArray[key], ref value, Empty.Default); if (key == 2999) address = store1.Log.TailAddress; @@ -278,15 +287,17 @@ public void SimpleReadAndUpdateInfoTest() AdSimpleFunctions functions2 = new(2); var session1 = store1.NewSession(functions1); + var bContext1 = session1.BasicContext; + for (int key = 0; key < numOps; key++) { value.numClicks = key; if ((key & 1) > 0) - session1.Upsert(ref inputArray[key], ref value, Empty.Default); + bContext1.Upsert(ref inputArray[key], ref value, Empty.Default); else { AdInput input = new() { adId = inputArray[key], numClicks = value }; - session1.RMW(ref inputArray[key], ref input); + bContext1.RMW(ref inputArray[key], ref input); } } store1.TryInitiateFullCheckpoint(out Guid token, CheckpointType.FoldOver); @@ -296,18 +307,19 @@ public void SimpleReadAndUpdateInfoTest() store2.Recover(token); var session2 = store2.NewSession(functions2); + var bContext2 = session2.BasicContext; // Just need one operation here to verify readInfo/upsertInfo in the functions var lastKey = inputArray.Length - 1; - var status = session2.Read(ref inputArray[lastKey], ref inputArg, ref output, Empty.Default); + var status = bContext2.Read(ref inputArray[lastKey], ref inputArg, ref output, Empty.Default); Assert.IsFalse(status.IsPending, status.ToString()); value.numClicks = lastKey; - status = session2.Upsert(ref inputArray[lastKey], ref value, Empty.Default); + status = bContext2.Upsert(ref inputArray[lastKey], ref value, Empty.Default); Assert.IsFalse(status.IsPending, status.ToString()); inputArg = new() { adId = inputArray[lastKey], numClicks = new NumClicks { numClicks = 0 } }; // CopyUpdater adds, so make this 0 - status = session2.RMW(ref inputArray[lastKey], ref inputArg); + status = bContext2.RMW(ref inputArray[lastKey], ref inputArg); Assert.IsFalse(status.IsPending, status.ToString()); // Now verify Pending @@ -315,25 +327,25 @@ public void SimpleReadAndUpdateInfoTest() output.value = new() { numClicks = lastKey }; inputArg.numClicks = new() { numClicks = lastKey }; - status = session2.Read(ref inputArray[lastKey], ref inputArg, ref output, Empty.Default); + status = bContext2.Read(ref inputArray[lastKey], ref inputArg, ref output, Empty.Default); Assert.IsTrue(status.IsPending, status.ToString()); - session2.CompletePending(wait: true); + bContext2.CompletePending(wait: true); // Upsert does not go pending so is skipped here --lastKey; output.value = new() { numClicks = lastKey }; inputArg.numClicks = new() { numClicks = lastKey }; - status = session2.RMW(ref inputArray[lastKey], ref inputArg); + status = bContext2.RMW(ref inputArray[lastKey], ref inputArg); Assert.IsTrue(status.IsPending, status.ToString()); - session2.CompletePending(wait: true); + bContext2.CompletePending(wait: true); session2.Dispose(); } } - public class AdSimpleFunctions : FunctionsBase + public class AdSimpleFunctions : SessionFunctionsBase { long expectedVersion; diff --git a/libs/storage/Tsavorite/cs/test/SingleWriterTests.cs b/libs/storage/Tsavorite/cs/test/SingleWriterTests.cs index 158a400943..3fd34704f3 100644 --- a/libs/storage/Tsavorite/cs/test/SingleWriterTests.cs +++ b/libs/storage/Tsavorite/cs/test/SingleWriterTests.cs @@ -8,7 +8,7 @@ namespace Tsavorite.test.SingleWriter { - internal class SingleWriterTestFunctions : SimpleFunctions + internal class SingleWriterTestFunctions : SimpleSimpleFunctions { internal WriteReason actualReason; @@ -36,6 +36,7 @@ class SingleWriterTests private TsavoriteKV store; private ClientSession session; + private BasicContext bContext; private IDevice log; [SetUp] @@ -61,6 +62,7 @@ public void Setup() store = new TsavoriteKV(1L << 20, logSettings, new CheckpointSettings { CheckpointDir = MethodTestDir }); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -80,7 +82,7 @@ void Populate() int input = (int)WriteReason.Upsert; int output = 0; for (int key = 0; key < numRecords; key++) - Assert.False(session.Upsert(key, input, key * valueMult, ref output).IsPending); + Assert.False(bContext.Upsert(key, input, key * valueMult, ref output).IsPending); } [Test] @@ -98,9 +100,9 @@ public void SingleWriterReasonsTest([Values] ReadCopyDestination readCopyDestina int key = 42; WriteReason expectedReason = readCopyDestination == ReadCopyDestination.ReadCache ? WriteReason.CopyToReadCache : WriteReason.CopyToTail; int input = (int)expectedReason; - var status = session.Read(key, input, out int output); + var status = bContext.Read(key, input, out int output); Assert.IsTrue(status.IsPending); - session.CompletePending(wait: true); + bContext.CompletePending(wait: true); Assert.AreEqual(expectedReason, functions.actualReason); functions.actualReason = NoReason; @@ -108,9 +110,9 @@ public void SingleWriterReasonsTest([Values] ReadCopyDestination readCopyDestina expectedReason = WriteReason.CopyToTail; input = (int)expectedReason; ReadOptions readOptions = new() { CopyOptions = new(ReadCopyFrom.AllImmutable, ReadCopyTo.MainLog) }; - status = session.Read(ref key, ref input, ref output, ref readOptions, out _); + status = bContext.Read(ref key, ref input, ref output, ref readOptions, out _); Assert.IsTrue(status.IsPending && !status.IsCompleted); - session.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); Assert.IsTrue(!status.IsPending && status.IsCompleted && status.IsCompletedSuccessfully); Assert.IsTrue(status.Found && !status.NotFound && status.Record.Copied); @@ -165,7 +167,7 @@ public override void Serialize(ref StructWithString obj) } } - internal class StructWithStringTestFunctions : SimpleFunctions + internal class StructWithStringTestFunctions : SimpleSimpleFunctions { } @@ -177,6 +179,7 @@ internal class StructWithStringTestFunctions : SimpleFunctions store; private ClientSession session; + private BasicContext bContext; private IDevice log, objlog; [SetUp] @@ -200,6 +203,7 @@ public void Setup() functions = new(); session = store.NewSession(functions); + bContext = session.BasicContext; } [TearDown] @@ -222,7 +226,7 @@ void Populate() { StructWithString key = new(ii, keyPrefix); StructWithString value = new(ii, valuePrefix); - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); if (ii % 3_000 == 0) { store.TakeHybridLogCheckpointAsync(CheckpointType.FoldOver).GetAwaiter().GetResult(); @@ -239,10 +243,10 @@ public void StructWithStringCompactTest([Values] CompactionType compactionType, void readKey(int keyInt) { StructWithString key = new(keyInt, keyPrefix); - var (status, output) = session.Read(key); + var (status, output) = bContext.Read(key); if (status.IsPending) { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); using (completedOutputs) (status, output) = GetSinglePendingResult(completedOutputs); } diff --git a/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs index 833c4a8aeb..c548edc0cf 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteIterationTests.cs @@ -74,6 +74,8 @@ public unsafe void SpanByteIterationBasicTest([Values] DeviceType deviceType, [V concurrencyControlMode: scanIteratorType == ScanIteratorType.Pull ? ConcurrencyControlMode.None : ConcurrencyControlMode.LockTable); using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; + SpanBytePushIterationTestFunctions scanIteratorFunctions = new(); const int totalRecords = 500; @@ -106,7 +108,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { keySpan[0] = i; valueSpan[0] = i; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } iterateAndVerify(1, totalRecords); @@ -114,7 +116,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { keySpan[0] = i; valueSpan[0] = i * 2; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } iterateAndVerify(2, totalRecords); @@ -122,7 +124,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { keySpan[0] = i; valueSpan[0] = i; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } iterateAndVerify(0, totalRecords); @@ -130,14 +132,14 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { keySpan[0] = i; valueSpan[0] = i; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } iterateAndVerify(0, totalRecords); for (int i = 0; i < totalRecords; i += 2) { keySpan[0] = i; - session.Delete(ref key); + bContext.Delete(ref key); } iterateAndVerify(0, totalRecords / 2); @@ -145,7 +147,7 @@ void iterateAndVerify(int keyMultToValue, int expectedRecs) { keySpan[0] = i; valueSpan[0] = i * 3; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } iterateAndVerify(3, totalRecords); @@ -163,6 +165,7 @@ public void SpanByteIterationPushStopTest([Values] DeviceType deviceType) (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 9, SegmentSizeBits = 22 }); using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; SpanBytePushIterationTestFunctions scanIteratorFunctions = new(); const int totalRecords = 2000; @@ -190,7 +193,7 @@ void scanAndVerify(int stopAt, bool useScan) { keySpan[0] = i; valueSpan[0] = i; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } scanAndVerify(42, useScan: true); @@ -230,11 +233,12 @@ void LocalUpdate(int tid) var value = valueSpan.AsSpanByte(); using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { keySpan[0] = i; valueSpan[0] = i * (tid + 1); - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } } @@ -247,11 +251,12 @@ void LocalUpdate(int tid) var value = valueSpan.AsSpanByte(); using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < totalRecords; i++) { keySpan[0] = i; valueSpan[0] = i; - session.Upsert(ref key, ref value); + bContext.Upsert(ref key, ref value); } } diff --git a/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs index 7e84bfe57a..f3447d3d6b 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteLogScanTests.cs @@ -85,6 +85,7 @@ public unsafe void SpanByteScanCursorTest([Values(HashModulo.NoMod, HashModulo.H const long PageSize = 1L << PageSizeBits; using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; Random rng = new(101); @@ -97,7 +98,7 @@ public unsafe void SpanByteScanCursorTest([Values(HashModulo.NoMod, HashModulo.H fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } } @@ -141,7 +142,7 @@ public unsafe void SpanByteScanCursorTest([Values(HashModulo.NoMod, HashModulo.H fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } } scanCursorFuncs.Initialize(verifyKeys); @@ -163,7 +164,7 @@ public unsafe void SpanByteScanCursorTest([Values(HashModulo.NoMod, HashModulo.H SpanByte input = default; SpanByteAndMemory output = default; ReadOptions readOptions = default; - var readStatus = session.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); + var readStatus = bContext.ReadAtAddress(store.hlog.HeadAddress, ref input, ref output, ref readOptions, out _); Assert.IsTrue(readStatus.Found, $"Could not read at HeadAddress; {readStatus}"); var keyString = new string(MemoryMarshal.Cast(output.Memory.Memory.Span)); var keyOrdinal = int.Parse(keyString.Substring(keyString.IndexOf('_') + 1)); @@ -185,6 +186,7 @@ public unsafe void SpanByteScanCursorTest([Values(HashModulo.NoMod, HashModulo.H public unsafe void SpanByteScanCursorFilterTest([Values(HashModulo.NoMod, HashModulo.Hundred)] HashModulo hashMod) { using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; Random rng = new(101); @@ -197,7 +199,7 @@ public unsafe void SpanByteScanCursorFilterTest([Values(HashModulo.NoMod, HashMo fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } } @@ -225,6 +227,7 @@ internal enum RCULocation { RCUNone, RCUBefore, RCUAfter }; public unsafe void SpanByteScanCursorWithRCUTest([Values(RCULocation.RCUBefore, RCULocation.RCUAfter)] RCULocation rcuLocation, [Values(HashModulo.NoMod, HashModulo.Hundred)] HashModulo hashMod) { using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; Random rng = new(101); @@ -237,7 +240,7 @@ public unsafe void SpanByteScanCursorWithRCUTest([Values(RCULocation.RCUBefore, fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } } @@ -304,6 +307,7 @@ unsafe void CheckForRCU() Task.Run(() => { using var session = store.NewSession(new ScanFunctions()); + var bContext = session.BasicContext; var valueFill = new string('x', 220); // Update the specified key with a longer value that requires RCU. var key = MemoryMarshal.Cast($"key_{rcuRecord}".AsSpan()); @@ -312,7 +316,7 @@ unsafe void CheckForRCU() fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } }).Wait(); @@ -371,6 +375,7 @@ public unsafe void SpanByteJumpToBeginAddressTest() using var store = new TsavoriteKV (1L << 20, new LogSettings { LogDevice = log, MemorySizeBits = 20, PageSizeBits = 15 }, concurrencyControlMode: ConcurrencyControlMode.None); using var session = store.NewSession>(new SpanByteFunctions()); + var bContext = session.BasicContext; const int numRecords = 200; const int numTailRecords = 10; @@ -390,7 +395,7 @@ public unsafe void SpanByteJumpToBeginAddressTest() fixed (byte* keyPtr = key) fixed (byte* valuePtr = value) { - session.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); + bContext.Upsert(SpanByte.FromPinnedPointer(keyPtr, key.Length), SpanByte.FromPinnedPointer(valuePtr, value.Length)); } } diff --git a/libs/storage/Tsavorite/cs/test/SpanByteTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteTests.cs index 0db6ac59ab..362b3a16ea 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteTests.cs @@ -29,7 +29,8 @@ public unsafe void SpanByteTest1() using var log = Devices.CreateLogDevice(Path.Join(TestUtils.MethodTestDir, "hlog1.log"), deleteOnClose: true); using var store = new TsavoriteKV (128, new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 12 }); - using var s = store.NewSession>(new SpanByteFunctions()); + using var session = store.NewSession>(new SpanByteFunctions()); + var bContext = session.BasicContext; var key1 = MemoryMarshal.Cast("key1".AsSpan()); var value1 = MemoryMarshal.Cast("value1".AsSpan()); @@ -41,8 +42,8 @@ public unsafe void SpanByteTest1() var key1SpanByte = SpanByte.FromPinnedPointer(key1Ptr, key1.Length); var value1SpanByte = SpanByte.FromPinnedPointer(value1Ptr, value1.Length); - s.Upsert(key1SpanByte, value1SpanByte); - s.Read(ref key1SpanByte, ref input, ref output1); + bContext.Upsert(key1SpanByte, value1SpanByte); + bContext.Read(ref key1SpanByte, ref input, ref output1); } Assert.IsTrue(output1.IsSpanByte); @@ -58,8 +59,8 @@ public unsafe void SpanByteTest1() var key2SpanByte = SpanByte.FromPinnedPointer(key2Ptr, key2.Length); var value2SpanByte = SpanByte.FromPinnedPointer(value2Ptr, value2.Length); - s.Upsert(key2SpanByte, value2SpanByte); - s.Read(ref key2SpanByte, ref input, ref output2); + bContext.Upsert(key2SpanByte, value2SpanByte); + bContext.Read(ref key2SpanByte, ref input, ref output2); } Assert.IsTrue(!output2.IsSpanByte); @@ -85,13 +86,14 @@ public unsafe void MultiRead_SpanByte_Test() size: 1L << 10, new LogSettings { LogDevice = log, MemorySizeBits = 15, PageSizeBits = 12 }); using var session = store.NewSession>(new SpanByteFunctions()); + var bContext = session.BasicContext; for (int i = 0; i < 200; i++) { var key = MemoryMarshal.Cast($"{i}".AsSpan()); var value = MemoryMarshal.Cast($"{i + 1000}".AsSpan()); fixed (byte* k = key, v = value) - session.Upsert(SpanByte.FromPinnedSpan(key), SpanByte.FromPinnedSpan(value)); + bContext.Upsert(SpanByte.FromPinnedSpan(key), SpanByte.FromPinnedSpan(value)); } // Read, evict all records to disk, read again @@ -116,14 +118,14 @@ void ReadKey(long key, long value, bool evicted) var keyBytes = MemoryMarshal.Cast($"{key}".AsSpan()); fixed (byte* _ = keyBytes) - status = session.Read(key: SpanByte.FromPinnedSpan(keyBytes), out output); + status = bContext.Read(key: SpanByte.FromPinnedSpan(keyBytes), out output); Assert.AreEqual(evicted, status.IsPending, "evicted/pending mismatch"); if (!evicted) Assert.IsTrue(status.Found, $"expected to find key; status = {status}"); else // needs to be fetched from disk { - session.CompletePendingWithOutputs(out var completedOutputs, wait: true); + bContext.CompletePendingWithOutputs(out var completedOutputs, wait: true); using (completedOutputs) { for (var count = 0; completedOutputs.Next(); ++count) @@ -210,6 +212,7 @@ public unsafe void ShouldSkipEmptySpaceAtEndOfPage() new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 10 }, // 1KB page null, null, null, concurrencyControlMode: ConcurrencyControlMode.None); using var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; const int PageSize = 1024; Span keySpan = stackalloc long[1]; @@ -259,7 +262,7 @@ void Set(ref Span keySpan, long keyValue, ref Span valueSpan, int va keySpan[0] = keyValue; value.Length = valueLength; valueSpan[0] = tag; - session.Upsert(ref key, ref value, Empty.Default); + bContext.Upsert(ref key, ref value, Empty.Default); } } } diff --git a/libs/storage/Tsavorite/cs/test/SpanByteVLVectorTests.cs b/libs/storage/Tsavorite/cs/test/SpanByteVLVectorTests.cs index 5ce474ad01..443af34496 100644 --- a/libs/storage/Tsavorite/cs/test/SpanByteVLVectorTests.cs +++ b/libs/storage/Tsavorite/cs/test/SpanByteVLVectorTests.cs @@ -28,7 +28,8 @@ public unsafe void VLVectorSingleKeyTest() (128, new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 12 }, null, null, null); - var s = store.NewSession(new VLVectorFunctions()); + var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; // Single alloc outside the loop, to the max length we'll need. Span keySpan = stackalloc int[1]; @@ -45,7 +46,7 @@ public unsafe void VLVectorSingleKeyTest() valueSpan[j] = len; var valueSpanByte = valueSpan.Slice(0, len).AsSpanByte(); - s.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); + bContext.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); } // Reset rng to get the same sequence of value lengths @@ -57,11 +58,11 @@ public unsafe void VLVectorSingleKeyTest() var valueLen = GetRandomLength(rng); int[] output = null; - var status = s.Read(ref keySpanByte, ref output, Empty.Default); + var status = bContext.Read(ref keySpanByte, ref output, Empty.Default); if (status.IsPending) { - s.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -70,7 +71,7 @@ public unsafe void VLVectorSingleKeyTest() for (int j = 0; j < valueLen; j++) Assert.AreEqual(valueLen, output[j]); } - s.Dispose(); + session.Dispose(); store.Dispose(); log.Dispose(); DeleteDirectory(MethodTestDir); @@ -88,7 +89,8 @@ public unsafe void VLVectorMultiKeyTest() (128, new LogSettings { LogDevice = log, MemorySizeBits = 17, PageSizeBits = 12 }, null, null, null); - var s = store.NewSession(new VLVectorFunctions()); + var session = store.NewSession(new VLVectorFunctions()); + var bContext = session.BasicContext; // Single alloc outside the loop, to the max length we'll need. Span keySpan = stackalloc int[StackAllocMax]; @@ -107,7 +109,7 @@ public unsafe void VLVectorMultiKeyTest() valueSpan[j] = valueLen; var valueSpanByte = valueSpan.Slice(0, valueLen).AsSpanByte(); - s.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); + bContext.Upsert(ref keySpanByte, ref valueSpanByte, Empty.Default); } // Reset rng to get the same sequence of key and value lengths @@ -121,11 +123,11 @@ public unsafe void VLVectorMultiKeyTest() var valueLen = GetRandomLength(rng); int[] output = null; - var status = s.Read(ref keySpanByte, ref output, Empty.Default); + var status = bContext.Read(ref keySpanByte, ref output, Empty.Default); if (status.IsPending) { - s.CompletePendingWithOutputs(out var outputs, wait: true); + bContext.CompletePendingWithOutputs(out var outputs, wait: true); (status, output) = GetSinglePendingResult(outputs); } @@ -135,7 +137,7 @@ public unsafe void VLVectorMultiKeyTest() Assert.AreEqual(valueLen, output[j]); } - s.Dispose(); + session.Dispose(); store.Dispose(); log.Dispose(); DeleteDirectory(MethodTestDir); diff --git a/libs/storage/Tsavorite/cs/test/StateMachineBarrierTests.cs b/libs/storage/Tsavorite/cs/test/StateMachineBarrierTests.cs index 59b314e50c..75c516d0a7 100644 --- a/libs/storage/Tsavorite/cs/test/StateMachineBarrierTests.cs +++ b/libs/storage/Tsavorite/cs/test/StateMachineBarrierTests.cs @@ -110,11 +110,12 @@ void Prepare(out SimpleFunctions f, NumClicks value; s1 = store.NewSession(f, "foo"); + var bc1 = s1.BasicContext; for (int key = 0; key < numOps; key++) { value.numClicks = key; - s1.Upsert(ref inputArray[key], ref value, Empty.Default); + bc1.Upsert(ref inputArray[key], ref value, Empty.Default); } // Ensure state machine needs no I/O wait during WAIT_FLUSH diff --git a/libs/storage/Tsavorite/cs/test/StateMachineTests.cs b/libs/storage/Tsavorite/cs/test/StateMachineTests.cs index b7bcf56af3..72c75c5ff7 100644 --- a/libs/storage/Tsavorite/cs/test/StateMachineTests.cs +++ b/libs/storage/Tsavorite/cs/test/StateMachineTests.cs @@ -379,7 +379,6 @@ public void LUCScenario2() [Category("TsavoriteKV"), Category("CheckpointRestore")] public void LUCScenario3() { - CreateSessions(out var f, out var s1, out var ts, out var lts); // System should be in REST, 1 @@ -413,7 +412,7 @@ public void LUCScenario3() luc1.EndLockable(); luc1.EndUnsafe(); - s1.Refresh(); + s1.BasicContext.Refresh(); // System should be in IN_PROGRESS, 1 Assert.IsTrue(SystemState.Equal(SystemState.Make(Phase.IN_PROGRESS, 2), store.SystemState)); @@ -540,11 +539,12 @@ void Prepare(out SimpleFunctions f, NumClicks value; s1 = store.NewSession(f, "foo"); + var bc1 = s1.BasicContext; for (int key = 0; key < numOps; key++) { value.numClicks = key; - s1.Upsert(ref inputArray[key], ref value, Empty.Default); + bc1.Upsert(ref inputArray[key], ref value, Empty.Default); } // Ensure state machine needs no I/O wait during WAIT_FLUSH @@ -576,11 +576,12 @@ void CreateSessions(out SimpleFunctions f, NumClicks value; s1 = store.NewSession(f, "foo"); + var bc1 = s1.BasicContext; for (int key = 0; key < numOps; key++) { value.numClicks = key; - s1.Upsert(ref inputArray[key], ref value, Empty.Default); + bc1.Upsert(ref inputArray[key], ref value, Empty.Default); } // Ensure state machine needs no I/O wait during WAIT_FLUSH @@ -599,7 +600,7 @@ void CreateSessions(out SimpleFunctions f, } } - public class SimpleFunctions : SimpleFunctions + public class SimpleFunctions : SimpleSessionFunctions { public override void ReadCompletionCallback(ref AdId key, ref NumClicks input, ref NumClicks output, Empty ctx, Status status, RecordMetadata recordMetadata) { diff --git a/libs/storage/Tsavorite/cs/test/TestTypes.cs b/libs/storage/Tsavorite/cs/test/TestTypes.cs index 348932a790..0e48a1b724 100644 --- a/libs/storage/Tsavorite/cs/test/TestTypes.cs +++ b/libs/storage/Tsavorite/cs/test/TestTypes.cs @@ -55,7 +55,7 @@ public class Functions : FunctionsWithContext { } - public class FunctionsWithContext : FunctionsBase + public class FunctionsWithContext : SessionFunctionsBase { public override void RMWCompletionCallback(ref KeyStruct key, ref InputStruct input, ref OutputStruct output, TContext ctx, Status status, RecordMetadata recordMetadata) { @@ -122,7 +122,7 @@ public override bool CopyUpdater(ref KeyStruct key, ref InputStruct input, ref V } } - public class FunctionsCompaction : FunctionsBase + public class FunctionsCompaction : SessionFunctionsBase { public override void RMWCompletionCallback(ref KeyStruct key, ref InputStruct input, ref OutputStruct output, int ctx, Status status, RecordMetadata recordMetadata) { @@ -182,7 +182,7 @@ public override bool CopyUpdater(ref KeyStruct key, ref InputStruct input, ref V } } - public class FunctionsCopyOnWrite : FunctionsBase + public class FunctionsCopyOnWrite : SessionFunctionsBase { private int _concurrentWriterCallCount; private int _inPlaceUpdaterCallCount; @@ -264,7 +264,7 @@ public override bool CopyUpdater(ref KeyStruct key, ref InputStruct input, ref V } } - class RMWSimpleFunctions : SimpleFunctions + class RMWSimpleFunctions : SimpleSimpleFunctions { public RMWSimpleFunctions(Func merger) : base(merger) { } diff --git a/libs/storage/Tsavorite/cs/test/ThreadSession.cs b/libs/storage/Tsavorite/cs/test/ThreadSession.cs index b46bff2be9..387de94b03 100644 --- a/libs/storage/Tsavorite/cs/test/ThreadSession.cs +++ b/libs/storage/Tsavorite/cs/test/ThreadSession.cs @@ -10,16 +10,16 @@ namespace Tsavorite.test.statemachine internal static class Extension { public static ThreadSession CreateThreadSession(this TsavoriteKV store, F f) - where K : new() where V : new() where F : IFunctions + where K : new() where V : new() where F : ISessionFunctions => new ThreadSession(store, f); public static LUCThreadSession CreateLUCThreadSession(this TsavoriteKV store, F f) - where K : new() where V : new() where F : IFunctions + where K : new() where V : new() where F : ISessionFunctions => new LUCThreadSession(store, f); } internal class ThreadSession - where K : new() where V : new() where F : IFunctions + where K : new() where V : new() where F : ISessionFunctions { readonly TsavoriteKV store; ClientSession s2; @@ -88,7 +88,7 @@ private void OtherSession(string command, bool waitComplete = true) } internal class LUCThreadSession - where K : new() where V : new() where F : IFunctions + where K : new() where V : new() where F : ISessionFunctions { readonly TsavoriteKV store; ClientSession session; @@ -139,7 +139,7 @@ private void LUCThread() if (isProtected) luc.Refresh(); else - session.Refresh(); + session.BasicContext.Refresh(); ev.Set(); break; case "dispose": diff --git a/test/Garnet.test/GarnetObjectTests.cs b/test/Garnet.test/GarnetObjectTests.cs index 316d1da508..663f0ec249 100644 --- a/test/Garnet.test/GarnetObjectTests.cs +++ b/test/Garnet.test/GarnetObjectTests.cs @@ -34,15 +34,16 @@ public void TearDown() [Test] public void WriteRead() { - using var session = store.NewSession>(new SimpleFunctions()); + using var session = store.NewSession>(new SimpleSessionFunctions()); + var bContext = session.BasicContext; var key = new byte[] { 0 }; var obj = new SortedSetObject(); - session.Upsert(key, obj); + bContext.Upsert(key, obj); IGarnetObject output = null; - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); Assert.IsTrue(status.Found); Assert.AreEqual(obj, output); @@ -52,12 +53,13 @@ public void WriteRead() public async Task WriteCheckpointRead() { var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; var key = new byte[] { 0 }; var obj = new SortedSetObject(); obj.Add([15], 10); - session.Upsert(key, obj); + bContext.Upsert(key, obj); session.Dispose(); @@ -69,9 +71,10 @@ public async Task WriteCheckpointRead() store.Recover(); session = store.NewSession(new MyFunctions()); + bContext = session.BasicContext; IGarnetObject output = null; - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); session.Dispose(); @@ -83,16 +86,17 @@ public async Task WriteCheckpointRead() public async Task CopyUpdate() { var session = store.NewSession(new MyFunctions()); + var bContext = session.BasicContext; var key = new byte[] { 0 }; IGarnetObject obj = new SortedSetObject(); ((SortedSetObject)obj).Add([15], 10); - session.Upsert(key, obj); + bContext.Upsert(key, obj); store.Log.Flush(true); - session.RMW(ref key, ref obj); + bContext.RMW(ref key, ref obj); session.Dispose(); @@ -104,9 +108,10 @@ public async Task CopyUpdate() store.Recover(); session = store.NewSession(new MyFunctions()); + bContext = session.BasicContext; IGarnetObject output = null; - var status = session.Read(ref key, ref output); + var status = bContext.Read(ref key, ref output); session.Dispose(); @@ -114,7 +119,7 @@ public async Task CopyUpdate() Assert.IsTrue(((SortedSetObject)obj).Equals((SortedSetObject)output)); } - private class MyFunctions : FunctionsBase + private class MyFunctions : SessionFunctionsBase { public MyFunctions() { }