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

Added support to expire specfic member of a sorted set #1000

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

Vijay-Nirmal
Copy link
Contributor

@Vijay-Nirmal Vijay-Nirmal commented Feb 5, 2025

Adding support for member-specific expiration in a sorted set. This PR adds ZEXPIRE, ZPEXPIRE, ZEXPIREAT, ZPEXPIREAT, ZTTL, ZPTTL, ZEXPIRETIME, ZPEXPIRETIME, ZPERSIST and ZCOLLECT commands to garnet to support this feature.

  • Implementation of ZEXPIRE, ZPEXPIRE, ZEXPIREAT, ZPEXPIREAT, ZTTL, ZPTTL, ZEXPIRETIME, ZPEXPIRETIME and ZPERSIST commands
  • Implementation of ZCOLLECT to delete expired fields on demand
  • Add Integration Test cases to get all the new commands
  • Add Integration to test all the existing Hash set commands with expiring fields
  • ACL Test
  • Slot Verification Test
  • Add documentation
  • Add a background task to call ZCOLLECT to delete expired items
  • Benchmarking and making sure there is no performance regression

Since this is a garnet specific command, I used hashset expire API format as the base line. Below is the syntax for each command

ZEXPIRE => ZEXPIRE key seconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...]
ZPEXPIRE => ZPEXPIRE key milliseconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...]
ZEXPIREAT => ZEXPIREAT key unix-time-seconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...]
ZPEXPIREAT => ZPEXPIREAT key unix-time-milliseconds [NX | XX | GT | LT] MEMBERS nummembers member [member ...]
ZTTL => ZTTL key MEMBERS nummembers member [member ...]
ZPTTL => ZPTTL key MEMBERS nummembers member [member ...]
ZEXPIRETIME => ZEXPIRETIME key MEMBERS nummembers member [member ...]
ZPEXPIRETIME => ZPEXPIRETIME key MEMBERS nummembers member [member ...]
ZPERSIST => ZPERSIST key MEMBERS nummembers member [member ...]
ZCOLLECT => ZCOLLECT key [key ...]

@@ -4837,6 +4837,31 @@
}
]
},
{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't these Garnet-specific commands be in playground/CommandInfoUpdater/GarnetCommandsInfo.json?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @TalZaccai ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember it should be in both otherwise there was an issue with test cases. I need to check once again its been sometime

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@@ -1523,6 +1523,241 @@ Integer reply: the number of members in the resulting sorted set at destination.

---

### ZEXPIRE
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These commands are not in RESP - maybe we need a section on this page for Garnet-specific Z commands.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have a plan to reorg the documentation for all commands. Here is my plan

  1. Create a new page just to show all commands in Garnet (Combine commands from Redis and Garnet specific). It will contain a table with columns category, command, Is garnet specific, description, and note. This page will not contain any commands that are yet to be implemented.
  2. Create a separate page for each category. Each data structure is its own page, there won't be any separation of garnet-specific commands, there will be just a field saying if it's a garnet-specific command.
  3. The Garnet-specific page that is there will be removed and those commands will be added as part of their own category's page.

Let me know if you are find with this change. I think this is need where we add more and more garnet-specific commands . I think we shouldn't stop treating special casing garnet-specific commands and just treat is as a normal command.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds okay, but we do need special pages to call out these:

  • Highlight a list of Garnet-specific commands as existing RESP users will not be familiar with those and can learn about it from one place
  • Users of other RESP systems might want to know which RESP commands are NYI in Garnet

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added this in Garnet-specific commands doc but I will be reorganizing it as part of a separate PR

@Vijay-Nirmal
Copy link
Contributor Author

@TalZaccai @badrishc With this PR, SortedSetOperation enum has more than 31 flags which causes issues with RespInputHeader because of FlagMask. Will you guys look into making the required changes? Or should I look into as part of a separate PR? Or should I alter my approach in this PR somehow?

// Code to reproduce the issue
var header = new RespInputHeader(GarnetObjectType.SortedSet) { SortedSetOp = SortedSetOperation.ZCOLLECT };
Assert.AreEqual(SortedSetOperation.ZCOLLECT, header.SortedSetOp ) // Fails Actual value: ZCARD

@TalZaccai
Copy link
Contributor

@TalZaccai @badrishc With this PR, SortedSetOperation enum has more than 31 flags which causes issues with RespInputHeader because of FlagMask. Will you guys look into making the required changes? Or should I look into as part of a separate PR? Or should I alter my approach in this PR somehow?

// Code to reproduce the issue
var header = new RespInputHeader(GarnetObjectType.SortedSet) { SortedSetOp = SortedSetOperation.ZCOLLECT };
Assert.AreEqual(SortedSetOperation.ZCOLLECT, header.SortedSetOp ) // Fails Actual value: ZCARD

Hey @Vijay-Nirmal, thanks for reaching out!
One idea I had, that might be least-disruptive at this moment, is to group some of these commands and have them use the same SortedSetOperation, for instance - ZRANGE, ZRANGEBYSCORE, ZRANGEBYLEX, ZREVRANGE, ZREVRANGEBYSCORE, ZREVRANGEBYLEX, can use ZRANGE and encode the BYSCORE, BYLEX and REV as flags in one of the additional integer arguments in ObjectInput (arg1 / arg2).
Let me know if that makes sense to you and if you'd like to take a hack at it. Let me know if you need any more details.

@Vijay-Nirmal
Copy link
Contributor Author

@TalZaccai Thought of the same thing as a temp fix, I will do it as part of a separate PR

Hey @Vijay-Nirmal, thanks for reaching out!

So formal ;-)

