-
Notifications
You must be signed in to change notification settings - Fork 24.4k
A better approach for COMMAND INFO for movablekeys commands #8324
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
Conversation
e153acf to
7b73d7a
Compare
|
@guybe7 for the 3 bugfixes you found (ZDIFFSTORE etc, mentioned above), i think it would be a good idea to issue a separate PR, so we can cherry pick that fix to 6.0. |
oranagra
left a comment
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.
@guybe7 i assume you compared the first 7 fields of COMMAND output to the previous version and saw that their identical for all command (except for the few which were buggy). right?
p.s. did you run valgrind?
|
@redis/core-team please approve the new COMMAND output fields, see example in the issue, and the new module API |
itamarhaber
left a comment
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 approve of the approach - haven't done a CR though
|
last commit handles the above comments by @yossigo + add the "startfrom" arg to the keyword spec(see top comment) |
|
@redis/core-team note the changes:
|
madolson
left a comment
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 have a high level of question of why do we have flags related to the keyspecs? I was meandering through some clients and I couldn't figure out where you would differentiate the read/write path. I thought about client side caching, but this doesn't give us enough introspection to use it for that case. It doesn't add a lot of overhead, but unclear why we want to maintain it.
I'm good with the change as long as we've thought through why we're sending extra information to clients.
|
@madolson i don't have a clear use case for the read/write flags for the keyspecs, but i think we should make that distinction, i'm sure it'll come in handy, even inside redis one day. maintaining this info in the command table is not a huge overhead, and since for each command we always know what each key argument is gonna be used for, there's never any doubt as to what to fill in that field. |
|
Sure, I guess my concern is we're optimistically building out this functionality without a clear understanding of how it will be used. The key location use case, which is well understand for cluster mode and ACLs, doesn't really need it. The one way decision is exposing this to clients. Even if we have use cases inside Redis, it may not be relevant to clients. The API is solid though, we could always introduce new specs/deprecate old ones. The risk is small is small in the end, I'm just not convinced we should release in for 6.2. |
|
@oranagra @guybe7 and myself got to discuss this a bit more. In a nutshell, we conclude that:
Calling @mgravell @andymccurdy @sazzad16 @mp911de @luin and other Redis client library developers to provide their perspective. TL;DR the idea is to extend |
|
Lettuce doesn't use |
|
@mp911de thanks.
|
Exactly. This more due to the fact that with Java you kind of expect a method declaration to assist users. Aside from that, we have a method to dispatch plain commands (
We have a parser for the |
|
@mp911de so with |
|
Excuse my oversimplified representation of our API. The actual code to use dispatch(MyCommands.SET, new StatusOutput<>(StringCodec.UTF8), new CommandArgs<>(StringCodec.UTF8).addKey(key).addValue(value));
|
|
@redis/core-team please approve again. quite a lot have changed since last time. i suggest to focus on reviewing the module api, and the output of COMMAND command. note that the changes to COMMAND command are gonna be reverted soon (that's why we don't document it yet), and instead will be probably put into a new COMMANDS command (the old command will only be kept for backwards compatibility, and the one one will have info on sub-commands). |
|
Approving the concept and APIs. |
yossigo
left a comment
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.
LGTM.
Re-reading this after several months, I realize we may need to include pseudo code for the key spec handling, or better yet - provide a reference implementation.
|
running daily CI before merging: https://github.com/redis/redis/actions/runs/1232825354 |
|
@guybe7 valgrind found some issues in the module API: https://github.com/redis/redis/runs/3596355820?check_suite_focus=true |
…DELETE) (#10122) The new ACL key based permissions in #9974 require the key-specs (#8324) to have more explicit flags rather than just READ and WRITE. See discussion in #10040 This PR defines two groups of flags: One about how redis internally handles the key (mutually-exclusive). The other is about the logical operation done from the user's point of view (3 mutually exclusive write flags, and one read flag, all optional). In both groups, if we can't explicitly flag something as explicit read-only, delete-only, or insert-only, we flag it as `RW` or `UPDATE`. here's the definition from the code: ``` /* Key-spec flags * * -------------- */ /* The following refer what the command actually does with the value or metadata * of the key, and not necessarily the user data or how it affects it. * Each key-spec may must have exaclty one of these. Any operation that's not * distinctly deletion, overwrite or read-only would be marked as RW. */ #define CMD_KEY_RO (1ULL<<0) /* Read-Only - Reads the value of the key, but * doesn't necessarily returns it. */ #define CMD_KEY_RW (1ULL<<1) /* Read-Write - Modifies the data stored in the * value of the key or its metadata. */ #define CMD_KEY_OW (1ULL<<2) /* Overwrite - Overwrites the data stored in * the value of the key. */ #define CMD_KEY_RM (1ULL<<3) /* Deletes the key. */ /* The follwing refer to user data inside the value of the key, not the metadata * like LRU, type, cardinality. It refers to the logical operation on the user's * data (actual input strings / TTL), being used / returned / copied / changed, * It doesn't refer to modification or returning of metadata (like type, count, * presence of data). Any write that's not INSERT or DELETE, would be an UPADTE. * Each key-spec may have one of the writes with or without access, or none: */ #define CMD_KEY_ACCESS (1ULL<<4) /* Returns, copies or uses the user data from * the value of the key. */ #define CMD_KEY_UPDATE (1ULL<<5) /* Updates data to the value, new value may * depend on the old value. */ #define CMD_KEY_INSERT (1ULL<<6) /* Adds data to the value with no chance of, * modification or deletion of existing data. */ #define CMD_KEY_DELETE (1ULL<<7) /* Explicitly deletes some content * from the value of the key. */ ``` Unrelated changes: - generate-command-code.py is only compatible with python3 (modified the shabang) - generate-command-code.py print file on json parsing error - rename `shard_channel` key-spec flag to just `channel`. - add INCOMPLETE flag in input spec of SORT and SORT_RO
Edit: the interfaces here later changed, see #10056
Fix #7297
The problem:
Today, there is no way for a client library or app to know the key name indexes for commands such as ZUNIONSTORE/EVAL and others with "numkeys", since COMMAND INFO returns no useful info for them.
For cluster-aware redis clients, this requires to 'patch' the client library code specifically for each of these commands or to resolve each execution of these commands with COMMAND GETKEYS.
The solution:
Introducing key specs other than the legacy "range" (first,last,step)
The 8th element of the command info array, if exists, holds an array of key specs. The array may be empty, which indicates the command doesn't take any key arguments or may contain one or more key-specs, each one may leads to the discovery of 0 or more key arguments.
A client library that doesn't support this key-spec feature will keep using the first,last,step and movablekeys flag which will obviously remain unchanged.
A client that supports this key-specs feature needs only to look at the key-specs array. If it finds an unrecognized spec, it must resort to using COMMAND GETKEYS if it wishes to get all key name arguments, but if all it needs is one key in order to know which cluster node to use, then maybe another spec (if the command has several) can supply that, and there's no need to use GETKEYS.
Each spec is an array of arguments, first one is the spec name, the second is an array of flags, and the third is an array containing details about the spec (specific meaning for each spec type)
The initial flags we support are "read" and "write" indicating if the keys that this key-spec finds are used for read or for write. clients should ignore any unfamiliar flags.
In order to easily find the positions of keys in a given array of args we introduce keys specs. There are two logical steps of key specs:
start_search: Given an array of args, indicate where we should start searching for keysfind_keys: Given the output of start_search and an array of args, indicate all possible indices of keys.start_search step specs
index: specify an argument index explicitlyindex: 0 based index (1 means the first command argument)keyword: specify a string to match inargv. We should start searching for keys just after the keyword appears.keyword: the string to search forstart_search: an index from which to start the keyword search (can be negative, which means to search from the end)Examples:
SEThas start_search of typeindexwith value1XREADhas start_search of typekeywordwith value[“STREAMS”,1]MIGRATEhas start_search of typekeywordwith value[“KEYS”,-2]find_keys step specs
range: specify[count, step, limit].lastkey: index of the last key. relative to the index returned from begin_search. -1 indicating till the last argument, -2 one before the laststep: how many args should we skip after finding a key, in order to find the next onelimit: if count is -1, we use limit to stop the search by a factor. 0 and 1 mean no limit. 2 means ½ of the remaining args, 3 means ⅓, and so on.[keynum_index, first_key_index, step].keynum_index: is relative to the return of thestart_searchspec.first_key_index: is relative tokeynum_index.step: how many args should we skip after finding a key, in order to find the next oneExamples:
SEThasrangeof[0,1,0]MSEThasrangeof[-1,2,0]XREADhasrangeof[-1,1,2]ZUNIONhasstart_searchof typeindexwith value1andfind_keysof typekeynumwith value[0,1,1]AI.DAGRUNhasstart_searchof typekeywordwith value[“LOAD“,1]andfind_keysof typekeynumwith value[0,1,1](see https://oss.redislabs.com/redisai/master/commands/#aidagrun)Note: this solution is not perfect as the module writers can come up with anything, but at least we will be able to find the key args of the vast majority of commands.
If one of the above specs can’t describe the key positions, the module writer can always fall back to the
getkeys-apioption.Some keys cannot be found easily (
KEYSinMIGRATE: Imagine the argument forAUTHis the string “KEYS” - we will start searching in the wrong index).The guarantee is that the specs may be incomplete (
incompletewill be specified in the spec to denote that) but we never report false information (assuming the command syntax is correct).For
MIGRATEwe start searching from the end -startfrom=-1- and if one of the keys is actually called "keys" we will report only a subset of all keys - hence theincompleteflag.Some
incompletespecs can be completely empty (i.e. UNKNOWN begin_search) which should tell the client that COMMAND GETKEYS (or any other way to get the keys) must be used (Example: ForSORTthere is no way to describe the STORE keyword spec, as the word "store" can appear anywhere in the command).We will expose these key specs in the
COMMANDcommand so that clients can learn, on startup, where the keys are for all commands instead of holding hardcoded tables or useCOMMAND GETKEYSin runtime.Comments:
"incomplete" specs:
the command we have issues with are MIGRATE, STRALGO, and SORT
for MIGRATE, because the token KEYS, if exists, must be the last token, we can search in reverse. it one of the keys is actually the string "keys" will return just a subset of the keys (hence, it's "incomplete")
for SORT and STRALGO we can use this heuristic (the keys can be anywhere in the command) and therefore we added a key spec that is both "incomplete" and of "unknown type"
if a client encounters an "incomplete" spec it means that it must find a different way (either COMMAND GETKEYS or have its own parser) to retrieve the keys.
please note that all commands, apart from the three mentioned above, have "complete" key specs