Skip to content

Commit

Permalink
Add more HTTL/HPTTL tests and fix issues found.
Browse files Browse the repository at this point in the history
  • Loading branch information
mmclure-msft committed Sep 19, 2024
1 parent 90ea729 commit ec98c82
Show file tree
Hide file tree
Showing 3 changed files with 70 additions and 2 deletions.
8 changes: 6 additions & 2 deletions libs/server/Objects/Hash/HashObjectImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -526,7 +526,7 @@ private void HashExpire(ref ObjectInput input, ref SpanByteAndMemory output)
var key = parseState.GetArgSliceByRef(currIdx).SpanByte.ToByteArray();

var result = 1; // Assume success
if (!hash.TryGetValue(key, out var hashValue) /* || hashValue.IsExpired() */)
if (!hash.TryGetValue(key, out var hashValue) || hashValue.IsExpired())
{
result = -2;
}
Expand Down Expand Up @@ -637,11 +637,15 @@ private void HashTtl(ref ObjectInput input, ref SpanByteAndMemory output)
var key = parseState.GetArgSliceByRef(currIdx).SpanByte.ToByteArray();

var result = 1L; // Assume success
if (!hash.TryGetValue(key, out var hashValue) /* || hashValue.IsExpired() */)
if (!hash.TryGetValue(key, out var hashValue) || hashValue.IsExpired())
{
result = -2L;
}
else if (hashValue.Expiration <= 0)
{
result = -1L;
}
else
{
result = (hop == HashOperation.HTTL) ?
ConvertUtils.SecondsFromDiffUtcNowTicks(hashValue.Expiration > 0 ? hashValue.Expiration : -1) :
Expand Down
22 changes: 22 additions & 0 deletions libs/server/Resp/Objects/HashCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,28 @@ private unsafe bool HashTtl<TGarnetApi>(RespCommand command, ref TGarnetApi stor
return AbortWithWrongNumberOfArguments(command.ToString());
}

// Redis parses syntax before seeing if hash exists, so we need to check for some
// bad syntax here to return the right errors if the hash does not exist - otherwise
// we'll get back an empty array instead of the correct error message.
if (parseState.GetString(1) != "FIELDS")
{
while (!RespWriteUtils.WriteError(CmdStrings.RESP_ERR_MISSING_ARGUMENT_FIELDS, ref dcurr, dend))
SendAndReset();
return true;
}
if (!parseState.TryGetInt(2, out var fieldCount))
{
while (!RespWriteUtils.WriteError(CmdStrings.RESP_ERR_GENERIC_VALUE_IS_NOT_INTEGER, ref dcurr, dend))
SendAndReset();
return true;
}
if (fieldCount != parseState.Count - 3)
{
while (!RespWriteUtils.WriteError(CmdStrings.RESP_ERR_MISMATCH_NUMFIELDS, ref dcurr, dend))
SendAndReset();
return true;
}

var sbKey = parseState.GetArgSliceByRef(0).SpanByte;
var keyBytes = sbKey.ToByteArray();

Expand Down
42 changes: 42 additions & 0 deletions test/Garnet.test/RespHashTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1225,6 +1225,12 @@ public void HExpireCommandParameters(string command)
var actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Non-numeric value for field count
res = lightClientRequest.SendCommand($"{command} {key} 10 FIELDS hello field1");
expectedResponse = "-ERR value is not an integer or out of range.\r\n";
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Invalid option
res = lightClientRequest.SendCommand($"{command} {key} 10 BB FIELDS 1 field1");
expectedResponse = "-ERR Mandatory argument FIELDS is missing or not at the right position\r\n";
Expand Down Expand Up @@ -1373,6 +1379,12 @@ public void HTtlCommandParameters(string command)
var actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Non-numeric value for field count
res = lightClientRequest.SendCommand($"{command} {key} FIELDS hello field1");
expectedResponse = "-ERR value is not an integer or out of range.\r\n";
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Too many fields listed
res = lightClientRequest.SendCommand($"{command} {key} FIELDS 1 field1 field2");
expectedResponse = "-ERR The `numfields` parameter must match the number of arguments\r\n";
Expand All @@ -1384,6 +1396,36 @@ public void HTtlCommandParameters(string command)
expectedResponse = $"-ERR wrong number of arguments for '{command}' command\r\n";
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Set up a field
res = lightClientRequest.SendCommand($"HSET {key} field1 1");
expectedResponse = ":1\r\n";
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Set a TTL
res = lightClientRequest.SendCommand($"HEXPIRE {key} 300 FIELDS 1 field1");
expectedResponse = "*1\r\n:1\r\n";
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);

// Get the TTL in seconds
res = lightClientRequest.SendCommand($"{command} {key} FIELDS 1 field1", 2);
expectedResponse = "*1\r\n"; // Should return a one element array
actualResponse = Encoding.ASCII.GetString(res).Substring(0, expectedResponse.Length);
ClassicAssert.AreEqual(expectedResponse, actualResponse);
if (command == "HTTL")
{
// Should be less or equal to 300 and within 5 seconds of it
var ttlValue = long.Parse(Encoding.ASCII.GetString(res).Substring(5, 3));
ClassicAssert.IsTrue(ttlValue <= 300 && ttlValue >= 295);
}
else
{
// Should be less or equal to 300000 and within 5 seconds of it
var ttlValue = long.Parse(Encoding.ASCII.GetString(res).Substring(5, 6));
ClassicAssert.IsTrue(ttlValue <= 300000 && ttlValue >= 295000);
}
}
#endregion

Expand Down

0 comments on commit ec98c82

Please sign in to comment.