@Vijay-Nirmal Vijay-Nirmal marked this pull request as ready for review February 9, 2025 20:22
@Vijay-Nirmal
Copy link
Contributor Author

I am marking it as ready for review but there are 2 pending items before merging this PR

  1. Fixing the issue caused by the size of SortedSetOperation enum. Will do it as part of a separate PR, need to merge that before this PR.
  2. Benchmarking and making sure there is no performance regression. I will do it as part of this PR but I don't want to hold the PR review still I finish this item

@Vijay-Nirmal
Copy link
Contributor Author

Here is the benchmark result, I don't see any much difference between main and this PR with no expiring items. I see very minor change in the item but it could be run to run variant. If you guys have any concern then let me know

Main as of 046fcd8

Method Params Mean Error StdDev Allocated
ZAddRem ACL 86.227 us 0.8282 us 0.7747 us 18400 B
ZCard ACL 6.645 us 0.0741 us 0.0693 us -
ZCount ACL 7.957 us 0.0661 us 0.0586 us -
ZDiff ACL 7.961 us 0.0739 us 0.0692 us -
ZDiffStore ACL 12.663 us 0.0878 us 0.0822 us -
ZIncrby ACL 8.183 us 0.1077 us 0.1008 us -
ZInter ACL 9.373 us 0.1097 us 0.1026 us -
ZInterCard ACL 14.384 us 0.0592 us 0.0554 us -
ZInterStore ACL 16.044 us 0.0903 us 0.0800 us -
ZLexCount ACL 8.823 us 0.0597 us 0.0529 us -
ZMPop ACL 47.960 us 0.4061 us 0.3600 us 6400 B
ZMScore ACL 8.659 us 0.0911 us 0.0852 us -
ZPopMax ACL 45.545 us 0.1678 us 0.1401 us 6400 B
ZPopMin ACL 47.118 us 0.2320 us 0.2056 us 6400 B
ZRandMember ACL 9.218 us 0.0380 us 0.0355 us 1184 B
ZRange ACL 8.501 us 0.0794 us 0.0742 us -
ZRangeStore ACL 14.332 us 0.0969 us 0.0906 us -
ZRank ACL 7.893 us 0.0492 us 0.0460 us -
ZRemRangeByLex ACL 49.690 us 0.2836 us 0.2653 us 6400 B
ZRemRangeByRank ACL 49.521 us 0.2618 us 0.2449 us 6400 B
ZRemRangeByScore ACL 48.774 us 0.4557 us 0.4263 us 6400 B
ZRevRank ACL 7.928 us 0.0654 us 0.0612 us -
ZScan ACL 6.649 us 0.0388 us 0.0363 us 776 B
ZScore ACL 8.917 us 0.0511 us 0.0478 us -
ZUnion ACL 8.718 us 0.1046 us 0.0978 us -
ZUnionStore ACL 16.794 us 0.1451 us 0.1357 us -
ZAddRem AOF 92.907 us 0.5947 us 0.5562 us 18400 B
ZCard AOF 25.226 us 0.2007 us 0.1877 us 3200 B
ZCount AOF 44.699 us 0.3999 us 0.3741 us 20000 B
ZDiff AOF 77.667 us 1.2583 us 1.1770 us 25600 B
ZDiffStore AOF 103.851 us 0.5590 us 0.4956 us 27200 B
ZIncrby AOF 61.546 us 1.1325 us 1.0594 us 12000 B
ZInter AOF 79.793 us 0.7543 us 0.7055 us 34400 B
ZInterCard AOF 83.690 us 0.7922 us 0.7411 us 34400 B
ZInterStore AOF 143.590 us 2.0081 us 1.8784 us 123200 B
ZLexCount AOF 56.802 us 0.5885 us 0.5505 us 66400 B
ZMPop AOF 158.489 us 1.6051 us 1.5014 us 59201 B
ZMScore AOF 38.518 us 0.2622 us 0.2324 us 6400 B
ZPopMax AOF 111.243 us 1.1636 us 1.0884 us 48001 B
ZPopMin AOF 110.495 us 1.1363 us 1.0629 us 48001 B
ZRandMember AOF 9.176 us 0.0774 us 0.0646 us 1184 B
ZRange AOF 50.276 us 0.4403 us 0.4119 us 28000 B
ZRangeStore AOF 74.163 us 1.0933 us 0.9692 us 12800 B
ZRank AOF 38.044 us 0.2703 us 0.2396 us 13600 B
ZRemRangeByLex AOF 133.386 us 1.5258 us 1.4273 us 111201 B
ZRemRangeByRank AOF 118.609 us 1.2706 us 1.1885 us 84801 B
ZRemRangeByScore AOF 129.039 us 0.7127 us 0.6318 us 84001 B
ZRevRank AOF 35.943 us 0.3040 us 0.2843 us 13600 B
ZScan AOF 6.415 us 0.0408 us 0.0382 us 776 B
ZScore AOF 39.490 us 0.3674 us 0.3436 us 6400 B
ZUnion AOF 82.716 us 0.7452 us 0.6971 us 34400 B
ZUnionStore AOF 154.892 us 2.0261 us 1.8952 us 129600 B
ZAddRem None 79.749 us 0.4961 us 0.4398 us 18400 B
ZCard None 27.899 us 0.1899 us 0.1776 us 3200 B
ZCount None 46.815 us 0.4123 us 0.3856 us 20000 B
ZDiff None 67.120 us 0.8419 us 0.7875 us 25600 B
ZDiffStore None 97.543 us 0.4154 us 0.3885 us 27200 B
ZIncrby None 57.513 us 0.3718 us 0.3477 us 12000 B
ZInter None 76.163 us 0.6307 us 0.5899 us 34400 B
ZInterCard None 77.876 us 0.6075 us 0.5683 us 34400 B
ZInterStore None 115.714 us 1.0104 us 0.9451 us 79200 B
ZLexCount None 57.176 us 0.7926 us 0.7414 us 66400 B
ZMPop None 142.690 us 0.8959 us 0.8380 us 59201 B
ZMScore None 38.645 us 0.2910 us 0.2722 us 6400 B
ZPopMax None 106.227 us 0.8101 us 0.7577 us 48001 B
ZPopMin None 106.171 us 1.3246 us 1.2390 us 48001 B
ZRandMember None 9.147 us 0.0908 us 0.0850 us 1184 B
ZRange None 49.757 us 0.4482 us 0.4192 us 28000 B
ZRangeStore None 68.989 us 0.5509 us 0.5153 us 12800 B
ZRank None 37.445 us 0.1882 us 0.1761 us 13600 B
ZRemRangeByLex None 126.054 us 1.1288 us 1.0558 us 111201 B
ZRemRangeByRank None 111.534 us 0.9427 us 0.8818 us 84801 B
ZRemRangeByScore None 123.049 us 0.8772 us 0.8205 us 84001 B
ZRevRank None 36.239 us 0.2858 us 0.2673 us 13600 B
ZScan None 6.340 us 0.0413 us 0.0386 us 776 B
ZScore None 39.258 us 0.3027 us 0.2831 us 6400 B
ZUnion None 78.463 us 0.5561 us 0.4643 us 34400 B
ZUnionStore None 122.963 us 1.0630 us 0.8877 us 84800 B

