StackExchange.Redis icon indicating copy to clipboard operation
StackExchange.Redis copied to clipboard

How hard would it be to group/multiplex a MGET across the cluster slots?

Open mshiels opened this issue 5 years ago • 14 comments

Was not even thinking about it when I started to explore, but normally MGET is not supported on a cluster. But I know at least a few situations where it's now support in clients like Lettuce(?) I think.

Thinking quickly without knowing the innards!! Breaks up a single MGET into an MGET per cluster shard, based on grouping the keys by shard they end up in? Doesn't sound too nasty, but again ideas/thoughts from those who know this better would be good!!

mshiels avatar Dec 23 '20 06:12 mshiels

In theory: entirely doable - however, while it works fine in the success case, it massively complicates things in the error scenarios, with things like timeouts, node errors, shard movement, etc - not to mention multi/exec; so: some thought as to how to handle those scenarios would be necessary. Honestly, I wonder whether "any errors at all, just give the hell up" is pragmatic

On Wed, 23 Dec 2020, 06:33 Michael Shiels, [email protected] wrote:

Was not even thinking about it when I started to explore, but normally MGET is not supported on a cluster. But I know at least a few situations where it's now support in clients like Lettuce(?) I think.

Thinking quickly without knowing the innards!! Breaks up a single MGET into an MGET per cluster shard, based on grouping the keys by shard they end up in? Doesn't sound too nasty, but again ideas/thoughts from those who know this better would be good!!

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/StackExchange/StackExchange.Redis/issues/1650, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAEHMDE2YMIMYKLQZFMZ43SWGFKVANCNFSM4VGS4R2A .

mgravell avatar Dec 23 '20 06:12 mgravell

Seems pragmatic enough to me :) BTW - I wasn't aware this isn't implemented though at all . currently in IDatabase we have -

///

/// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the special value nil is returned. /// /// The keys of the strings. /// The flags to use for this operation. /// The values of the strings with nil for keys do not exist. /// https://redis.io/commands/mget RedisValue[] StringGet(RedisKey[] keys, CommandFlags flags = CommandFlags.None);

It doesn't mention anything about not being implemented in case of a cluster. Should I look elsewhere for documentation about this ? I'm afraid I might be using other stuff as well that may not work with a cluster.

pianoman4873 avatar Dec 23 '20 07:12 pianoman4873

Every redis command has the cross-slot limitation on cluster; that's out of my hands - it is a server feature, and you need to read about "hash tags" in the redis cluster documentation to work around it. MGET is unusual and perhaps unique in that there is actually something useful and relevant we can do at the client when multiple slots are involved!

On Wed, 23 Dec 2020, 07:54 pianoman4873, [email protected] wrote:

Seems pragmatic enough to me :) BTW - I wasn't aware this isn't implemented though at all . currently in IDatabase we have -

///

/// Returns the values of all specified keys. For every key that does not hold a string value or does not exist, the special value nil is returned. /// /// The keys of the strings. /// The flags to use for this operation. /// The values of the strings with nil for keys do not exist. /// https://redis.io/commands/mget RedisValue[] StringGet(RedisKey[] keys, CommandFlags flags = CommandFlags.None);

It doesn't mention anything about not being implemented in case of a cluster. Should I look elsewhere for documentation about this ? I'm afraid I might be using other stuff as well that may not work with a cluster.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/StackExchange/StackExchange.Redis/issues/1650#issuecomment-749993755, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAEHMDLLWPVS6T2MU7C7HTSWGO3JANCNFSM4VGS4R2A .

mgravell avatar Dec 23 '20 11:12 mgravell

Thanks, I'll keep that in mind :) Funny that https://redis.io/commands/mget doesn't say anything about it either.

pianoman4873 avatar Dec 23 '20 12:12 pianoman4873

Again, it doesn't say it on any individual command because it applies to every command (that accepts multiple keys).

On Wed, 23 Dec 2020, 12:24 pianoman4873, [email protected] wrote:

Thanks, I'll keep that in mind :) Funny that https://redis.io/commands/mget doesn't say anything about it either.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/StackExchange/StackExchange.Redis/issues/1650#issuecomment-750254375, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAEHMHA36ZOTJCIUATPVUTSWHOOLANCNFSM4VGS4R2A .

