123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511typet=|Single_keyof{key_index:int;readonly:bool}|Multi_keyof{first_key_index:int;readonly:bool}|Keyless_randomof{readonly:bool}|Fan_primaries|Fan_all_nodesletreadonly=function|Single_key{readonly;_}|Multi_key{readonly;_}|Keyless_random{readonly}->readonly|Fan_primaries|Fan_all_nodes->false(* Helper constructors — keep the tables compact and the intent
obvious at a glance. Every Valkey command routes via one of these.
"wk<i>" = write, key at position i.
"rk<i>" = read, key at position i.
"mw" = multi-key write; caller co-locates via hashtag.
"mr" = multi-key read. *)letwki=Single_key{key_index=i;readonly=false}letrki=Single_key{key_index=i;readonly=true}letmwi=Multi_key{first_key_index=i;readonly=false}letmri=Multi_key{first_key_index=i;readonly=true}letrand_r=Keyless_random{readonly=true}letrand_w=Keyless_random{readonly=false}(* Two-word commands (subcommand in args.(1)). Looked up with names
already upper-cased. *)lettwo_word:(string*string*t)list=[(* SCRIPT / FUNCTION — scripts live on every primary *)"SCRIPT","LOAD",Fan_primaries;"SCRIPT","FLUSH",Fan_primaries;"SCRIPT","EXISTS",Fan_primaries;"FUNCTION","LOAD",Fan_primaries;"FUNCTION","DELETE",Fan_primaries;"FUNCTION","FLUSH",Fan_primaries;"FUNCTION","LIST",Fan_primaries;"FUNCTION","DUMP",Fan_primaries;"FUNCTION","STATS",Fan_primaries;"FUNCTION","RESTORE",Fan_primaries;(* CONFIG — global server configuration is per-node *)"CONFIG","SET",Fan_primaries;"CONFIG","RESETSTAT",Fan_primaries;"CONFIG","REWRITE",Fan_primaries;"CONFIG","GET",rand_r;(* CLIENT — most are per-connection, so any one node answers *)"CLIENT","LIST",Fan_all_nodes;"CLIENT","ID",rand_r;"CLIENT","GETNAME",rand_r;"CLIENT","INFO",rand_r;"CLIENT","GETREDIR",rand_r;"CLIENT","SETNAME",rand_w;"CLIENT","SETINFO",rand_w;"CLIENT","NO-EVICT",rand_w;"CLIENT","NO-TOUCH",rand_w;"CLIENT","TRACKING",rand_w;"CLIENT","TRACKINGINFO",rand_r;"CLIENT","CACHING",rand_w;"CLIENT","REPLY",rand_w;"CLIENT","PAUSE",Fan_primaries;"CLIENT","UNPAUSE",Fan_primaries;"CLIENT","KILL",rand_w;(* CLUSTER — cluster membership queries *)"CLUSTER","NODES",Fan_all_nodes;"CLUSTER","SHARDS",rand_r;"CLUSTER","INFO",rand_r;"CLUSTER","MYID",rand_r;"CLUSTER","MYSHARDID",rand_r;"CLUSTER","COUNTKEYSINSLOT",rand_r;"CLUSTER","KEYSLOT",rand_r;"CLUSTER","GETKEYSINSLOT",rand_r;"CLUSTER","COUNT-FAILURE-REPORTS",rand_r;"CLUSTER","LINKS",rand_r;"CLUSTER","SLAVES",rand_r;"CLUSTER","REPLICAS",rand_r;"CLUSTER","SLOTS",rand_r;"CLUSTER","MEET",rand_w;"CLUSTER","FORGET",rand_w;"CLUSTER","RESET",rand_w;"CLUSTER","ADDSLOTS",rand_w;"CLUSTER","DELSLOTS",rand_w;"CLUSTER","ADDSLOTSRANGE",rand_w;"CLUSTER","DELSLOTSRANGE",rand_w;"CLUSTER","FLUSHSLOTS",rand_w;"CLUSTER","SETSLOT",rand_w;"CLUSTER","BUMPEPOCH",rand_w;"CLUSTER","REPLICATE",rand_w;"CLUSTER","FAILOVER",rand_w;"CLUSTER","HELP",rand_r;(* XINFO / stream introspection *)"XINFO","STREAM",rk2;"XINFO","GROUPS",rk2;"XINFO","CONSUMERS",rk2;"XINFO","HELP",rand_r;(* OBJECT — each variant takes a key *)"OBJECT","ENCODING",rk2;"OBJECT","REFCOUNT",rk2;"OBJECT","IDLETIME",rk2;"OBJECT","FREQ",rk2;"OBJECT","HELP",rand_r;(* DEBUG (mostly disabled on production Valkey 8+) *)"DEBUG","OBJECT",rk2;"DEBUG","SLEEP",rand_w;"DEBUG","JMAP",rand_r;"DEBUG","SET-ACTIVE-EXPIRE",rand_w;(* MEMORY — USAGE takes a key; others are server-local *)"MEMORY","USAGE",rk2;"MEMORY","STATS",rand_r;"MEMORY","DOCTOR",rand_r;"MEMORY","HELP",rand_r;"MEMORY","PURGE",rand_w;"MEMORY","MALLOC-STATS",rand_r;(* LATENCY *)"LATENCY","LATEST",rand_r;"LATENCY","HISTORY",rand_r;"LATENCY","RESET",rand_w;"LATENCY","GRAPH",rand_r;"LATENCY","DOCTOR",rand_r;"LATENCY","HELP",rand_r;"LATENCY","HISTOGRAM",rand_r;(* SLOWLOG *)"SLOWLOG","GET",rand_r;"SLOWLOG","LEN",rand_r;"SLOWLOG","RESET",rand_w;"SLOWLOG","HELP",rand_r;(* ACL — global user management, propagate everywhere *)"ACL","LIST",Fan_primaries;"ACL","USERS",Fan_primaries;"ACL","WHOAMI",rand_r;"ACL","GETUSER",Fan_primaries;"ACL","SETUSER",Fan_primaries;"ACL","DELUSER",Fan_primaries;"ACL","CAT",rand_r;"ACL","GENPASS",rand_r;"ACL","LOG",rand_r;"ACL","LOAD",Fan_primaries;"ACL","SAVE",Fan_primaries;"ACL","HELP",rand_r;(* PUBSUB introspection (not SUBSCRIBE — that is connection-bound) *)"PUBSUB","CHANNELS",Fan_all_nodes;"PUBSUB","NUMSUB",Fan_all_nodes;"PUBSUB","NUMPAT",Fan_all_nodes;"PUBSUB","SHARDCHANNELS",Fan_all_nodes;"PUBSUB","SHARDNUMSUB",Fan_all_nodes;"PUBSUB","HELP",rand_r;(* MODULE *)"MODULE","LOAD",Fan_primaries;"MODULE","LOADEX",Fan_primaries;"MODULE","UNLOAD",Fan_primaries;"MODULE","LIST",Fan_all_nodes;(* COMMAND introspection *)"COMMAND","COUNT",rand_r;"COMMAND","DOCS",rand_r;"COMMAND","INFO",rand_r;"COMMAND","LIST",rand_r;"COMMAND","GETKEYS",rand_r;"COMMAND","GETKEYSANDFLAGS",rand_r;](* Single-word commands. Alphabetical within groups. *)letone_word:(string*t)list=[(* ---------- strings / counters ---------- *)"APPEND",wk1;"DECR",wk1;"DECRBY",wk1;"GET",rk1;"GETDEL",wk1;"GETEX",wk1;"GETRANGE",rk1;"GETSET",wk1;"INCR",wk1;"INCRBY",wk1;"INCRBYFLOAT",wk1;"PSETEX",wk1;"SET",wk1;"SETEX",wk1;"SETNX",wk1;"SETRANGE",wk1;"STRLEN",rk1;"SUBSTR",rk1;(* ---------- generic keyspace ---------- *)"COPY",mw1;"DEL",mw1;"DELIFEQ",wk1;"DUMP",rk1;"EXISTS",mr1;"EXPIRE",wk1;"EXPIREAT",wk1;"EXPIRETIME",rk1;"LCS",mr1;"MGET",mr1;"MSET",mw1;"MSETNX",mw1;"PERSIST",wk1;"PEXPIRE",wk1;"PEXPIREAT",wk1;"PEXPIRETIME",rk1;"PTTL",rk1;"RANDOMKEY",rand_r;"RENAME",mw1;"RENAMENX",mw1;"RESTORE",wk1;"SORT",wk1;(* SORT has STORE form - writes *)"SORT_RO",rk1;"TOUCH",mr1;"TTL",rk1;"TYPE",rk1;"UNLINK",mw1;(* ---------- hashes ---------- *)"HDEL",wk1;"HEXISTS",rk1;"HEXPIRE",wk1;"HEXPIREAT",wk1;"HEXPIRETIME",rk1;"HGET",rk1;"HGETALL",rk1;"HGETDEL",wk1;"HGETEX",wk1;"HINCRBY",wk1;"HINCRBYFLOAT",wk1;"HKEYS",rk1;"HLEN",rk1;"HMGET",rk1;"HMSET",wk1;"HPERSIST",wk1;"HPEXPIRE",wk1;"HPEXPIREAT",wk1;"HPEXPIRETIME",rk1;"HPTTL",rk1;"HRANDFIELD",rk1;"HSCAN",rk1;"HSET",wk1;"HSETEX",wk1;"HSETNX",wk1;"HSTRLEN",rk1;"HTTL",rk1;"HVALS",rk1;(* ---------- lists ---------- *)"BLMOVE",mw1;"BLMPOP",Keyless_random{readonly=false};(* keys after count *)"BLPOP",mw1;"BRPOP",mw1;"BRPOPLPUSH",mw1;"LINDEX",rk1;"LINSERT",wk1;"LLEN",rk1;"LMOVE",mw1;"LMPOP",Keyless_random{readonly=false};"LPOP",wk1;"LPOS",rk1;"LPUSH",wk1;"LPUSHX",wk1;"LRANGE",rk1;"LREM",wk1;"LSET",wk1;"LTRIM",wk1;"RPOP",wk1;"RPOPLPUSH",mw1;"RPUSH",wk1;"RPUSHX",wk1;(* ---------- sets ---------- *)"SADD",wk1;"SCARD",rk1;"SDIFF",mr1;"SDIFFSTORE",mw1;"SINTER",mr1;"SINTERCARD",Keyless_random{readonly=true};(* keys after count *)"SINTERSTORE",mw1;"SISMEMBER",rk1;"SMEMBERS",rk1;"SMISMEMBER",rk1;"SMOVE",mw1;"SPOP",wk1;"SRANDMEMBER",rk1;"SREM",wk1;"SSCAN",rk1;"SUNION",mr1;"SUNIONSTORE",mw1;(* ---------- sorted sets ---------- *)"BZMPOP",Keyless_random{readonly=false};"BZPOPMAX",mw1;"BZPOPMIN",mw1;"ZADD",wk1;"ZCARD",rk1;"ZCOUNT",rk1;"ZDIFF",Keyless_random{readonly=true};(* keys after count *)"ZDIFFSTORE",mw1;"ZINCRBY",wk1;"ZINTER",Keyless_random{readonly=true};"ZINTERCARD",Keyless_random{readonly=true};"ZINTERSTORE",mw1;"ZLEXCOUNT",rk1;"ZMPOP",Keyless_random{readonly=false};"ZMSCORE",rk1;"ZPOPMAX",wk1;"ZPOPMIN",wk1;"ZRANDMEMBER",rk1;"ZRANGE",rk1;"ZRANGEBYLEX",rk1;"ZRANGEBYSCORE",rk1;"ZRANGESTORE",mw1;"ZRANK",rk1;"ZREM",wk1;"ZREMRANGEBYLEX",wk1;"ZREMRANGEBYRANK",wk1;"ZREMRANGEBYSCORE",wk1;"ZREVRANGE",rk1;"ZREVRANGEBYLEX",rk1;"ZREVRANGEBYSCORE",rk1;"ZREVRANK",rk1;"ZSCAN",rk1;"ZSCORE",rk1;"ZUNION",Keyless_random{readonly=true};"ZUNIONSTORE",mw1;(* ---------- streams ---------- *)"XACK",wk1;"XADD",wk1;"XAUTOCLAIM",wk1;"XCLAIM",wk1;"XDEL",wk1;"XGROUP",wk2;"XLEN",rk1;"XPENDING",rk1;"XRANGE",rk1;(* XREAD / XREADGROUP: keys live after STREAMS keyword — the
typed wrapper computes the slot from the first stream key and
passes target explicitly. Default Keyless_random is safe here
because a raw caller should also pass target. *)"XREAD",rand_r;"XREADGROUP",rand_w;"XREVRANGE",rk1;"XSETID",wk1;"XTRIM",wk1;(* ---------- bitmaps ---------- *)"BITCOUNT",rk1;"BITFIELD",wk1;"BITFIELD_RO",rk1;"BITOP",wk2;(* BITOP OP dest src1 src2 … — dest at 2 *)"BITPOS",rk1;"GETBIT",rk1;"SETBIT",wk1;(* ---------- HyperLogLog ---------- *)"PFADD",wk1;"PFCOUNT",mr1;"PFMERGE",mw1;(* ---------- geo ---------- *)"GEOADD",wk1;"GEODIST",rk1;"GEOHASH",rk1;"GEOPOS",rk1;"GEORADIUS",wk1;"GEORADIUS_RO",rk1;"GEORADIUSBYMEMBER",wk1;"GEORADIUSBYMEMBER_RO",rk1;"GEOSEARCH",rk1;"GEOSEARCHSTORE",mw1;(* dest src … *)(* ---------- scripting ---------- *)"EVAL",Single_key{key_index=3;readonly=false};"EVALSHA",Single_key{key_index=3;readonly=false};"EVAL_RO",Single_key{key_index=3;readonly=true};"EVALSHA_RO",Single_key{key_index=3;readonly=true};"FCALL",Single_key{key_index=3;readonly=false};"FCALL_RO",Single_key{key_index=3;readonly=true};(* ---------- transactions (connection-scoped) ---------- *)"MULTI",rand_w;"EXEC",rand_w;"DISCARD",rand_w;"WATCH",mr1;(* slot of first key; all keys same slot *)"UNWATCH",rand_w;(* ---------- pub/sub (connection-scoped subscribe channels) ---------- *)"SUBSCRIBE",rand_w;"UNSUBSCRIBE",rand_w;"PSUBSCRIBE",rand_w;"PUNSUBSCRIBE",rand_w;"PUBLISH",rand_w;(* cluster-wide broadcast on any node *)"SPUBLISH",rand_w;(* router will compute slot from channel *)"SSUBSCRIBE",rand_w;"SUNSUBSCRIBE",rand_w;(* ---------- iteration / admin ---------- *)"SCAN",rand_r;"KEYS",Fan_all_nodes;(* ---------- server / info ---------- *)"BGREWRITEAOF",Fan_primaries;"BGSAVE",Fan_primaries;"COMMAND",rand_r;"DBSIZE",Fan_primaries;"DEBUG",rand_w;"ECHO",rand_r;"FAILOVER",rand_w;"FLUSHALL",Fan_primaries;"FLUSHDB",Fan_primaries;"INFO",rand_r;"LASTSAVE",rand_r;"LATENCY",rand_r;"LOLWUT",rand_r;"MEMORY",rand_r;"PING",rand_r;"REPLICAOF",rand_w;"RESET",rand_w;"ROLE",rand_r;"SAVE",Fan_primaries;"SHUTDOWN",Fan_primaries;"SLAVEOF",rand_w;"SLOWLOG",rand_r;"SWAPDB",rand_w;"TIME",rand_r;"WAIT",Fan_primaries;"WAITAOF",Fan_primaries;(* ---------- connection ---------- *)"AUTH",rand_w;"HELLO",rand_w;"QUIT",rand_w;"SELECT",rand_w;]letuppers=String.uppercase_asciisletone_word_tbl=leth=Hashtbl.create(List.lengthone_word)inList.iter(fun(k,v)->Hashtbl.replacehkv)one_word;hlettwo_word_tbl=leth=Hashtbl.create(List.lengthtwo_word)inList.iter(fun(a,b,v)->Hashtbl.replaceh(a,b)v)two_word;hletdefault=Keyless_random{readonly=false}letlookupargs=ifArray.lengthargs=0thendefaultelseletcmd=upperargs.(0)inifArray.lengthargs>=2thenletsub=upperargs.(1)inmatchHashtbl.find_opttwo_word_tbl(cmd,sub)with|Somev->v|None->(matchHashtbl.find_optone_word_tblcmdwith|Somev->v|None->default)elsematchHashtbl.find_optone_word_tblcmdwith|Somev->v|None->defaultlettarget_and_rfrfargs=matchlookupargswith|Single_key{key_index;readonly}->ifkey_index>=Array.lengthargsthenSome(Router.Target.Random,rf)elseleteffective_rf=ifreadonlythenrfelseRouter.Read_from.PrimaryinSome(Router.Target.By_slot(Slot.of_keyargs.(key_index)),effective_rf)|Multi_key{first_key_index;readonly}->iffirst_key_index>=Array.lengthargsthenSome(Router.Target.Random,rf)elseleteffective_rf=ifreadonlythenrfelseRouter.Read_from.PrimaryinSome(Router.Target.By_slot(Slot.of_keyargs.(first_key_index)),effective_rf)|Keyless_random{readonly}->leteffective_rf=ifreadonlythenrfelseRouter.Read_from.PrimaryinSome(Router.Target.Random,effective_rf)|Fan_primaries|Fan_all_nodes->None(* Table size exposed for visibility/debug. *)letcommand_count()=Hashtbl.lengthone_word_tbl+Hashtbl.lengthtwo_word_tblletone_word_commands()=Hashtbl.fold(funname_acc->name::acc)one_word_tbl[]|>List.sortcompare