Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ZMSCORE command support #118

Merged
merged 8 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ ClientBin/
*.publishsettings
node_modules/
**/log-commits
.mono

# RIA/Silverlight projects
Generated_Code/
Expand Down Expand Up @@ -214,4 +215,4 @@ test/tmp/
.idea/

# BenchmarkDotNet Results
BenchmarkDotNet.Artifacts/
BenchmarkDotNet.Artifacts/
4 changes: 4 additions & 0 deletions libs/server/API/GarnetApiObjectCommands.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ public GarnetStatus SortedSetRange(byte[] key, ArgSlice input, ref GarnetObjectS
public GarnetStatus SortedSetScore(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter)
=> storageSession.SortedSetScore(key, input, ref outputFooter, ref objectContext);

/// <inheritdoc />
public GarnetStatus SortedSetScores(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter)
=> storageSession.SortedSetScores(key, input, ref outputFooter, ref objectContext);

/// <inheritdoc />
public GarnetStatus SortedSetPop(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter)
=> storageSession.SortedSetPop(key, input, ref outputFooter, ref objectContext);
Expand Down
7 changes: 7 additions & 0 deletions libs/server/API/GarnetWatchApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ public GarnetStatus SortedSetScore(byte[] key, ArgSlice input, ref GarnetObjectS
return garnetApi.SortedSetScore(key, input, ref outputFooter);
}

/// <inheritdoc />
public GarnetStatus SortedSetScores(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter)
{
garnetApi.WATCH(key, StoreType.Object);
return garnetApi.SortedSetScores(key, input, ref outputFooter);
}

/// <inheritdoc />
public GarnetStatus SortedSetRank(byte[] key, ArgSlice input, out ObjectOutputHeader output)
{
Expand Down
42 changes: 26 additions & 16 deletions libs/server/API/IGarnetApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
/// </summary>
/// <param name="key">Name of the key or object to get the memory usage</param>
/// <param name="memoryUsage">The value in bytes the key or object is using</param>
/// <param name="samples">Number of sampled nested values</param>
/// <param name="samples">Number of sampled nested values</param>
/// <returns>GarnetStatus</returns>
GarnetStatus MemoryUsageForKey(ArgSlice key, out long memoryUsage, int samples = 0);

Expand Down Expand Up @@ -335,7 +335,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
GarnetStatus SortedSetRemoveRangeByLex(byte[] key, ArgSlice input, out ObjectOutputHeader output);

/// <summary>
/// Removes and returns the first element from the sorted set stored at key,
/// Removes and returns the first element from the sorted set stored at key,
/// with the scores ordered from low to high (min) or high to low (max).
/// </summary>
/// <param name="key"></param>
Expand All @@ -355,7 +355,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
GarnetStatus SortedSetPop(ArgSlice key, out (ArgSlice score, ArgSlice member)[] pairs, int count = 1, bool lowScoresFirst = true);

/// <summary>
/// Increments the score of member in the sorted set stored at key by increment.
/// Increments the score of member in the sorted set stored at key by increment.
/// If member does not exist in the sorted set, it is added with increment as its score (as if its previous score was 0.0).
/// </summary>
/// <param name="key"></param>
Expand Down Expand Up @@ -442,7 +442,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi

/// <summary>
/// Adds the specified members to the set at key.
/// Specified members that are already a member of this set are ignored.
/// Specified members that are already a member of this set are ignored.
/// If key does not exist, a new set is created.
/// </summary>
/// <param name="key"></param>
Expand All @@ -453,7 +453,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi

/// <summary>
/// Adds the specified members to the set at key.
/// Specified members that are already a member of this set are ignored.
/// Specified members that are already a member of this set are ignored.
/// If key does not exist, a new set is created.
/// </summary>
/// <param name="key"></param>
Expand All @@ -464,7 +464,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi

/// <summary>
/// Removes the specified member from the set.
/// Specified members that are not a member of this set are ignored.
/// Specified members that are not a member of this set are ignored.
/// If key does not exist, this command returns 0.
/// </summary>
/// <param name="key"></param>
Expand All @@ -475,7 +475,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi

/// <summary>
/// Removes the specified members from the set.
/// Specified members that are not a member of this set are ignored.
/// Specified members that are not a member of this set are ignored.
/// If key does not exist, this command returns 0.
/// </summary>
/// <param name="key"></param>
Expand All @@ -486,7 +486,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi

/// <summary>
/// Removes the specified members from the set.
/// Specified members that are not a member of this set are ignored.
/// Specified members that are not a member of this set are ignored.
/// If key does not exist, this command returns 0.
/// </summary>
/// <param name="key"></param>
Expand Down Expand Up @@ -726,7 +726,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
GarnetStatus HashSet(byte[] key, ArgSlice input, out ObjectOutputHeader output);

/// <summary>
/// Set only if field does not yet exist. If key does not exist, a new key holding a hash is created.
/// Set only if field does not yet exist. If key does not exist, a new key holding a hash is created.
/// If field already exists, no action is performed.
/// HashSet only when field does not exist
/// </summary>
Expand Down Expand Up @@ -788,7 +788,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
#region BitMaps Methods

/// <summary>
///
///
/// </summary>
/// <param name="key"></param>
/// <param name="offset"></param>
Expand Down Expand Up @@ -818,7 +818,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
GarnetStatus StringBitOperation(ArgSlice[] keys, BitmapOperation bitop, out long result);

/// <summary>
/// Perform a bitwise operation between multiple keys
/// Perform a bitwise operation between multiple keys
/// and store the result in the destination key.
/// </summary>
/// <param name="bitop"></param>
Expand Down Expand Up @@ -865,7 +865,7 @@ public interface IGarnetApi : IGarnetReadApi, IGarnetAdvancedApi
GarnetStatus HyperLogLogAdd(ArgSlice keys, string[] elements, out bool updated);

/// <summary>
/// Merge multiple HyperLogLog values into a unique value that will approximate the cardinality
/// Merge multiple HyperLogLog values into a unique value that will approximate the cardinality
/// of the union of the observed Sets of the source HyperLogLog structures.
/// </summary>
/// <param name="keys"></param>
Expand Down Expand Up @@ -986,6 +986,16 @@ public interface IGarnetReadApi
/// <returns></returns>
GarnetStatus SortedSetScore(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter);

/// <summary>
/// Returns the scores associated with the specified members in the sorted set stored at key.
/// For every member that does not exist in the sorted set, a nil value is returned.
/// </summary>
/// <param name="key"></param>
/// <param name="input"></param>
/// <param name="outputFooter"></param>
/// <returns></returns>
GarnetStatus SortedSetScores(byte[] key, ArgSlice input, ref GarnetObjectStoreOutput outputFooter);

/// <summary>
/// Returns the number of elements in the sorted set at key with a score between min and max.
/// </summary>
Expand All @@ -997,7 +1007,7 @@ public interface IGarnetReadApi

/// <summary>
/// Returns the number of elements in the sorted set with a value between min and max.
/// When all the elements in a sorted set have the same score,
/// When all the elements in a sorted set have the same score,
/// this command forces lexicographical ordering.
/// </summary>
/// <param name="key"></param>
Expand Down Expand Up @@ -1333,7 +1343,7 @@ public interface IGarnetReadApi
GarnetStatus StringGetBit(ArgSlice key, ArgSlice offset, out bool bValue);

/// <summary>
/// Count the number of set bits in a string.
/// Count the number of set bits in a string.
/// It can be specified an interval for counting, passing the start and end arguments.
/// </summary>
/// <param name="key"></param>
Expand All @@ -1343,7 +1353,7 @@ public interface IGarnetReadApi
GarnetStatus StringBitCount(ref SpanByte key, ref SpanByte input, ref SpanByteAndMemory output);

/// <summary>
/// Count the number of set bits in a string.
/// Count the number of set bits in a string.
/// It can be specified an interval for counting, passing the start and end arguments.
/// </summary>
/// <param name="key"></param>
Expand Down Expand Up @@ -1388,7 +1398,7 @@ public interface IGarnetReadApi
GarnetStatus HyperLogLogLength(ArgSlice[] keys, ref SpanByte input, out long count, out bool error);

/// <summary>
///
///
/// </summary>
/// <param name="keys"></param>
/// <param name="count"></param>
Expand Down
6 changes: 5 additions & 1 deletion libs/server/Objects/SortedSet/SortedSetObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ public enum SortedSetOperation : byte
ZRANDMEMBER,
ZDIFF,
ZSCAN,
ZMSCORE
}

/// <summary>
Expand Down Expand Up @@ -204,6 +205,9 @@ public override unsafe bool Operate(ref SpanByte input, ref SpanByteAndMemory ou
case SortedSetOperation.ZSCORE:
SortedSetScore(_input, ref output);
break;
case SortedSetOperation.ZMSCORE:
SortedSetScores(_input, input.Length, ref output);
break;
case SortedSetOperation.ZCOUNT:
SortedSetCount(_input, input.Length, _output);
break;
Expand Down Expand Up @@ -381,7 +385,7 @@ public static void InPlaceDiff(Dictionary<byte[], double> dict1, Dictionary<byte

private void UpdateSize(byte[] item, bool add = true)
{
// item's length + overhead to store item + value of type double added to sorted set and dictionary + overhead for those datastructures
// item's length + overhead to store item + value of type double added to sorted set and dictionary + overhead for those datastructures
var size = Utility.RoundUp(item.Length, IntPtr.Size) + MemoryUtils.ByteArrayOverhead + (2 * sizeof(double))
+ MemoryUtils.SortedSetEntryOverhead + MemoryUtils.DictionaryEntryOverhead;
this.Size += add ? size : -size;
Expand Down
57 changes: 55 additions & 2 deletions libs/server/Objects/SortedSet/SortedSetObjectImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,59 @@ private void SortedSetScore(byte* input, ref SpanByteAndMemory output)
}
}

private void SortedSetScores(byte* input, int length, ref SpanByteAndMemory output)
{
//ZMSCORE key member
var _input = (ObjectInputHeader*)input;
ObjectOutputHeader _output = default;

int count = _input->count;

bool isMemory = false;
MemoryHandle ptrHandle = default;

byte* ptr = output.SpanByte.ToPointer();
var curr = ptr;
var end = curr + output.Length;

byte* input_startptr = input + sizeof(ObjectInputHeader);
byte* input_currptr = input_startptr;
byte* input_endptr = input + length;

try
{
while (!RespWriteUtils.WriteArrayLength(count, ref curr, end))
ObjectUtils.ReallocateOutput(ref output, ref isMemory, ref ptr, ref ptrHandle, ref curr, ref end);

for (int c = 0; c < count; c++)
{
if (!RespReadUtils.ReadByteArrayWithLengthHeader(out var scoreKey, ref input_currptr, input_endptr))
return;
if (!sortedSetDict.TryGetValue(scoreKey, out var score))
{
while (!RespWriteUtils.WriteNull(ref curr, end))
ObjectUtils.ReallocateOutput(ref output, ref isMemory, ref ptr, ref ptrHandle, ref curr, ref end);
}
else
{
while (!RespWriteUtils.WriteBulkString(Encoding.ASCII.GetBytes(score.ToString()), ref curr, end))
ObjectUtils.ReallocateOutput(ref output, ref isMemory, ref ptr, ref ptrHandle, ref curr, ref end);
}
}
_output.bytesDone = (int)(input_currptr - input_startptr);
_output.countDone = count;
ProTip marked this conversation as resolved.
Show resolved Hide resolved
_output.opsDone = count;
}
finally
{
while (!RespWriteUtils.WriteDirect(ref _output, ref curr, end))
ObjectUtils.ReallocateOutput(ref output, ref isMemory, ref ptr, ref ptrHandle, ref curr, ref end);

if (isMemory) ptrHandle.Dispose();
output.Length = (int)(curr - ptr);
}
}

private void SortedSetCount(byte* input, int length, byte* output)
{
var _input = (ObjectInputHeader*)input;
Expand Down Expand Up @@ -287,7 +340,7 @@ private void SortedSetRank(byte* input, int length, byte* output)
private void SortedSetRange(byte* input, int length, ref SpanByteAndMemory output)
{
//ZRANGE key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]
//ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
//ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
var _input = (ObjectInputHeader*)input;
int count = _input->count;

Expand Down Expand Up @@ -747,7 +800,7 @@ private void GetRangeOrCountByLex(byte* input, int length, byte* output, SortedS
}

/// <summary>
/// Gets the rank of a member of the sorted set
/// Gets the rank of a member of the sorted set
/// in ascending or descending order
/// </summary>
/// <param name="input"></param>
Expand Down
7 changes: 4 additions & 3 deletions libs/server/Resp/CmdStrings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ public static ReadOnlySpan<byte> GetConfig(ReadOnlySpan<byte> key)
else return RESP_EMPTYLIST;
}

/// <summary>
/// Request strings
/// </summary>
/// <summary>
/// Request strings
/// </summary>
public static ReadOnlySpan<byte> CLIENT => "CLIENT"u8;
public static ReadOnlySpan<byte> SUBSCRIBE => "SUBSCRIBE"u8;
public static ReadOnlySpan<byte> RUNTXP => "RUNTXP"u8;
Expand Down Expand Up @@ -85,6 +85,7 @@ public static ReadOnlySpan<byte> GetConfig(ReadOnlySpan<byte> key)
public static ReadOnlySpan<byte> RESP_ERR => "-ERR unknown command\r\n"u8;
public static ReadOnlySpan<byte> RESP_CLUSTER_DISABLED => "-ERR This instance has cluster support disabled\r\n"u8;
public static ReadOnlySpan<byte> RESP_WRONG_ARGUMENTS => "-ERR wrong number of arguments for 'config|set' command\r\n"u8;
public static ReadOnlySpan<byte> RESP_WRONG_TYPE => "-WRONGTYPE Operation against a key holding the wrong kind of value.\r\n"u8;
public static ReadOnlySpan<byte> RESP_ERRNOTFOUND => "$-1\r\n"u8;
public static ReadOnlySpan<byte> RESP_ERRNOSUCHKEY => "-ERR no such key\r\n"u8;
public static ReadOnlySpan<byte> RESP_NOAUTH => "-NOAUTH Authentication required.\r\n"u8;
Expand Down
Loading