Skip to content

Commit 8153dcd

Browse files
Fix RCU Bug in metadata and value overlap of SETIFGREATER and SETIFMATCH (#1185)
* Fix Bug in SETIFGREATER and SETIFMATCH for no-exp to exp case * retrigger tests * fmt --------- Co-authored-by: Hamdaan Khalid <[email protected]>
1 parent d0f4e4b commit 8153dcd

File tree

2 files changed

+58
-3
lines changed

2 files changed

+58
-3
lines changed

libs/server/Storage/Functions/MainStore/RMWMethods.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,22 +1058,22 @@ public bool CopyUpdater(ref SpanByte key, ref RawStringInput input, ref SpanByte
10581058
// By now the comparison for etag against existing etag has already been done in NeedCopyUpdate
10591059
shouldUpdateEtag = true;
10601060
// Copy input to value
1061-
Span<byte> dest = newValue.AsSpan(EtagConstants.EtagSize);
10621061
ReadOnlySpan<byte> src = input.parseState.GetArgSliceByRef(0).ReadOnlySpan;
10631062

10641063
// retain metadata unless metadata sent
10651064
int metadataSize = input.arg1 != 0 ? sizeof(long) : oldValue.MetadataSize;
10661065

10671066
Debug.Assert(src.Length + EtagConstants.EtagSize + metadataSize == newValue.Length);
10681067

1069-
src.CopyTo(dest);
1070-
10711068
newValue.ExtraMetadata = oldValue.ExtraMetadata;
10721069
if (input.arg1 != 0)
10731070
{
10741071
newValue.ExtraMetadata = input.arg1;
10751072
}
10761073

1074+
Span<byte> dest = newValue.AsSpan(EtagConstants.EtagSize);
1075+
src.CopyTo(dest);
1076+
10771077
etagFromClient = input.parseState.GetLong(1);
10781078

10791079
functionsState.etagState.etag = etagFromClient;

test/Garnet.test/RespEtagTests.cs

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,7 @@ public void SetIfGreaterWorksWithoutInitialETag()
396396
#endregion
397397

398398
#region ETAG DEL Happy Paths
399+
399400
[Test]
400401
public void DelIfGreaterOnAnAlreadyExistingKeyWithEtagWorks()
401402
{
@@ -590,6 +591,60 @@ private void MakeReadOnly(long untilAddress, IServer server, IDatabase db)
590591
#endregion
591592

592593
#region Edgecases
594+
595+
[Test]
596+
[TestCase("m", "mo", null)] // RCU with no existing exp on noetag key
597+
[TestCase("mexicanmochawithdoubleespresso", "c", null)] // IPU with no existing exp on noetag key
598+
[TestCase("m", "mo", 30)] // RCU with existing exp on noetag key
599+
[TestCase("mexicanmochawithdoubleespresso", "c", 30)] // IPU with existing exp on noetag key
600+
public void SetIfGreaterWhenExpIsSentForExistingNonEtagKey(string initialValue, string newValue, double? exp)
601+
{
602+
using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig());
603+
IDatabase db = redis.GetDatabase(0);
604+
var key = "meow-key";
605+
606+
if (exp != null)
607+
db.StringSet(key, initialValue, TimeSpan.FromSeconds(exp.Value));
608+
else
609+
db.StringSet(key, initialValue);
610+
611+
RedisResult[] arrRes = (RedisResult[])db.Execute("SETIFGREATER", key, newValue, 5, "EX", 90);
612+
613+
ClassicAssert.AreEqual(5, (long)arrRes[0]);
614+
ClassicAssert.IsTrue(arrRes[1].IsNull);
615+
616+
var res = db.StringGetWithExpiry(key);
617+
ClassicAssert.AreEqual(newValue, res.Value.ToString());
618+
ClassicAssert.IsTrue(res.Expiry.HasValue);
619+
}
620+
621+
[Test]
622+
[TestCase("m", "mo", null)] // RCU with no existing exp on noetag key
623+
[TestCase("mexicanmochawithdoubleespresso", "c", null)] // IPU with no existing exp on noetag key
624+
[TestCase("m", "mo", 30)] // RCU with existing exp on noetag key
625+
[TestCase("mexicanmochawithdoubleespresso", "c", 30)] // IPU with existing exp on noetag key
626+
public void SetIfMatchWhenExpIsSentForExistingNonEtagKey(string initialValue, string newValue, int? exp)
627+
{
628+
using var redis = ConnectionMultiplexer.Connect(TestUtils.GetConfig());
629+
IDatabase db = redis.GetDatabase(0);
630+
var key = "meow-key";
631+
632+
if (exp != null)
633+
db.StringSet(key, initialValue, TimeSpan.FromSeconds(exp.Value));
634+
else
635+
db.StringSet(key, initialValue);
636+
637+
RedisResult[] arrRes = (RedisResult[])db.Execute("SETIFMATCH", key, newValue, 0, "EX", 90);
638+
639+
ClassicAssert.AreEqual(1, (long)arrRes[0]);
640+
ClassicAssert.IsTrue(arrRes[1].IsNull);
641+
642+
var res = db.StringGetWithExpiry(key);
643+
ClassicAssert.AreEqual(newValue, res.Value.ToString());
644+
ClassicAssert.IsTrue(res.Expiry.HasValue);
645+
}
646+
647+
593648
[Test]
594649
public void SetIfMatchSetsKeyValueOnNonExistingKey()
595650
{

0 commit comments

Comments
 (0)