From 90dadb94e8a12b03ca36815f83e7f848f6a6b70c Mon Sep 17 00:00:00 2001 From: tisilent Date: Mon, 1 Apr 2024 00:29:58 +0800 Subject: [PATCH] add txn, when existing same key overwritten of SDIFFSTORE --- libs/server/API/GarnetApiObjectCommands.cs | 4 +- libs/server/Objects/Set/SetObject.cs | 2 +- .../Storage/Session/ObjectStore/SetOps.cs | 85 +++++++++++++------ test/Garnet.test/RespSetTest.cs | 32 ++++++- 4 files changed, 91 insertions(+), 32 deletions(-) diff --git a/libs/server/API/GarnetApiObjectCommands.cs b/libs/server/API/GarnetApiObjectCommands.cs index cb73fe002a..9aa5ea35a3 100644 --- a/libs/server/API/GarnetApiObjectCommands.cs +++ b/libs/server/API/GarnetApiObjectCommands.cs @@ -298,10 +298,10 @@ public GarnetStatus SetScan(ArgSlice key, long cursor, string match, int count, /// public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet members) - => storageSession.SetDiff(keys, out members, ref objectContext); + => storageSession.SetDiff(keys, out members); public GarnetStatus SetDiffStore(byte[] key, ArgSlice[] keys, out int count) - => storageSession.SetDiffStore(key, keys, out count, ref objectContext); + => storageSession.SetDiffStore(key, keys, out count); #endregion #region Hash Methods diff --git a/libs/server/Objects/Set/SetObject.cs b/libs/server/Objects/Set/SetObject.cs index 9e4d71df76..39ec1a53e8 100644 --- a/libs/server/Objects/Set/SetObject.cs +++ b/libs/server/Objects/Set/SetObject.cs @@ -137,7 +137,7 @@ public override unsafe bool Operate(ref SpanByte input, ref SpanByteAndMemory ou return true; } - private void UpdateSize(byte[] item, bool add = true) + internal void UpdateSize(byte[] item, bool add = true) { var size = Utility.RoundUp(item.Length, IntPtr.Size) + MemoryUtils.ByteArrayOverhead + MemoryUtils.HashSetEntryOverhead; this.Size += add ? size : -size; diff --git a/libs/server/Storage/Session/ObjectStore/SetOps.cs b/libs/server/Storage/Session/ObjectStore/SetOps.cs index 167da0d802..af3a4eba36 100644 --- a/libs/server/Storage/Session/ObjectStore/SetOps.cs +++ b/libs/server/Storage/Session/ObjectStore/SetOps.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using Garnet.common; @@ -435,35 +436,36 @@ public GarnetStatus SetPop(byte[] key, ArgSlice input, ref Garne /// /// Returns the members of the set resulting from the difference between the first set at key and all the successive sets at keys. /// - /// /// /// - /// /// - public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet members, ref TObjectContext objectContext) - where TObjectContext : ITsavoriteContext + public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet members) { members = default; if (keys.Length == 0) return GarnetStatus.OK; - members = []; + var createTransaction = false; - var status = GET(keys[0].Bytes, out var first, ref objectContext); - if (status == GarnetStatus.OK) + if (txnManager.state != TxnState.Running) { - members.UnionWith(((SetObject)first.garnetObject).Set); + Debug.Assert(txnManager.state == TxnState.None); + createTransaction = true; + foreach (var item in keys) + txnManager.SaveKeyEntryToLock(item, true, LockType.Shared); + _ = txnManager.Run(true); } - for (var i = 1; i < keys.Length; i++) + var objectStoreLockableContext = txnManager.ObjectStoreLockableContext; + + try { - status = GET(keys[i].Bytes, out var next, ref objectContext); - if (status == GarnetStatus.OK) - { - var nextSet = ((SetObject)next.garnetObject).Set; - var interItems = members.Intersect(nextSet, nextSet.Comparer); - members.ExceptWith(interItems); - } + members = _setDiff(keys, ref objectStoreLockableContext); + } + finally + { + if (createTransaction) + txnManager.Commit(true); } return GarnetStatus.OK; @@ -473,32 +475,59 @@ public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet /// Diff result store. /// Returns the number of result set. /// - /// /// destination /// /// - /// /// - public GarnetStatus SetDiffStore(byte[] key, ArgSlice[] keys,out int count, ref TObjectContext objectContext) - where TObjectContext : ITsavoriteContext + public GarnetStatus SetDiffStore(byte[] key, ArgSlice[] keys,out int count) { count = default; if (key.Length == 0 || keys.Length == 0) return GarnetStatus.OK; - var diffSet = _setDiff(keys, ref objectContext); - var asKey = scratchBufferManager.CreateArgSlice(key); - var asMembers = new ArgSlice[diffSet.Count]; - for (var i = 0; i < diffSet.Count; i++) + + var createTransaction = false; + + if (txnManager.state != TxnState.Running) { - asMembers[i] = scratchBufferManager.CreateArgSlice(diffSet.ElementAt(i)); + Debug.Assert(txnManager.state == TxnState.None); + createTransaction = true; + txnManager.SaveKeyEntryToLock(asKey, true, LockType.Exclusive); + foreach (var item in keys) + txnManager.SaveKeyEntryToLock(item, true, LockType.Shared); + _ = txnManager.Run(true); } - var status = SetAdd(asKey, [.. asMembers], out var saddCount, ref objectContext); - count = saddCount; - return status; + var objectStoreLockableContext = txnManager.ObjectStoreLockableContext; + + try + { + var diffSet = _setDiff(keys, ref objectStoreLockableContext); + + var asMembers = new ArgSlice[diffSet.Count]; + for (var i = 0; i < diffSet.Count; i++) + { + asMembers[i] = scratchBufferManager.CreateArgSlice(diffSet.ElementAt(i)); + } + + var newSetObject = new SetObject(); + foreach (var item in diffSet) + { + newSetObject.Set.Add(item); + newSetObject.UpdateSize(item); + } + _ = SET(key, newSetObject, ref objectStoreLockableContext); + count = diffSet.Count; + } + finally + { + if (createTransaction) + txnManager.Commit(true); + } + + return GarnetStatus.OK; } private HashSet _setDiff(ArgSlice[] keys, ref TObjectContext objectContext) diff --git a/test/Garnet.test/RespSetTest.cs b/test/Garnet.test/RespSetTest.cs index 461654f837..b9ca1f7452 100644 --- a/test/Garnet.test/RespSetTest.cs +++ b/test/Garnet.test/RespSetTest.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using System.Text; using NUnit.Framework; using StackExchange.Redis; @@ -478,6 +477,37 @@ public void CanDoSdiffStoreLC() expectedResponse = "*2\r\n$1\r\nb\r\n$1\r\nd\r\n"; Assert.AreEqual(expectedResponse, membersResponse.AsSpan().Slice(0, expectedResponse.Length).ToArray()); } + + [Test] + public void CanDoSdiffStoreOverwrittenLC() + { + var lightClientRequest = TestUtils.CreateRequest(); + + // diffstore set example 1 + lightClientRequest.SendCommand("SADD key1 a b c d"); + lightClientRequest.SendCommand("SADD key2 c"); + var response = lightClientRequest.SendCommand("SDIFFSTORE key key1 key2"); // key = a b d + var expectedResponse = ":3\r\n"; + Assert.AreEqual(expectedResponse, response.AsSpan().Slice(0, expectedResponse.Length).ToArray()); + + var membersResponse = lightClientRequest.SendCommand("SMEMBERS key"); + expectedResponse = "*3\r\n$1\r\na\r\n$1\r\nb\r\n$1\r\nd\r\n"; + Assert.AreEqual(expectedResponse, membersResponse.AsSpan().Slice(0, expectedResponse.Length).ToArray()); + + // diffstore set example 2 + //lightClientRequest.SendCommand("SADD key3 a c b"); + //lightClientRequest.SendCommand("SADD key4 a b"); + //response = lightClientRequest.SendCommand("SDIFFSTORE key key3 key4"); // key overwritten = c + + // TODO why response + + //expectedResponse = ":1\r\n"; + //// Assert.AreEqual(expectedResponse, response.AsSpan().Slice(0, expectedResponse.Length).ToArray()); + + //membersResponse = lightClientRequest.SendCommand("SMEMBERS key"); + //expectedResponse = "*1\r\n$1\r\nc\r\n"; + //Assert.AreEqual(expectedResponse, membersResponse.AsSpan().Slice(0, expectedResponse.Length).ToArray()); + } #endregion