Skip to content

Commit

Permalink
support PX in SET command (#93)
Browse files Browse the repository at this point in the history
* support PX in SET command

* Delete unneeded log

* Fix formatting

* Fix formatting

* Refactor NetworkSET_EX and NetworkSET_Conditional methods signature

* Fixing UnitTest to Avoid Possible Failures

---------
  • Loading branch information
argsno authored Mar 23, 2024
1 parent 4a2b369 commit 910f5f3
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 12 deletions.
58 changes: 47 additions & 11 deletions libs/server/Resp/BasicCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,29 @@ private bool NetworkSETEXNX<TGarnetApi>(byte* ptr, ref TGarnetApi storageApi)
continue;
}
}
else if (*(long*)ptr == 724332215865782820) // [PX]
{
ptr += 8;
cmdcount--;

if (!RespReadUtils.ReadIntWithLengthHeader(out expiry, ref ptr, recvBufferPtr + bytesRead))
return false;
cmdcount--;

if (expOption != ExpirationOption.None)
{
errorMessage = CmdStrings.RESP_SYNTAX_ERROR;
error = true;
continue;
}
expOption = ExpirationOption.PX;
if (expiry <= 0)
{
errorMessage = CmdStrings.RESP_ERRINVALIDEXP_IN_SET;
error = true;
continue;
}
}
else if (*(long*)ptr == 5784105485020772132 && *(int*)(ptr + 8) == 223106132 && *(ptr + 12) == 10) // [KEEPTTL]
{
ptr += 13;
Expand Down Expand Up @@ -566,12 +589,25 @@ private bool NetworkSETEXNX<TGarnetApi>(byte* ptr, ref TGarnetApi storageApi)
{
case ExistOptions.None:
return getValue ?
NetworkSET_Conditional(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi) :
NetworkSET_EX(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, ref storageApi); // Can perform a blind update
NetworkSET_Conditional(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi) :
NetworkSET_EX(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, false, ref storageApi); // Can perform a blind update
case ExistOptions.XX:
return NetworkSET_Conditional(RespCommand.SETEXXX, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi);
case ExistOptions.NX:
return NetworkSET_Conditional(RespCommand.SETEXNX, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi);
}
break;
case ExpirationOption.PX:
switch (existOptions)
{
case ExistOptions.None:
return getValue ?
NetworkSET_Conditional(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, getValue, true, ref storageApi) :
NetworkSET_EX(RespCommand.SET, ptr, expiry, keyPtr, valPtr, vsize, true, ref storageApi); // Can perform a blind update
case ExistOptions.XX:
return NetworkSET_Conditional(RespCommand.SETEXXX, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi);
return NetworkSET_Conditional(RespCommand.SETEXXX, ptr, expiry, keyPtr, valPtr, vsize, getValue, true, ref storageApi);
case ExistOptions.NX:
return NetworkSET_Conditional(RespCommand.SETEXNX, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi);
return NetworkSET_Conditional(RespCommand.SETEXNX, ptr, expiry, keyPtr, valPtr, vsize, getValue, true, ref storageApi);
}
break;

Expand All @@ -581,11 +617,11 @@ private bool NetworkSETEXNX<TGarnetApi>(byte* ptr, ref TGarnetApi storageApi)
{
case ExistOptions.None:
// We can never perform a blind update due to KEEPTTL
return NetworkSET_Conditional(RespCommand.SETKEEPTTL, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi);
return NetworkSET_Conditional(RespCommand.SETKEEPTTL, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi);
case ExistOptions.XX:
return NetworkSET_Conditional(RespCommand.SETKEEPTTLXX, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi);
return NetworkSET_Conditional(RespCommand.SETKEEPTTLXX, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi);
case ExistOptions.NX:
return NetworkSET_Conditional(RespCommand.SETEXNX, ptr, expiry, keyPtr, valPtr, vsize, getValue, ref storageApi);
return NetworkSET_Conditional(RespCommand.SETEXNX, ptr, expiry, keyPtr, valPtr, vsize, getValue, false, ref storageApi);
}
break;
}
Expand All @@ -595,7 +631,7 @@ private bool NetworkSETEXNX<TGarnetApi>(byte* ptr, ref TGarnetApi storageApi)
return true;
}

private bool NetworkSET_EX<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, byte* keyPtr, byte* valPtr, int vsize, ref TGarnetApi storageApi)
private bool NetworkSET_EX<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, byte* keyPtr, byte* valPtr, int vsize, bool highPrecision, ref TGarnetApi storageApi)
where TGarnetApi : IGarnetApi
{
Debug.Assert(cmd == RespCommand.SET);
Expand All @@ -609,7 +645,7 @@ private bool NetworkSET_EX<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, b
// Move payload forward to make space for metadata
Buffer.MemoryCopy(valPtr + sizeof(int), valPtr + sizeof(int) + sizeof(long), vsize, vsize);
*(int*)valPtr = vsize + sizeof(long);
SpanByte.Reinterpret(valPtr).ExtraMetadata = DateTimeOffset.UtcNow.Ticks + TimeSpan.FromSeconds(expiry).Ticks;
SpanByte.Reinterpret(valPtr).ExtraMetadata = DateTimeOffset.UtcNow.Ticks + (highPrecision ? TimeSpan.FromMilliseconds(expiry).Ticks : TimeSpan.FromSeconds(expiry).Ticks);
}

