Skip to content

MULTI: unknown container subcommands are queued instead of aborting the transaction (6/6 containers) #670

Description

@TinDang97

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

  1. 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.
  2. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions