-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Adding ZRangeStore #2052
Adding ZRangeStore #2052
Conversation
Awesome - will try and review this tonight! Some quick pass feedback:
I'll try and give a thorough review tonight but wanted to give feedback ASAP too. This looks like a great effort to get the API in and match the norms, hugely appreciated <3 |
Hey @NickCraver :
this would of course bubble up as a Redis Server exception so I don't think there's anything to worry about from a type miss-match perspective. So I agree with you, |
@slorello89 Oh I see now (thanks for the detail!) - yes for certain that's why those were separate. I'd recommend we more closely match Redis here on these new methods by combining and look at folding those old style together in a future major release. |
@NickCraver - sounds good, I'll fold them together, honestly will make the API much cleaner. Did I break the thing in appveyor? sounds like it's probably separate from what I did. Probably should skip all my code since it's running on windows lol. |
Aye ignore - that's not you :) |
@NickCraver - one final question, for erroneous combinations of arguments, should we catch the error's pre-flight? E.g. using the inclusive/exclusive ranges for ZRANGE for ranks is not valid, same with using LIMIT/offset (skip/take). My philosophy is so long as the errors thrown by the server clear enough I would typically leave such validations to the server, not sure where you land on this:
your call (I sort of don't think either of those errors will be extremely helpful to someone debugging so we're probably better off throwing argument exceptions). This isn't really necessary when they're split because you can control the inputs. |
I could go either way on this - if it's a few lines to put an |
db.KeyDelete(new RedisKey[] {sourceKey, destinationKey}, CommandFlags.FireAndForget); | ||
db.SortedSetAdd(sourceKey, lexEntries, CommandFlags.FireAndForget); | ||
var exception = Assert.Throws<ArgumentException>(()=>db.SortedSetRangeAndStore(destinationKey, sourceKey,0,-1, take:5)); | ||
Assert.Equal($"take argument is not valid when sortedSetOrder is ByRank you may want to try setting the SortedSetOrder to ByLex or ByScore{Environment.NewLine}Parameter name: take", exception.Message); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This may be built on one OS and run on another - if new lines are in tests you'll want to use the optional param:
Assert.Equal("", "", ignoreLineEndingDifferences: true);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured out what it was, it's not an OS thing, it's a Framework vs core thing lol. framework's Argument
good ol' framework uses environment newline: https://github.com/microsoft/referencesource/blob/master/mscorlib/system/argumentexception.cs#L70
whereas core runtime uses a space:
https://github.com/dotnet/runtime/blob/main/src/libraries/System.Private.CoreLib/src/System/ArgumentException.cs#L86
lol
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just said, use the param name, good enough lol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Went through and tried to give a thorough review - major kudos on a plethora of tests <3 - happy to take these tweaks, hands off, or anywhere in-between - please let me know! Thanks a ton for everything so far here.
{ | ||
SortedSetOrder.ByLex => RedisLiterals.BYLEX, | ||
SortedSetOrder.ByScore => RedisLiterals.BYSCORE, | ||
_ => RedisValue.EmptyString |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Recommend RedisValue.Null
here, and downstream always call .GetLiteral()
and check ! .IsNull
on it (prop on RedisValue
). This makes maintenance a bit easier. Right now these 2 if/switches have to correlate.
/// <param name="take">Number of elements to pull into the new set.</param> | ||
/// <param name="flags">The flags to use for this operation</param> | ||
/// <remarks>https://redis.io/commands/zrangestore</remarks> | ||
/// <returns>The cardinality of the newly created sorted set</returns> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copy from above please!
|
||
if (sortedSetOrder != SortedSetOrder.ByRank) | ||
{ | ||
arguments.Add(sortedSetOrder.GetLiteral()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead, let's call .GetLiteral()
and check for .IsNull
on our if
here - extra line, but cleaner and decoupled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure thing, that's how I initially had it, I figured an enum-comparison would be slightly more efficient than the null check.
@@ -84,6 +86,7 @@ public static readonly RedisValue | |||
PING = "PING", | |||
PURGE = "PURGE", | |||
PX = "PX", | |||
REV = "REV", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alpha order on all these please :)
arguments.Add(RedisLiterals.LIMIT); | ||
arguments.Add(skip); | ||
arguments.Add(take); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm happy to take a pass at this but basically we want to avoid the List
and array allocations here, likely by using an existing Message
type. We may need to add a 7th on CommandKeyValueValueValueValueValueMessage
. If you want, I can take the efficiency part of that on and push those bits :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't mind doing this, what's the correct pattern here?
I guess it would be CommandKeyKeyValueValueValueValueValueValueValueMessage
, set any unused value to null and just not write the nulls out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as there's the command, 2 keys, and at most 7 literals?
Committing comment & more trivial suggestions in batch. Co-authored-by: Nick Craver <nrcraver@gmail.com>
I went through and made the updates you requested, One thing I'd point you at is all the classes I added to Also not 100% sure I did the unshipped->shipped conversion correctly, I think there's a script somewhere that automagically ports them for you, but it was less effort to just alphabetize copy/paste 😂 so if that's off I'd ask you to just do the port for that bit of meta-data. |
public override int ArgCount => 8; | ||
} | ||
|
||
private sealed class CommandKeyKeyValueValueValueValueValueValueValueMessage : CommandKeyBase |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's possible we've jumped the shark...but the garbage collector will thank us.
else | ||
{ | ||
return Message.Create(db, flags, RedisCommand.ZRANGESTORE, destinationKey, sourceKey, start, stop, RedisLiterals.REV); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: let's make this a switch expression with a _ =>
argument exception for order. (can do tonight)
return Message.Create(db, flags, RedisCommand.ZRANGESTORE, destinationKey, sourceKey, formattedStart, | ||
formattedStop, sortedSetOrder.GetLiteral()); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: can so more switch expressions here to simplicity - will poke tonight!
@slorello89 Sorry I've been slammed the last 2 days. The changes are just fantastic, thanks a ton - I'd like to push just a few style/doc tweaks tonight then merge this in :) I should be able to get to it this evening! |
{ | ||
var msg = CreateSortedSetRangeStoreMessage(Database, flags, sourceKey, destinationKey, start, stop, | ||
sortedSetOrder, order, exclude, skip, take); | ||
return await ExecuteAsync(msg, ResultProcessor.Int64); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh note for future - no async/await needed here, saves a state machine :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking awesome - thanks a ton for doing this and going all out on fitting in the existing codebase <3
Adds ZRangeStore for #2047
APIs added:
Rather than funneling everything through a single method, I left the interface to be reflective of the current ZRange interface in
IDatabase
/IDatabaseAsync
.Added a new Message type:
SortedSetRangeAndStoreCommandMessage
to handle theZRANGESTORE
as it didn't quite fit any of the other message types. I left this inRedisDatabase.cs
as this follows the pattern ofSortedSetCombineAndStoreCommandMessage
, if you'd like to evict it to somewhere else I've no issue doing that.API Quirks
ZRANGESTORE
with theREV
flag is a bit odd, all it effectively does is reverse the ordering of the start/stop arguments, basically ifREV
is setstart
must be greater thanstop
, I did not add any sort of smarts to effect this reversal by default as again this would not be the expected behavior of the older APIs, happy to effect the swap if you think that API would be more desirous.