storageApi.SET(ref Unsafe.AsRef<SpanByte>(keyPtr), ref Unsafe.AsRef<SpanByte>(valPtr));
Expand All @@ -620,7 +656,7 @@ private bool NetworkSET_EX<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, b
return true;
}

private bool NetworkSET_Conditional<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, byte* keyPtr, byte* inputPtr, int isize, bool getValue, ref TGarnetApi storageApi)
private bool NetworkSET_Conditional<TGarnetApi>(RespCommand cmd, byte* ptr, int expiry, byte* keyPtr, byte* inputPtr, int isize, bool getValue, bool highPrecision, ref TGarnetApi storageApi)
where TGarnetApi : IGarnetApi
{
// Make space for RespCommand in input
Expand All @@ -643,7 +679,7 @@ private bool NetworkSET_Conditional<TGarnetApi>(RespCommand cmd, byte* ptr, int
((RespInputHeader*)(inputPtr + sizeof(int) + sizeof(long)))->flags = 0;
if (getValue)
((RespInputHeader*)(inputPtr + sizeof(int) + sizeof(long)))->SetSetGetFlag();
SpanByte.Reinterpret(inputPtr).ExtraMetadata = DateTimeOffset.UtcNow.Ticks + TimeSpan.FromSeconds(expiry).Ticks;
SpanByte.Reinterpret(inputPtr).ExtraMetadata = DateTimeOffset.UtcNow.Ticks + (highPrecision ? TimeSpan.FromMilliseconds(expiry).Ticks : TimeSpan.FromSeconds(expiry).Ticks);
}

if (getValue)
Expand Down
39 changes: 38 additions & 1 deletion test/Garnet.test/RespTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -182,11 +182,15 @@ public void SetExpiryHighPrecision()
var db = redis.GetDatabase(0);

string origValue = "abcdefghij";
db.StringSet("mykey", origValue, TimeSpan.FromSeconds(1.1));
db.StringSet("mykey", origValue, TimeSpan.FromSeconds(1.9));

string retValue = db.StringGet("mykey");
Assert.AreEqual(origValue, retValue);

Thread.Sleep(1000);
retValue = db.StringGet("mykey");
Assert.AreEqual(origValue, retValue);

Thread.Sleep(2000);
retValue = db.StringGet("mykey");
Assert.AreEqual(null, retValue);
Expand Down Expand Up @@ -367,6 +371,13 @@ public void SetOptionsCaseSensitivityTest()
resp = (string)db.Execute($"{ttlCommand}", key);
Assert.IsTrue(int.TryParse(resp, out var ttl) && ttl == -1);

// px
resp = (string)db.Execute($"{setCommand}", key, value, "px", "1000");
Assert.AreEqual(okResponse, resp);
Thread.Sleep(TimeSpan.FromSeconds(1.1));
resp = (string)db.Execute($"{ttlCommand}", key);
Assert.IsTrue(int.TryParse(resp, out ttl) && ttl == -1);

// keepttl
Assert.IsTrue(db.StringSet(key, 1, TimeSpan.FromMinutes(1)));
resp = (string)db.Execute($"{setCommand}", key, value, "keepttl");
Expand Down Expand Up @@ -399,6 +410,32 @@ public void SetOptionsCaseSensitivityTest()
Thread.Sleep(TimeSpan.FromSeconds(1.1));
resp = (string)db.Execute($"{ttlCommand}", key);
Assert.IsTrue(int.TryParse(resp, out ttl) && ttl == -1);

// px .. nx, non-existing key
Assert.IsTrue(db.KeyDelete(key));
resp = (string)db.Execute($"{setCommand}", key, value, "px", "1000", "nx");
Assert.AreEqual(okResponse, resp);
Thread.Sleep(TimeSpan.FromSeconds(1.1));
resp = (string)db.Execute($"{ttlCommand}", key);
Assert.IsTrue(int.TryParse(resp, out ttl) && ttl == -1);

// px .. nx, existing key
Assert.IsTrue(db.StringSet(key, value));
resp = (string)db.Execute($"{setCommand}", key, value, "px", "1000", "nx");
Assert.IsNull(resp);

// px .. xx, non-existing key
Assert.IsTrue(db.KeyDelete(key));
resp = (string)db.Execute($"{setCommand}", key, value, "px", "1000", "xx");
Assert.IsNull(resp);

// px .. xx, existing key
Assert.IsTrue(db.StringSet(key, value));
resp = (string)db.Execute($"{setCommand}", key, value, "px", "1000", "xx");
Assert.AreEqual(okResponse, resp);
Thread.Sleep(TimeSpan.FromSeconds(1.1));
resp = (string)db.Execute($"{ttlCommand}", key);
Assert.IsTrue(int.TryParse(resp, out ttl) && ttl == -1);
}

[Test]
Expand Down

0 comments on commit 910f5f3

Please sign in to comment.