mgravell avatar Dec 23 '20 12:12 mgravell

See "implemented subset" here: https://redis.io/topics/cluster-spec#implemented-subset

On Wed, 23 Dec 2020, 12:36 Marc Gravell, [email protected] wrote:

Again, it doesn't say it on any individual command because it applies to every command (that accepts multiple keys).

On Wed, 23 Dec 2020, 12:24 pianoman4873, [email protected] wrote:

Thanks, I'll keep that in mind :) Funny that https://redis.io/commands/mget doesn't say anything about it either.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/StackExchange/StackExchange.Redis/issues/1650#issuecomment-750254375, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAEHMHA36ZOTJCIUATPVUTSWHOOLANCNFSM4VGS4R2A .

mgravell avatar Dec 23 '20 12:12 mgravell

Got you - thanks !

pianoman4873 avatar Dec 23 '20 13:12 pianoman4873

In the case of an MGET I would think 'best attempt' is probably still better than 'nothing' Right? MSET is a bit trickier but also semi-useful, beyond that it gets harder and harder I think. So it looks like I be spelunking the code to see how this all fits together, thanks again for the comments.

mshiels avatar Dec 23 '20 13:12 mshiels

Since I am just getting going with this stuff - a few quick questions on if this could be done/simulated/manipulated above StackExchange.Redis - do our own breaking up up of our list - target specific servers based on the shards etc? If that would work we may just try that for now. MGET just seems like something I don't want to lose,.

mshiels avatar Jan 01 '21 05:01 mshiels

There is a HashSlot method on the multiplexer that returns the slot for a key. Basically, group by slot, issue requests per group, and recombine the results from all the groups.

On Fri, 1 Jan 2021, 05:49 Michael Shiels, [email protected] wrote:

Since I am just getting going with this stuff - a few quick questions on if this could be done/simulated/manipulated above StackExchange.Redis - do our own breaking up up of our list - target specific servers based on the shards etc? If that would work we may just try that for now. MGET just seems like something I don't want to lose,.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/StackExchange/StackExchange.Redis/issues/1650#issuecomment-753264980, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAEHMBBSXWBHKEQ2BNAGJ3SXVO7RANCNFSM4VGS4R2A .

mgravell avatar Jan 01 '21 09:01 mgravell

Oh darn, lost what I had typed, oh well - so yes I had seen that key to hash which then allows you to tell what machiens service that shard. So the question is what interface would still allow mget even in the face of it being a cluster, wher we are breaking things apart ourselves. I have not 100% explored the interface yet but am going as fast as I can in terms of exploration. We are doing a wrapper layer to let us implement 2 tier and other stuff eventually. Using ideas/concepts from CachemAnager/CachingFramework wrappers for StackExchange.Redis. And the nasty part is a COM wrapper for our older code (vs doing a hiredis-cluster port) which seems to work well even under some heavy loads. Hats off it's so far the slickest interface in terms of low connections/high thread support that I found.

mshiels avatar Jan 02 '21 16:01 mshiels

The tricky thing is: we have no guarantees at that point in terms of atomicity or point-in-time consistency.

What I am tempted to do is add a new CommandFlags option along the lines of AllowCrossSlot (name TBD) that does this split internally (but grouping by target server, not by slot, so : fewer ops) - and re-combine inside the library. The point being: I wouldn't want to make the existing API do this without making it opt-in. We could make the MGET operation mention this in the exception message for cross-slot scenarios.

mgravell avatar Jan 02 '21 17:01 mgravell

Ok makes sense, in our case it's very basic caching so would be fine probably . But ya that could add a new layer of power to the library for sure. I didn't even think it was sanely doable till I ran across 'lettuce' I think that implemented it recently. That got my brain churning. I am assuming from what I have digested so far MGET will still be more efficient (we are only using 100 batches I think) than doing that many Async GETs even, which was what got me thinking it has to be doable in some really slick/sneaky way that would make it usable for more than one thing possibly. Thanks again!! So basically the GetString(keys[]) function would benefit only if requested, which is perfect, if you know it's safe/doable, you can enable it otherwise I assume it would fail on a cluster?

mshiels avatar Jan 02 '21 21:01 mshiels

hi all, any update on this?

czd890 avatar Jul 02 '24 03:07 czd890