This PR as of c236828

Method Params Mean Error StdDev Allocated
ZAddRem ACL 86.291 us 0.6677 us 0.6246 us 18400 B
ZCard ACL 6.854 us 0.0485 us 0.0454 us -
ZCount ACL 8.054 us 0.0732 us 0.0685 us -
ZDiff ACL 8.431 us 0.0581 us 0.0543 us -
ZDiffStore ACL 12.924 us 0.0857 us 0.0801 us -
ZIncrby ACL 9.092 us 0.0691 us 0.0612 us -
ZInter ACL 9.879 us 0.0685 us 0.0641 us -
ZInterCard ACL 14.899 us 0.0712 us 0.0666 us -
ZInterStore ACL 16.240 us 0.2064 us 0.1931 us -
ZLexCount ACL 9.197 us 0.0963 us 0.0900 us -
ZMPop ACL 50.586 us 0.3868 us 0.3618 us 6400 B
ZMScore ACL 9.395 us 0.0334 us 0.0296 us -
ZPopMax ACL 50.009 us 0.2803 us 0.2622 us 6400 B
ZPopMin ACL 50.357 us 0.3146 us 0.2943 us 6400 B
ZRandMember ACL 9.241 us 0.1025 us 0.0959 us 1184 B
ZRange ACL 8.791 us 0.0822 us 0.0769 us -
ZRangeStore ACL 14.728 us 0.0774 us 0.0724 us -
ZRank ACL 8.290 us 0.0921 us 0.0862 us -
ZRemRangeByLex ACL 49.155 us 0.2526 us 0.2363 us 6400 B
ZRemRangeByRank ACL 51.690 us 0.4511 us 0.4219 us 6400 B
ZRemRangeByScore ACL 51.636 us 0.2608 us 0.2439 us 6400 B
ZRevRank ACL 8.069 us 0.0826 us 0.0773 us -
ZScan ACL 6.389 us 0.0370 us 0.0346 us 776 B
ZScore ACL 8.999 us 0.0506 us 0.0423 us -
ZUnion ACL 8.939 us 0.0670 us 0.0523 us -
ZUnionStore ACL 17.154 us 0.1073 us 0.0951 us -
ZAddRem AOF 93.565 us 0.8697 us 0.8135 us 18400 B
ZCard AOF 26.424 us 0.1271 us 0.1127 us 3200 B
ZCount AOF 47.971 us 0.4615 us 0.4317 us 20000 B
ZDiff AOF 72.860 us 0.9051 us 0.8466 us 25600 B
ZDiffStore AOF 103.681 us 0.7512 us 0.7027 us 27200 B
ZIncrby AOF 61.990 us 0.8294 us 0.7758 us 12000 B
ZInter AOF 80.809 us 1.2344 us 1.1547 us 34400 B
ZInterCard AOF 84.643 us 0.9549 us 0.8932 us 34400 B
ZInterStore AOF 148.237 us 1.5363 us 1.4371 us 124800 B
ZLexCount AOF 58.108 us 0.8823 us 0.8253 us 66400 B
ZMPop AOF 158.784 us 1.6676 us 1.5599 us 60801 B
ZMScore AOF 40.436 us 0.2826 us 0.2644 us 6400 B
ZPopMax AOF 111.047 us 1.4156 us 1.3241 us 49601 B
ZPopMin AOF 110.527 us 1.0386 us 0.8673 us 49601 B
ZRandMember AOF 9.019 us 0.0477 us 0.0423 us 1184 B
ZRange AOF 51.151 us 0.3604 us 0.3195 us 28000 B
ZRangeStore AOF 75.264 us 0.6721 us 0.5958 us 12800 B
ZRank AOF 37.878 us 0.2489 us 0.2206 us 13600 B
ZRemRangeByLex AOF 136.738 us 0.9088 us 0.8501 us 112801 B
ZRemRangeByRank AOF 121.395 us 1.4137 us 1.3224 us 86401 B
ZRemRangeByScore AOF 131.464 us 1.0325 us 0.9658 us 85601 B
ZRevRank AOF 38.573 us 0.2659 us 0.2488 us 13600 B
ZScan AOF 6.343 us 0.0332 us 0.0295 us 776 B
ZScore AOF 39.967 us 0.1842 us 0.1723 us 6400 B
ZUnion AOF 82.588 us 1.0647 us 0.9959 us 34400 B
ZUnionStore AOF 155.708 us 1.5391 us 1.2852 us 131200 B
ZAddRem None 82.372 us 0.6633 us 0.6205 us 18400 B
ZCard None 26.987 us 0.2067 us 0.1934 us 3200 B
ZCount None 46.070 us 0.3793 us 0.3548 us 20000 B
ZDiff None 66.825 us 0.7742 us 0.7242 us 25600 B
ZDiffStore None 98.438 us 0.4567 us 0.4272 us 27200 B
ZIncrby None 55.608 us 0.3032 us 0.2836 us 12000 B
ZInter None 78.494 us 0.7307 us 0.6102 us 34400 B
ZInterCard None 78.774 us 1.0076 us 0.9425 us 34400 B
ZInterStore None 118.745 us 1.3749 us 1.2861 us 80800 B
ZLexCount None 57.529 us 0.6328 us 0.5284 us 66400 B
ZMPop None 144.723 us 1.3884 us 1.2987 us 60801 B
ZMScore None 39.741 us 0.3986 us 0.3533 us 6400 B
ZPopMax None 106.519 us 0.9822 us 0.9188 us 49601 B
ZPopMin None 104.701 us 1.0068 us 0.9418 us 49601 B
ZRandMember None 8.973 us 0.0403 us 0.0336 us 1184 B
ZRange None 50.707 us 0.4560 us 0.4043 us 28000 B
ZRangeStore None 67.222 us 0.5196 us 0.4606 us 12800 B
ZRank None 36.391 us 0.2621 us 0.2452 us 13600 B
ZRemRangeByLex None 129.346 us 1.3225 us 1.2371 us 112801 B
ZRemRangeByRank None 114.506 us 0.7495 us 0.7011 us 86401 B
ZRemRangeByScore None 124.950 us 0.9076 us 0.8489 us 85601 B
ZRevRank None 36.698 us 0.3525 us 0.3297 us 13600 B
ZScan None 6.386 us 0.0343 us 0.0321 us 776 B
ZScore None 39.525 us 0.3649 us 0.3235 us 6400 B
ZUnion None 77.541 us 0.7434 us 0.6954 us 34400 B
ZUnionStore None 124.973 us 1.5389 us 1.4395 us 86400 B

