-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Treat subcommands as commands #9504
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
The purpose is to allow having different flags/ACL categories for subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't) We create a small command table for every command that has subcommands and each subcommand has its own flags, etc. (same as a "regular" command) This commit also unites the Redis and the Sentinel command tables This commit adds a new command COMMANDS in order to preserve backward-compatibility of COMMAND and also in the intention of deprecating it.
|
modules: maybe we want something like RM_CreateContainerCommand + RM_CreateSubcommand ? |
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.
part one of the review.
reviewed everything except the command table itself.
|
I didn't go deep into this PR, the subcommand idea is good, but a problem that came to my mind is about module, how to let module to register subcommands? |
|
@soloestoy I guess we should introduce a new API |
|
TODO:
@oranagra please ack |
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.
Some comments
27b21f9 to
dcc19f6
Compare
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 think the ACL code is correct, but adding tests for various cases like -@all +config -config|get work would probably be good.
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, only skimmed through the code - but concept and API make sense and look great.
Improve code doc for allowed_firstargs (used to be allowed_commands before #9504. I don't think the text in the code needs to refer to the history (it's not there just for backwards compatibility). instead it should just describe what it does.
The following error commands will crash redis-server: ``` > get| Error: Server closed the connection > get|set Error: Server closed the connection > get|other ``` The reason is in redis#9504, we use `lookupCommandBySds` for find the container command. And it split the command (argv[0]) with `|`. If we input something like `get|other`, after the split, `get` will become a valid command name, pass the `ERR unknown command` check, and finally crash in `addReplySubcommandSyntaxError` In this case we do not need to split the command name with `|` and just look in the commands dict to find if `argv[0]` is a container command. So this commit introduce a new function call `isContainerCommandBySds` that it will return true if a command name is a container command. Also with the old code, there is a incorrect error message: ``` > config|get set (error) ERR Unknown subcommand or wrong number of arguments for 'set'. Try CONFIG|GET HELP. ``` The crash was reported in redis#10070.
The following error commands will crash redis-server: ``` > get| Error: Server closed the connection > get|set Error: Server closed the connection > get|other ``` The reason is in #9504, we use `lookupCommandBySds` for find the container command. And it split the command (argv[0]) with `|`. If we input something like `get|other`, after the split, `get` will become a valid command name, pass the `ERR unknown command` check, and finally crash in `addReplySubcommandSyntaxError` In this case we do not need to split the command name with `|` and just look in the commands dict to find if `argv[0]` is a container command. So this commit introduce a new function call `isContainerCommandBySds` that it will return true if a command name is a container command. Also with the old code, there is a incorrect error message: ``` > config|get set (error) ERR Unknown subcommand or wrong number of arguments for 'set'. Try CONFIG|GET HELP. ``` The crash was reported in #10070.
| parent->subcommands_dict = dictCreate(&commandTableDictType); | ||
|
|
||
| subcommand->parent = parent; /* Assign the parent command */ | ||
| sds fullname = getFullCommandName(subcommand); |
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.
can we just set the subcommand->name = fullname? (or add a fullname field....)
it seems to me now that the subcommand name are generally unusable, we must get the fullname to use it.
so i suppose if we can just set the name = fullname, when we need it, we can just use it...
like here, it looks like the subcommand won't get the full name (unfamiliar with modules)
Lines 10198 to 10204 in 56a8020
| /* Return the name of the command currently running */ | |
| const char *RM_GetCurrentCommandName(RedisModuleCtx *ctx) { | |
| if (!ctx || !ctx->client || !ctx->client->cmd) | |
| return NULL; | |
| return (const char*)ctx->client->cmd->name; | |
| } |
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.
interesting..
in theory changing that (past 7.0) could be a breaking change.
but currently (before 7.0 is released), modules can't register sub-commands.
@enjoy-binbin did you conduct some search to see who's still using the simple cmd->name (on something that could be a sub-command) to find that one?
if that's the only one, i tend to agree we wanna make name the full name by default and drop all the complication of getFullCommandName
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.
did you conduct some search to see who's still using the simple cmd->name (on something that could be a sub-command) to find that one?
actually no, i happen to see it (reviewing / studying some module codes.)
i also tend to use the full name by default, so that we don't have to deal with getFullCommandName.
i do some search, here are what i found:
-
latencyAllCommandsFillCDF, show sub-commands INFO latencystats and LATENCY HISTOGRAM #10103
-
afterErrorReply (we need to take care of it, it could be a subcommand i think):
const char *cmdname = c->lastcmd ? c->lastcmd->name : "<unknown>";
serverLog(LL_WARNING,"== CRITICAL == This %s is sending an error "
"to its %s: '%.*s' after processing the command "
"'%s'", from, to, (int)len, s, cmdname);
-
ACL CAT? maybe we need to also print the subcommand in future? (for now, it only works on parentcommand)
-
COMMAND LIST? same as above, only print the parent command, but others one, like command docs config|get, can work on subcommand
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 considering sub-commands are now first class citizens, i suppose we should list them in ACL CAT and COMMAND LIST, WDYT?
and IIRC from @enjoy-binbin findings, we might be better off just deleting getFullCommandName and put the full command name in cmd->name, which will also fix:
- RM_GetCurrentCommandName module API
- the
CRITICALlog warning inafterErrorReply - replies in
COMMAND LISTandACL CATonce we change them to include sub-commands.
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.
@enjoy-binbin Guy didn't respond yet, but i'm paranoid about forgetting this (discussion in a closed PR), and i think we must do that in 7.0 for completeness. wanna make a PR and we can continue the discussion there if needed?
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.
@oranagra @enjoy-binbin I agree. we should always store the fullname, and eliminate all uses getFullCommandName, except for the initial assignment of the name.
we should rename redisCommand.name to something like tmp_cmdname so that the compiler will make sure we didn't miss anything. after that, we can call it name again. another option is to change it to fullname permanently in order to cause conflicts in open PRs such as #10103
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 'd like go the fullname option.
at the same time we should also care about subcommands_dict, now the key is the subcommand short name.
we can treat name as the short name(or rename it in some function arg name), fullname as the full name
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 think that the command name is always the full name (also in the ubcommand_dict)
to clarify, the word "rewrite" from CONFIG REWRITE should never appear anywhere by itself, it should always be saved as "config|rewrite"
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.
suggestion: we need to make sure that all callers to commandAddSubcommand already pass the subcommand with the fullname
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.
@enjoy-binbin maybe while you're working on it, you can also handle #10124 and see if we missed anything.
… stores fullname (#10127) Summary of changes: 1. Rename `redisCommand->name` to `redisCommand->declared_name`, it is a const char * for native commands and SDS for module commands. 2. Store the [sub]command fullname in `redisCommand->fullname` (sds). 3. List subcommands in `ACL CAT` 4. List subcommands in `COMMAND LIST` 5. `moduleUnregisterCommands` now will also free the module subcommands. 6. RM_GetCurrentCommandName returns full command name Other changes: 1. Add `addReplyErrorArity` and `addReplyErrorExpireTime` 2. Remove `getFullCommandName` function that now is useless. 3. Some cleanups about `fullname` since now it is SDS. 4. Delete `populateSingleCommand` function from server.h that is useless. 5. Added tests to cover this change. 6. Add some module unload tests and fix the leaks 7. Make error messages uniform, make sure they always contain the full command name and that it's quoted. 7. Fixes some typos see the history in #9504, fixes #10124 Co-authored-by: Oran Agra <oran@redislabs.com> Co-authored-by: guybe7 <guy.benoish@redislabs.com>
Intro
The purpose is to allow having different flags/ACL categories for
subcommands (Example: CONFIG GET is ok-loading but CONFIG SET isn't)
We create a small command table for every command that has subcommands
and each subcommand has its own flags, etc. (same as a "regular" command)
This commit also unites the Redis and the Sentinel command tables
Affected commands
CONFIG
Used to have "admin ok-loading ok-stale no-script"
Changes:
XINFO
Used to have "read-only random"
Changes:
XGROUP
Used to have "write use-memory"
Changes:
COMMAND
No changes.
MEMORY
Used to have "random read-only"
Changes:
ACL
Used to have "admin no-script ok-loading ok-stale"
Changes:
LATENCY
No changes.
MODULE
No changes.
SLOWLOG
Used to have "admin random ok-loading ok-stale"
Changes:
OBJECT
Used to have "read-only random"
Changes:
SCRIPT
Used to have "may-replicate no-script"
Changes:
CLIENT
Used to have "admin no-script random ok-loading ok-stale"
Changes:
STRALGO
No changes.
PUBSUB
No changes.
CLUSTER
Changes:
SENTINEL
No changes.
(note that DEBUG also fits, but we decided not to convert it since it's for
debugging and anyway undocumented)
New sub-command
This commit adds another element to the per-command output of COMMAND,
describing the list of subcommands, if any (in the same structure as "regular" commands)
Also, it adds a new subcommand:
which returns a set of all commands (unless filters), but excluding subcommands.
Module API
A new module API, RM_CreateSubcommand, was added, in order to allow
module writer to define subcommands
ACL changes:
(blocking/allowing a subcommand is the same as blocking/allowing a regular command),
but we had to keep it, to support the widespread usage of allowed_subcommands
to block commands with certain args, that aren't subcommands (e.g. "-select +select|0").
(e.g. "+client -client|kill"), which wasn't possible in the past.
For example:
+config -config|set +config|set|loglevelwill block all CONFIG SET exceptfor setting the log level.
NOTE: this capability was later removed in Improved handling of subcommands (don't allow ACL on first-arg of a sub-command) #10147
Misc
subcommands use the same function, determining what to do according to argv[0].
For now, I took the former approaches only with CONFIG and COMMAND,
while other commands use the latter approach (for smaller blamelog diff).
Some commands have a different implementation in Sentinel, so we redirect
them (these are ROLE, PUBLISH, and INFO).
for "config" you will have stats for "config|set", "config|get", etc.)
COMMAND INFO CONFIG|GET (The pipeline syntax was inspired from ACL, and
can be used in functions lookupCommandBySds and lookupCommandByCString)
Breaking changes:
TODO