Summary
Inside MULTI, redis validates a container command's subcommand at
queue time: an unknown subcommand is refused with -ERR on the MULTI
connection immediately, and the whole transaction is then poisoned so EXEC
answers -EXECABORT. moon queues the bad subcommand with +QUEUED and only
discovers it during EXEC, so the transaction runs and the client gets a
one-element array whose single element is the error.
A client that treats +QUEUED as "this command is valid" — which is what redis
guarantees — will happily send the rest of a transaction that redis would have
refused wholesale, then apply the partial result.
Measured (moon 0.8.7 @ 4df5db1, shards=4, vs redis-server 8.6.1)
Raw socket, one connection, MULTI / <container> BOGUS / EXEC:
| container |
moon |
redis |
CONFIG BOGUS |
+OK +QUEUED *1 -ERR unknown subcommand 'BOGUS'. Try CONFIG GET, CONFIG SET, CONFIG REWRITE, CONFIG RESETSTAT. |
+OK -ERR unknown subcommand 'BOGUS'. Try CONFIG HELP. -EXECABORT Transaction discarded because of previous errors. |
CLIENT BOGUS |
+OK +QUEUED *1 -ERR unknown subcommand 'BOGUS' |
+OK -ERR …Try CLIENT HELP. -EXECABORT … |
OBJECT BOGUS |
+OK +QUEUED *1 -ERR unknown OBJECT subcommand |
+OK -ERR …Try OBJECT HELP. -EXECABORT … |
ACL BOGUS |
+OK +QUEUED *1 -ERR unknown subcommand 'BOGUS'. Try ACL HELP. |
+OK -ERR …Try ACL HELP. -EXECABORT … |
MEMORY BOGUS |
+OK +QUEUED *1 -ERR MEMORY subcommand 'BOGUS' not supported |
+OK -ERR …Try MEMORY HELP. -EXECABORT … |
MODULE BOGUS |
+OK +QUEUED *1 -ERR unknown subcommand 'BOGUS'. Try MODULE HELP. |
+OK -ERR …Try MODULE HELP. -EXECABORT … |
Six of six containers behave the same way in moon, so this is one systemic
gap in the MULTI queue gate, not per-command drift. MODULE above is the
container added in moon#636; it inherits the behaviour rather than causing it.
Two separable defects
- Queue-time validation — the
MULTI queue gate checks arity and command
existence but never consults SUBCOMMAND_META, so it cannot reject an
unknown subcommand or poison the transaction. This is the client-visible
one.
- Error text — four of the six error strings above are moon-specific
(unknown OBJECT subcommand, MEMORY subcommand 'BOGUS' not supported,
a bare unknown subcommand 'BOGUS', and CONFIG's enumerated variant).
redis's shape is uniformly ERR unknown subcommand '<as sent>'. Try <CONTAINER> HELP. — ACL and MODULE already match it. This is
independent of MULTI: the same texts appear outside a transaction.
Fixing (2) alone is cheap and safe. (1) needs the queue gate to look up
SUBCOMMAND_META[container] and to set the existing "transaction is dirty"
flag on a miss — the same flag an arity error already sets — so please check
the MULTI queue gate exemption list first: several families are documented
as running inline and never reaching the intercepts.
Repro
q() { { printf 'MULTI\r\n%s\r\nEXEC\r\n' "$1"; sleep 1; } | nc 127.0.0.1 "$2" | tr '\r\n' ' '; echo; }
q "MEMORY BOGUS" 6379 # redis: EXECABORT
q "MEMORY BOGUS" 7801 # moon: +QUEUED, then *1 with the error inside
Summary
Inside
MULTI, redis validates a container command's subcommand atqueue time: an unknown subcommand is refused with
-ERRon theMULTIconnection immediately, and the whole transaction is then poisoned so
EXECanswers
-EXECABORT. moon queues the bad subcommand with+QUEUEDand onlydiscovers it during
EXEC, so the transaction runs and the client gets aone-element array whose single element is the error.
A client that treats
+QUEUEDas "this command is valid" — which is what redisguarantees — will happily send the rest of a transaction that redis would have
refused wholesale, then apply the partial result.
Measured (moon 0.8.7 @ 4df5db1, shards=4, vs redis-server 8.6.1)
Raw socket, one connection,
MULTI/<container> BOGUS/EXEC:CONFIG BOGUS+OK+QUEUED*1-ERR unknown subcommand 'BOGUS'. Try CONFIG GET, CONFIG SET, CONFIG REWRITE, CONFIG RESETSTAT.+OK-ERR unknown subcommand 'BOGUS'. Try CONFIG HELP.-EXECABORT Transaction discarded because of previous errors.CLIENT BOGUS+OK+QUEUED*1-ERR unknown subcommand 'BOGUS'+OK-ERR …Try CLIENT HELP.-EXECABORT …OBJECT BOGUS+OK+QUEUED*1-ERR unknown OBJECT subcommand+OK-ERR …Try OBJECT HELP.-EXECABORT …ACL BOGUS+OK+QUEUED*1-ERR unknown subcommand 'BOGUS'. Try ACL HELP.+OK-ERR …Try ACL HELP.-EXECABORT …MEMORY BOGUS+OK+QUEUED*1-ERR MEMORY subcommand 'BOGUS' not supported+OK-ERR …Try MEMORY HELP.-EXECABORT …MODULE BOGUS+OK+QUEUED*1-ERR unknown subcommand 'BOGUS'. Try MODULE HELP.+OK-ERR …Try MODULE HELP.-EXECABORT …Six of six containers behave the same way in moon, so this is one systemic
gap in the
MULTIqueue gate, not per-command drift.MODULEabove is thecontainer added in moon#636; it inherits the behaviour rather than causing it.
Two separable defects
MULTIqueue gate checks arity and commandexistence but never consults
SUBCOMMAND_META, so it cannot reject anunknown subcommand or poison the transaction. This is the client-visible
one.
(
unknown OBJECT subcommand,MEMORY subcommand 'BOGUS' not supported,a bare
unknown subcommand 'BOGUS', and CONFIG's enumerated variant).redis's shape is uniformly
ERR unknown subcommand '<as sent>'. Try <CONTAINER> HELP.—ACLandMODULEalready match it. This isindependent of MULTI: the same texts appear outside a transaction.
Fixing (2) alone is cheap and safe. (1) needs the queue gate to look up
SUBCOMMAND_META[container]and to set the existing "transaction is dirty"flag on a miss — the same flag an arity error already sets — so please check
the
MULTIqueue gate exemption list first: several families are documentedas running inline and never reaching the intercepts.
Repro