Skip to content

Commit

Permalink
add txn, when existing same key overwritten of SDIFFSTORE
Browse files Browse the repository at this point in the history
  • Loading branch information
tisilent committed Mar 31, 2024
1 parent 99071fd commit 90dadb9
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 32 deletions.
4 changes: 2 additions & 2 deletions libs/server/API/GarnetApiObjectCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -298,10 +298,10 @@ public GarnetStatus SetScan(ArgSlice key, long cursor, string match, int count,

/// <inheritdoc />
public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet<byte[]> 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
Expand Down
2 changes: 1 addition & 1 deletion libs/server/Objects/Set/SetObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
85 changes: 57 additions & 28 deletions libs/server/Storage/Session/ObjectStore/SetOps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using Garnet.common;
Expand Down Expand Up @@ -435,35 +436,36 @@ public GarnetStatus SetPop<TObjectContext>(byte[] key, ArgSlice input, ref Garne
/// <summary>
/// Returns the members of the set resulting from the difference between the first set at key and all the successive sets at keys.
/// </summary>
/// <typeparam name="TObjectContext"></typeparam>
/// <param name="keys"></param>
/// <param name="members"></param>
/// <param name="objectContext"></param>
/// <returns></returns>
public GarnetStatus SetDiff<TObjectContext>(ArgSlice[] keys, out HashSet<byte[]> members, ref TObjectContext objectContext)
where TObjectContext : ITsavoriteContext<byte[], IGarnetObject, SpanByte, GarnetObjectStoreOutput, long>
public GarnetStatus SetDiff(ArgSlice[] keys, out HashSet<byte[]> 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;
Expand All @@ -473,32 +475,59 @@ public GarnetStatus SetDiff<TObjectContext>(ArgSlice[] keys, out HashSet<byte[]>
/// Diff result store.
/// Returns the number of result set.
/// </summary>
/// <typeparam name="TObjectContext"></typeparam>
/// <param name="key">destination</param>
/// <param name="keys"></param>
/// <param name="count"></param>
/// <param name="objectContext"></param>
/// <returns></returns>
public GarnetStatus SetDiffStore<TObjectContext>(byte[] key, ArgSlice[] keys,out int count, ref TObjectContext objectContext)
where TObjectContext : ITsavoriteContext<byte[], IGarnetObject, SpanByte, GarnetObjectStoreOutput, long>
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<byte[]> _setDiff<TObjectContext>(ArgSlice[] keys, ref TObjectContext objectContext)
Expand Down
32 changes: 31 additions & 1 deletion test/Garnet.test/RespSetTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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


Expand Down

0 comments on commit 90dadb9

Please sign in to comment.