@Vijay-Nirmal
Copy link
Contributor Author

Are we supporting Resp format v2? If not why some of the commands has respProtocolVersion flag?

@TalZaccai
Copy link
Contributor

TalZaccai commented Feb 14, 2025

Are we supporting Resp format v2? If not why some of the commands has respProtocolVersion flag?

We do indeed support RESP3, not fully yet though.

Copy link
Contributor

@TalZaccai TalZaccai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added a few comments, not done reviewing yet...

@@ -829,5 +829,255 @@
"Flags": "RO"
}
]
},
{
"Command": "ZCOLLECT",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also add docs for these (GarnetCommandsDocs.json)

@@ -403,7 +403,7 @@ private static unsafe bool TryGetNextSetObjects(byte[] key, SortedSetObject sort
{
result = default;

if (sortedSetObj.Dictionary.Count == 0) return false;
if (sortedSetObj.Count() == 0) return false;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each of these Count() calls is an O(n) op, and the count can't change since you're locking the key here, so you might want to cache that value. The caller of this method is also calling Count(), so you might also pass that along to the method.

@@ -132,10 +138,15 @@ public enum SortedSetOrderOperation
/// <summary>
/// Sorted Set
/// </summary>
public partial class SortedSetObject : GarnetObjectBase
public unsafe partial class SortedSetObject : GarnetObjectBase
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the added unsafe?

}
else
{
sortedSetDict.Add(item, score);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can do add an addItem boolean to decide if we should add these (as these adds are duplicated)

@@ -171,20 +203,41 @@ public SortedSetObject(BinaryReader reader)
/// <summary>
/// Copy constructor
/// </summary>
public SortedSetObject(SortedSet<(double, byte[])> sortedSet, Dictionary<byte[], double> sortedSetDict, long expiration, long size)
public SortedSetObject(SortedSet<(double, byte[])> sortedSet, Dictionary<byte[], double> sortedSetDict, Dictionary<byte[], long> expirationTimes, PriorityQueue<byte[], long> expirationQueue, long expiration, long size)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not implement a copy constructor that takes a SortedSetObject instance?

@@ -223,6 +290,7 @@ public void Add(byte[] item, double score)
/// </summary>
public bool Equals(SortedSetObject other)
{
// TODO: Implement equals with expiration times
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not in the scope of this PR?

dict1.Remove(item.Key);
}
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool TryGetScore(byte[] key, out double value)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add XML comments for public methods


public int Count()
{
if (expirationTimes is null)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: !HasExpirableItems

Copy link
Contributor

@TalZaccai TalZaccai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some more comments...

@@ -385,7 +395,8 @@ private void SortedSetIncrement(ref ObjectInput input, ref SpanByteAndMemory out

if (sortedSetDict.TryGetValue(member, out var score))
{
sortedSetDict[member] += incrValue;
score = IsExpired(member) ? 0 : score;
sortedSetDict[member] = score + incrValue;
sortedSet.Remove((score, member));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're changing the score if the member is expired, so you might not be removing the member from the sorted set here...

@@ -641,6 +642,36 @@ private bool NetworkHCOLLECT<TGarnetApi>(ref TGarnetApi storageApi)
return true;
}

private bool NetworkZCOLLECT<TGarnetApi>(ref TGarnetApi storageApi)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

General question: why is this an admin command?

var fieldOption = parseState.GetArgSliceByRef(currIdx++);
if (!fieldOption.ReadOnlySpan.EqualsUpperCaseSpanIgnoringCase(CmdStrings.MEMBERS))
{
return AbortWithErrorMessage(Encoding.ASCII.GetBytes(string.Format(CmdStrings.GenericErrMandatoryMissing, "MEMBERS")));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should have an AbortWithErrorMessage that takes a (string format, params string[] args)... If you could add that that'd be cool

@@ -1697,5 +1697,265 @@ private unsafe bool SortedSetBlockingMPop()

return true;
}

/// <summary>
/// Sets an expiration time for a member in the SortedSet stored at key.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you could add the command(s) format on the XML comment that'd be super helpful (e.g. ZEXPIRE key seconds [NX | XX | GT | LT] MEMBERS member [member ... ])
Especially when these are not standard RESP commands.

return AbortWithErrorMessage(Encoding.ASCII.GetBytes(string.Format(CmdStrings.GenericErrMandatoryMissing, "MEMBERS")));
}

if (!parseState.TryGetInt(currIdx++, out var numMembers))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need numMembers? Can't we just assume that all strings after the MEMBERS keyword are members?

/// <param name="outputFooter">The output footer object to store the result.</param>
/// <param name="objectContext">The object context for the operation.</param>
/// <returns>The status of the operation.</returns>
public GarnetStatus SortedSetExpire<TObjectContext>(ArgSlice key, long expireAt, bool isMilliseconds, ExpireOption expireOption, ref ObjectInput input, ref GarnetObjectStoreOutput outputFooter, ref TObjectContext objectContext)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be 2 implementations for each of these methods - 1 for programmatic calls and 1 for network calls. The network call should have the ref ObjectInput input, ref GarnetObjectStoreOutput outputFooter parameters and shouldn't have to make any changes to the ObjectInput / parseState. The programmatic call should not have any of these parameters and should build the ObjectInput & parseState from scratch as well as process the output.
Refer to other methods for examples (e.g. SortedSetPop)


static void ExecuteSortedSetCollect(ScratchBufferManager scratchBufferManager, StorageSession storageSession)
{
var header = new RespInputHeader(GarnetObjectType.SortedSet) { SortedSetOp = SortedSetOperation.ZCOLLECT };
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code (as well as the contents of ExecuteHashCollect) doesn't belong here IMO, there should be a programmatic call to HashCollect / SortedSetCollect in SortedSetOps (see my comment about programmatic calls there).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants