FerricStore implements 250+ commands covering standard Redis data types, probabilistic structures, and FerricStore-native operations. This guide documents each command with its exact syntax, return values, embedded API equivalent, and Redis compatibility notes.

Redis Compatibility Summary

Fully Compatible Commands

These commands match Redis 7.4 behavior exactly -- same arguments, same return values, same error messages:

GET, SET (EX/PX/EXAT/PXAT/NX/XX/GET/KEEPTTL), DEL, EXISTS, MGET, MSET, MSETNX, INCR, DECR, INCRBY, DECRBY, INCRBYFLOAT, APPEND, STRLEN, GETSET, GETDEL, GETEX, SETNX, SETEX, PSETEX, GETRANGE, SETRANGE, HSET, HGET, HDEL, HMGET, HGETALL, HEXISTS, HKEYS, HVALS, HLEN, HINCRBY, HINCRBYFLOAT, HSETNX, HSTRLEN, HRANDFIELD, HSCAN, HEXPIRE, HTTL, HPERSIST, HPEXPIRE, HPTTL, HEXPIRETIME, HGETDEL, HGETEX, HSETEX, LPUSH, RPUSH, LPOP, RPOP, LRANGE, LLEN, LINDEX, LSET, LREM, LTRIM, LPOS, LINSERT, LMOVE, RPOPLPUSH, LPUSHX, RPUSHX, SADD, SREM, SMEMBERS, SISMEMBER, SMISMEMBER, SCARD, SRANDMEMBER, SPOP, SDIFF, SINTER, SUNION, SDIFFSTORE, SINTERSTORE, SUNIONSTORE, SINTERCARD, SMOVE, SSCAN, ZADD (NX/XX/GT/LT/CH), ZSCORE, ZRANK, ZREVRANK, ZRANGE, ZREVRANGE, ZCARD, ZREM, ZINCRBY, ZCOUNT, ZPOPMIN, ZPOPMAX, ZRANDMEMBER, ZMSCORE, ZRANGEBYSCORE, ZREVRANGEBYSCORE, ZSCAN, XADD, XLEN, XRANGE, XREVRANGE, XREAD (including BLOCK), XTRIM, XDEL, XINFO STREAM, XGROUP CREATE, XREADGROUP, XACK, EXPIRE, PEXPIRE, EXPIREAT, PEXPIREAT, TTL, PTTL, PERSIST, EXPIRETIME, PEXPIRETIME, SETBIT, GETBIT, BITCOUNT, BITPOS, BITOP, PFADD, PFCOUNT, PFMERGE, GEOADD, GEOPOS, GEODIST, GEOHASH, GEOSEARCH, GEOSEARCHSTORE, PING, ECHO, DBSIZE, KEYS, FLUSHDB, FLUSHALL, INFO, TYPE, UNLINK, RENAME, RENAMENX, COPY, RANDOMKEY, OBJECT HELP/REFCOUNT, SCAN, CONFIG GET/SET/RESETSTAT/REWRITE, SLOWLOG GET/LEN/RESET, COMMAND/COMMAND COUNT/COMMAND LIST/COMMAND INFO/COMMAND DOCS/COMMAND GETKEYS, MULTI, EXEC, DISCARD, WATCH, UNWATCH, SUBSCRIBE, UNSUBSCRIBE, PSUBSCRIBE, PUNSUBSCRIBE, PUBLISH, CLIENT ID/SETNAME/GETNAME/INFO/LIST/TRACKING/CACHING/TRACKINGINFO/GETREDIR, HELLO, AUTH, QUIT, RESET

Commands with Minor Differences

CommandDifference
ZRANGERedis 6.2+ unified syntax (BYSCORE/BYLEX/REV/LIMIT) not yet supported -- use ZRANGEBYSCORE/ZREVRANGEBYSCORE instead
SCANCursor is key-based (alphabetic position), not an opaque integer. Functionally equivalent but cursor values differ from Redis
HSCAN/SSCAN/ZSCANCursor is an integer offset into the scanned list. Redis uses a hash-table-based cursor; results are equivalent
FLUSHDB/FLUSHALLASYNC/SYNC accepted but both execute synchronously; true async reclaim happens during Bitcask merge
UNLINKSemantically identical to DEL -- async reclaim is deferred to Bitcask merge
OBJECT ENCODINGReturns type-specific encoding ("embstr", "raw", "hashtable", "quicklist", "skiplist", "stream") instead of Redis's internal encodings like ziplist/listpack/intset
OBJECT FREQReturns the LFU counter from keydir, not Redis's logarithmic frequency
OBJECT IDLETIMEReturns idle seconds derived from LFU last-decrement-time, not Redis's LRU clock
SELECTReturns error -- FerricStore is single-database
INFOReturns FerricStore-specific sections (raft, bitcask, ferricstore, keydir_analysis, namespace_config) in addition to Redis standard sections
WAITAlways returns 0 immediately (no replica acknowledgement)
BLPOP/BRPOP/BLMOVE/BLMPOPSupported in TCP mode only, not in embedded mode
XREAD BLOCKSupported in TCP mode via stream waiters; not available in embedded mode

FerricStore-Only Commands

These have no Redis equivalent:

CAS, LOCK, UNLOCK, EXTEND, RATELIMIT.ADD, FETCH_OR_COMPUTE, FETCH_OR_COMPUTE_RESULT, FETCH_OR_COMPUTE_ERROR, KEY_INFO, FERRICSTORE.CONFIG, FERRICSTORE.METRICS, FERRICSTORE.HOTNESS, FERRICSTORE.KEY_INFO, CLUSTER.HEALTH, CLUSTER.STATS, CLUSTER.KEYSLOT, CLUSTER.SLOTS

Redis Commands NOT Yet Supported

EVAL, EVALSHA, EVALSHA_RO, EVAL_RO (Lua scripting), LMPOP, ZMPOP, BZMPOP (multi-key pop), ZUNIONSTORE, ZINTERSTORE, ZDIFFSTORE (sorted set store operations), ZRANGESTORE, ZRANGEBYLEX, ZREVRANGEBYLEX, ZLEXCOUNT, SORT, SORT_RO, OBJECT extended subcommands (OBJECT PERSIST, OBJECT COPY), CLUSTER (full Redis Cluster protocol), DUMP, RESTORE, MIGRATE, MOVE, CLIENT KILL, CLIENT NO-EVICT, CLIENT PAUSE, CLIENT UNPAUSE, DEBUG (most subcommands)


String Commands

String commands operate on simple key-value pairs. Values are stored as raw byte strings in Bitcask. All writes go through Raft group-commit.

GET

Retrieves the value of a key. Returns a WRONGTYPE error if the key holds a non-string data structure (hash, list, set, zset). FerricStore detects data structure types by peeking at ETF header bytes without deserializing the entire value.

RESP3 syntaxGET key
Embedded APIFerricStore.get(key)
RESP3 returnBulk string, or _ (null) if key does not exist
Elixir return{:ok, binary()} or {:ok, nil}
Redis compatFully compatible

SET

Sets a string value with optional expiry and conditional flags.

RESP3 syntaxSET key value [EX seconds | PX milliseconds | EXAT unix-sec | PXAT unix-ms] [NX|XX] [GET] [KEEPTTL]
Embedded APIFerricStore.set(key, value, ttl: ms)
RESP3 return+OK on success, _ (null) when NX/XX condition fails. With GET: returns old value or null.
Elixir return:ok on success, {:ok, nil} when condition fails

Options:

  • EX seconds -- set expiry in seconds (must be > 0)
  • PX milliseconds -- set expiry in milliseconds (must be > 0)
  • EXAT unix-sec -- set absolute expiry as Unix timestamp in seconds
  • PXAT unix-ms -- set absolute expiry as Unix timestamp in milliseconds
  • NX -- only set if key does not exist
  • XX -- only set if key already exists
  • GET -- return the old value stored at key (or null if key didn't exist)
  • KEEPTTL -- retain the existing TTL on the key (cannot combine with EX/PX/EXAT/PXAT)

Redis compat: Fully compatible -- all SET options supported.

FerricStore behavior: Expiry is stored as an absolute HLC timestamp (expire_at_ms). Writes go through Raft group-commit -- the ETS keydir is updated immediately (sub-microsecond read visibility) while Bitcask persistence is batched.

DEL

Deletes one or more keys. Handles both plain string keys and compound data structure keys (hash, list, set, zset) by cleaning up all sub-keys and type metadata.

RESP3 syntaxDEL key [key ...]
Embedded APIFerricStore.del(key)
RESP3 returnInteger -- number of keys deleted
Elixir return:ok
Redis compatFully compatible

EXISTS

Returns the count of keys that exist. Checks both plain keys and compound data structure type metadata.

RESP3 syntaxEXISTS key [key ...]
Embedded APIFerricStore.exists(key)
RESP3 returnInteger -- count of existing keys (a key is counted once for each time it appears in the argument list)
Elixir returntrue or false (single key)
Redis compatFully compatible

MGET

Returns values for multiple keys. Returns nil for keys that do not exist.

RESP3 syntaxMGET key [key ...]
Embedded APIFerricStore.mget(keys)
RESP3 returnArray of bulk strings / nulls
Elixir return{:ok, [binary() | nil]}
Redis compatFully compatible

MSET

Sets multiple key-value pairs atomically. Never fails (always overwrites).

RESP3 syntaxMSET key value [key value ...]
Embedded APIFerricStore.mset(map)
RESP3 return+OK
Elixir return:ok
Redis compatFully compatible

Validation: Rejects empty keys and keys larger than 65,535 bytes.

MSETNX

Sets multiple keys only if NONE of the keys exist. Returns 0 if any key already exists (none are set).

RESP3 syntaxMSETNX key value [key value ...]
Embedded APIFerricStore.msetnx(map)
RESP3 returnInteger -- 1 (all set) or 0 (none set)
Elixir return{:ok, true} or {:ok, false}
Redis compatFully compatible

INCR / DECR / INCRBY / DECRBY

Atomically increment or decrement integer values. If the key does not exist, it is initialized to 0 before the operation.

RESP3 syntaxINCR key, DECR key, INCRBY key increment, DECRBY key decrement
Embedded APIFerricStore.incr(key), FerricStore.decr(key), FerricStore.incr_by(key, n), FerricStore.decr_by(key, n)
RESP3 returnInteger -- the new value
Elixir return{:ok, integer()}
Redis compatFully compatible

Error: Returns ERR value is not an integer or out of range if the value is not a valid integer.

INCRBYFLOAT

Atomically increment a value by a floating point amount. If the key does not exist, it is initialized to 0.0. Rejects inf and NaN.

RESP3 syntaxINCRBYFLOAT key increment
Embedded APIFerricStore.incr_by_float(key, delta)
RESP3 returnBulk string -- the new value as a string
Elixir return{:ok, binary()}
Redis compatFully compatible

APPEND

Appends a value to an existing string. If the key does not exist, it is created with the given value.

RESP3 syntaxAPPEND key value
Embedded APIFerricStore.append(key, value)
RESP3 returnInteger -- the new length in bytes
Elixir return{:ok, integer()}
Redis compatFully compatible

STRLEN

Returns the byte length of the string stored at key. Returns 0 if the key does not exist.

RESP3 syntaxSTRLEN key
Embedded APIFerricStore.strlen(key)
RESP3 returnInteger
Elixir return{:ok, integer()}
Redis compatFully compatible

GETSET

Atomically sets a key and returns the old value. Deprecated in Redis 6.2+ (use SET ... GET), but still supported.

RESP3 syntaxGETSET key value
Embedded APIFerricStore.getset(key, value)
RESP3 returnBulk string (old value) or null
Elixir return{:ok, binary() | nil}
Redis compatFully compatible

GETDEL

Atomically gets and deletes a key.

RESP3 syntaxGETDEL key
Embedded APIFerricStore.getdel(key)
RESP3 returnBulk string or null
Elixir return{:ok, binary() | nil}
Redis compatFully compatible

GETEX

Gets a key and optionally updates its TTL.

RESP3 syntaxGETEX key [EX seconds | PX ms | EXAT ts | PXAT ms_ts | PERSIST]
Embedded APIFerricStore.getex(key, ttl: ms)
RESP3 returnBulk string or null
Elixir return{:ok, binary() | nil}
Redis compatFully compatible -- all five TTL options supported

SETNX

Sets a key only if it does not already exist.

RESP3 syntaxSETNX key value
Embedded APIFerricStore.setnx(key, value)
RESP3 returnInteger -- 1 (set) or 0 (not set)
Elixir return{:ok, true} or {:ok, false}
Redis compatFully compatible

SETEX / PSETEX

Sets a key with an expiry.

RESP3 syntaxSETEX key seconds value, PSETEX key milliseconds value
Embedded APIFerricStore.setex(key, seconds, value), FerricStore.psetex(key, ms, value)
RESP3 return+OK
Elixir return:ok
Redis compatFully compatible. TTL must be > 0.

GETRANGE

Returns a substring of the string value by byte range. Supports negative indices (from end).

RESP3 syntaxGETRANGE key start end
Embedded APIFerricStore.getrange(key, start, stop)
RESP3 returnBulk string (empty if key missing or range invalid)
Elixir return{:ok, binary()}
Redis compatFully compatible

SETRANGE

Overwrites part of a string starting at the given byte offset. If the key does not exist, creates a zero-padded string.

RESP3 syntaxSETRANGE key offset value
Embedded APIFerricStore.setrange(key, offset, value)
RESP3 returnInteger -- the new string length
Elixir return{:ok, integer()}
Redis compatFully compatible

Hash Commands

Each hash field is stored as an individual compound key in the shared shard Bitcask: H:redis_key\0field_name -> value. This allows individual field access without reading the entire hash. Type metadata is maintained by TypeRegistry -- using a hash command on a key that holds a different type returns WRONGTYPE.

HSET

Sets one or more field-value pairs. Returns the number of NEW fields added (not updated).

RESP3 syntaxHSET key field value [field value ...]
Embedded APIFerricStore.hset(key, map)
RESP3 returnInteger -- count of new fields added
Elixir return:ok
Redis compatFully compatible

HGET

Returns the value of a single field.

RESP3 syntaxHGET key field
Embedded APIFerricStore.hget(key, field)
RESP3 returnBulk string or null
Elixir return{:ok, binary() | nil}
Redis compatFully compatible

HDEL

Deletes one or more fields. Cleans up type metadata if the hash becomes empty.

RESP3 syntaxHDEL key field [field ...]
Embedded APIFerricStore.hdel(key, fields)
RESP3 returnInteger -- count of fields deleted
Elixir return{:ok, integer()}
Redis compatFully compatible

HMGET

Returns values for multiple fields. Missing fields return null.

RESP3 syntaxHMGET key field [field ...]
Embedded APIFerricStore.hmget(key, fields)
RESP3 returnArray of bulk strings / nulls
Elixir return{:ok, [binary() | nil]}
Redis compatFully compatible

HGETALL

Returns all fields and values as a flat list: [field1, value1, field2, value2, ...].

RESP3 syntaxHGETALL key
Embedded APIFerricStore.hgetall(key)
RESP3 returnArray (flat interleaved) or Map in RESP3
Elixir return{:ok, map()}
Redis compatFully compatible

HEXISTS / HLEN / HKEYS / HVALS

CommandSyntaxReturn
HEXISTSHEXISTS key field1 if exists, 0 if not
HLENHLEN keyInteger -- field count
HKEYSHKEYS keyArray of field names
HVALSHVALS keyArray of values

All return empty results (0, []) for non-existent keys. Redis-compatible.

HINCRBY / HINCRBYFLOAT

Atomically increment hash field values. If the field does not exist, it is initialized to 0.

RESP3 syntaxHINCRBY key field increment, HINCRBYFLOAT key field increment
Embedded APIFerricStore.hincrby(key, field, n), FerricStore.hincrbyfloat(key, field, delta)
RESP3 returnInteger (HINCRBY) or bulk string (HINCRBYFLOAT)
Redis compatFully compatible

HSETNX

Sets a field only if it does not exist.

RESP3 syntaxHSETNX key field value
RESP3 return1 (set) or 0 (not set)
Redis compatFully compatible

HSTRLEN

Returns the string length of a hash field value. Returns 0 for missing fields.

RESP3 syntaxHSTRLEN key field
RESP3 returnInteger
Redis compatFully compatible

HRANDFIELD

Returns random field(s). Negative count allows duplicates.

RESP3 syntaxHRANDFIELD key [count [WITHVALUES]]
RESP3 returnBulk string (single), array (multiple)
Redis compatFully compatible. Negative count behavior (repeats allowed) matches Redis.

HSCAN

Cursor-based iteration over hash fields with optional pattern matching.

RESP3 syntaxHSCAN key cursor [MATCH pattern] [COUNT count]
RESP3 return[next_cursor, [field, value, ...]]
Redis compatCursor is an integer offset, not a Redis-style hash-table cursor. Results are equivalent. Default COUNT is 10.

Hash Field TTL (Redis 7.4+)

FerricStore supports per-field expiry on hash fields:

CommandSyntaxReturn
HEXPIREHEXPIRE key seconds FIELDS count field [field ...]List of 1 (set) / -2 (field missing)
HTTLHTTL key FIELDS count field [field ...]List of TTL seconds / -1 (no expiry) / -2 (missing)
HPERSISTHPERSIST key FIELDS count field [field ...]List of 1 (removed) / -1 (no expiry) / -2 (missing)
HPEXPIREHPEXPIRE key ms FIELDS count field [field ...]Same as HEXPIRE but milliseconds
HPTTLHPTTL key FIELDS count field [field ...]Same as HTTL but milliseconds
HEXPIRETIMEHEXPIRETIME key FIELDS count field [field ...]Absolute Unix timestamp (seconds)
HGETDELHGETDEL key FIELDS count field [field ...]List of values (nil for missing)
HGETEXHGETEX key [EX sec|PX ms|EXAT ts|PXAT ms|PERSIST] FIELDS count field [...]List of values
HSETEXHSETEX key seconds field value [field value ...]Count of new fields

Redis compat: These follow the Redis 7.4+ hash field expiry syntax.


List Commands

Lists are stored via ListOps using compound keys. Each element is individually addressable. Push operations notify any blocking waiters (BLPOP/BRPOP).

LPUSH / RPUSH

Push one or more elements to the head or tail. Returns the new list length.

RESP3 syntaxLPUSH key element [element ...], RPUSH key element [element ...]
Embedded APIFerricStore.lpush(key, elements), FerricStore.rpush(key, elements)
RESP3 returnInteger -- new length
Elixir return{:ok, integer()}
Redis compatFully compatible

LPOP / RPOP

Pop one or more elements from head or tail.

RESP3 syntaxLPOP key [count], RPOP key [count]
Embedded APIFerricStore.lpop(key), FerricStore.rpop(key)
RESP3 returnBulk string (single pop), Array (counted pop), null (empty/missing)
Elixir return{:ok, binary() | nil}
Redis compatFully compatible. Count=0 returns empty list if key exists, nil if not.

LRANGE

Returns elements in the specified range. Supports negative indices.

RESP3 syntaxLRANGE key start stop
Embedded APIFerricStore.lrange(key, start, stop)
RESP3 returnArray of bulk strings
Elixir return{:ok, [binary()]}
Redis compatFully compatible

LLEN / LINDEX / LSET / LREM / LTRIM / LPOS / LINSERT

CommandSyntaxReturnNotes
LLENLLEN keyIntegerRedis-compatible
LINDEXLINDEX key indexBulk string / nullSupports negative indices
LSETLSET key index element+OK or errorRedis-compatible
LREMLREM key count elementInteger (removed count)count>0: head-to-tail, count<0: tail-to-head, count=0: all
LTRIMLTRIM key start stop+OKRedis-compatible
LPOSLPOS key element [RANK r] [COUNT c] [MAXLEN m]Integer / Array / nullRANK 0 is invalid
LINSERTLINSERT key BEFORE|AFTER pivot elementInteger (new length) / -1 (pivot not found)Redis-compatible

LMOVE / RPOPLPUSH

Atomically pops from one list and pushes to another.

RESP3 syntaxLMOVE source destination LEFT|RIGHT LEFT|RIGHT
Embedded APIFerricStore.lmove(src, dst, from, to)
Redis compatFully compatible. RPOPLPUSH is an alias for LMOVE source dest RIGHT LEFT.

LPUSHX / RPUSHX

Push only if the list already exists. Returns 0 if the key does not exist.

RESP3 syntaxLPUSHX key element [element ...], RPUSHX key element [element ...]
Redis compatFully compatible

BLPOP / BRPOP / BLMOVE / BLMPOP

Blocking variants of pop/move. These are only available in TCP/RESP3 mode -- not in embedded mode. When the list is empty, the connection blocks until an element is pushed or the timeout expires.


Set Commands

Each set member is stored as a compound key S:redis_key\0member -> "1". This allows O(1) membership testing.

SADD / SREM

RESP3 syntaxSADD key member [member ...], SREM key member [member ...]
Embedded APIFerricStore.sadd(key, members), FerricStore.srem(key, members)
RESP3 returnInteger -- count of members added/removed
Elixir return{:ok, integer()}
Redis compatFully compatible. Type metadata cleaned up when set becomes empty.

SMEMBERS / SISMEMBER / SCARD

CommandSyntaxReturn
SMEMBERSSMEMBERS keyArray of members
SISMEMBERSISMEMBER key member1 or 0
SCARDSCARD keyInteger -- set size

All Redis-compatible. Non-existent keys return empty/0.

SRANDMEMBER / SPOP

RESP3 syntaxSRANDMEMBER key [count], SPOP key [count]
Redis compatFully compatible. Negative count for SRANDMEMBER allows duplicates (matches Redis). SPOP removes the selected members.

SDIFF / SINTER / SUNION

Set algebra operations across multiple keys.

RESP3 syntaxSDIFF key [key ...], SINTER key [key ...], SUNION key [key ...]
Embedded APIFerricStore.sdiff(keys), FerricStore.sinter(keys), FerricStore.sunion(keys)
RESP3 returnArray of members
Redis compatFully compatible. All keys are loaded into MapSet for computation.

SDIFFSTORE / SINTERSTORE / SUNIONSTORE

Store operations that compute set algebra and write the result to a destination key.

RESP3 syntaxSDIFFSTORE dest key [key ...], SINTERSTORE dest key [key ...], SUNIONSTORE dest key [key ...]
RESP3 returnInteger -- cardinality of the resulting set
Redis compatFully compatible. Destination is cleared and re-created.

SINTERCARD

Returns the cardinality of the intersection without creating a new set.

RESP3 syntaxSINTERCARD numkeys key [key ...] [LIMIT limit]
RESP3 returnInteger -- intersection cardinality (capped by LIMIT if provided)
Redis compatFully compatible

SMISMEMBER

Returns whether each member is a member of the set.

RESP3 syntaxSMISMEMBER key member [member ...]
RESP3 returnArray of 1 / 0
Redis compatFully compatible

SMOVE

Atomically moves a member from source to destination set.

RESP3 syntaxSMOVE source destination member
RESP3 return1 (moved) or 0 (member not in source)
Redis compatFully compatible

SSCAN

Cursor-based iteration with optional MATCH and COUNT.

RESP3 syntaxSSCAN key cursor [MATCH pattern] [COUNT count]
Redis compatCursor is offset-based. Default COUNT is 10.

Sorted Set Commands

Each sorted set member is stored as Z:redis_key\0member -> score_string. Scores are float64 strings. For range queries, all members are loaded and sorted in memory -- adequate for typical cache workloads.

ZADD

Adds members with scores. Supports all Redis modifier flags.

RESP3 syntaxZADD key [NX|XX] [GT|LT] [CH] score member [score member ...]
Embedded APIFerricStore.zadd(key, [{score, member}, ...])
RESP3 returnInteger -- count of elements added (or added+changed with CH)
Elixir return{:ok, integer()}

Options:

  • NX -- only add new elements, don't update existing
  • XX -- only update existing elements, don't add new
  • GT -- only update when new score > current score
  • LT -- only update when new score < current score
  • CH -- return count of added + changed (instead of just added)

Redis compat: Fully compatible.

ZSCORE / ZMSCORE

RESP3 syntaxZSCORE key member, ZMSCORE key member [member ...]
RESP3 returnBulk string (score) or null
Redis compatFully compatible

ZRANK / ZREVRANK

Returns zero-based rank of a member.

RESP3 syntaxZRANK key member, ZREVRANK key member
RESP3 returnInteger or null (member not found)
Redis compatFully compatible

ZRANGE / ZREVRANGE

Range query by index with optional WITHSCORES.

RESP3 syntaxZRANGE key start stop [WITHSCORES], ZREVRANGE key start stop [WITHSCORES]
Embedded APIFerricStore.zrange(key, start, stop, withscores: bool)
RESP3 returnArray of members, or interleaved [member, score, ...] with WITHSCORES
Redis compatThe legacy index-based syntax is fully compatible. The Redis 6.2+ unified ZRANGE syntax (BYSCORE/BYLEX/REV/LIMIT) is NOT yet supported -- use ZRANGEBYSCORE/ZREVRANGEBYSCORE instead.

ZRANGEBYSCORE / ZREVRANGEBYSCORE

Range by score with optional WITHSCORES and LIMIT.

RESP3 syntaxZRANGEBYSCORE key min max [WITHSCORES] [LIMIT offset count]
Supported boundsNumeric, -inf, +inf, (exclusive prefix
Redis compatFully compatible. Negative LIMIT count means "all remaining".

ZCOUNT

Count members with scores in the given range.

RESP3 syntaxZCOUNT key min max
Redis compatFully compatible. Supports -inf, +inf, and (exclusive.

ZINCRBY

Increment the score of a member. Creates the member if it does not exist.

RESP3 syntaxZINCRBY key increment member
RESP3 returnBulk string -- the new score
Redis compatFully compatible

ZPOPMIN / ZPOPMAX

Pop the lowest/highest scored members.

RESP3 syntaxZPOPMIN key [count], ZPOPMAX key [count]
RESP3 returnArray of [member, score, ...]
Redis compatFully compatible. Cleans up type metadata when empty.

ZRANDMEMBER / ZSCAN / ZCARD / ZREM

CommandRedis compat
ZRANDMEMBER key [count [WITHSCORES]]Fully compatible. Negative count allows duplicates.
ZSCAN key cursor [MATCH pattern] [COUNT count]Offset-based cursor
ZCARD keyFully compatible
ZREM key member [member ...]Fully compatible

Stream Commands

Stream entries are stored as compound keys X:{stream_key}\0{ms}-{seq} with field-value pairs serialized as ETF. Stream metadata (length, first/last ID, sequence counters) is tracked in an ETS table for fast access. Stream IDs use a Hybrid Logical Clock (HLC) for monotonicity, even when the wall clock jumps backward.

XADD

Adds an entry to a stream with optional trimming and NOMKSTREAM.

RESP3 syntaxXADD key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold] *|ID field value [field value ...]
Embedded APIFerricStore.xadd(key, fields)
RESP3 returnBulk string -- the generated entry ID
Elixir return{:ok, binary()}

ID generation: * auto-generates using HLC. Explicit IDs must be strictly greater than the last entry. Partial IDs (just milliseconds) auto-assign the sequence.

Redis compat: Fully compatible, including NOMKSTREAM and trim options.

XLEN / XRANGE / XREVRANGE

CommandSyntaxReturnNotes
XLENXLEN keyIntegerFrom ETS metadata, O(1)
XRANGEXRANGE key start end [COUNT count]Array of entries- = min, + = max
XREVRANGEXREVRANGE key end start [COUNT count]Array (reversed)Redis-compatible

XREAD

Reads entries from one or more streams. Supports BLOCK for waiting on new data.

RESP3 syntaxXREAD [COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...]
Special IDs$ = only new entries from now on; 0 = all entries
BLOCK behaviorIn TCP mode, the connection registers as a stream waiter and is notified by XADD. In embedded mode, BLOCK is not supported.
Redis compatFully compatible in TCP mode. BLOCK 0 = infinite wait.

XTRIM / XDEL

CommandSyntaxReturnNotes
XTRIMXTRIM key MAXLEN|MINID [=|~] thresholdInteger (entries deleted)~ is accepted but exact trim is always applied
XDELXDEL key id [id ...]Integer (entries deleted)Metadata rebuilt after deletion

XINFO STREAM

Returns stream metadata as a map.

RESP3 syntaxXINFO STREAM key
RESP3 returnMap with length, first-entry, last-entry, last-generated-id, groups
Redis compatSubset of Redis XINFO. FULL option not yet supported.

XGROUP CREATE / XREADGROUP / XACK

Consumer group support:

CommandSyntaxNotes
XGROUP CREATEXGROUP CREATE key group id [MKSTREAM]$ for new-only, 0 for all
XREADGROUPXREADGROUP GROUP group consumer [COUNT count] STREAMS key [key ...] id [id ...]> for new messages, 0 for pending
XACKXACK key group id [id ...]Returns count acknowledged

Consumer group state (pending entries, consumers, last-delivered-id) is tracked in ETS. XGROUP DESTROY, DELCONSUMER, and SETID are not yet implemented.


Key/Generic Commands

TYPE

Returns the type of a key as a simple string.

RESP3 syntaxTYPE key
RESP3 returnSimple string: string, hash, list, set, zset, stream, or none
Redis compatFully compatible

RENAME / RENAMENX / COPY

CommandSyntaxReturnNotes
RENAMERENAME key newkey+OK or errorCopies value+TTL, deletes old
RENAMENXRENAMENX key newkey1 (renamed) or 0 (dest exists)Same key returns 0
COPYCOPY source dest [REPLACE]1 (success) or errorREPLACE overwrites existing dest

Note: These operate on plain string keys only. Renaming compound data structures (hash, list, set, zset) is not supported -- only the raw value is copied.

SCAN

Cursor-based key iteration with optional MATCH pattern, COUNT hint, and TYPE filter.

RESP3 syntaxSCAN cursor [MATCH pattern] [COUNT count] [TYPE type]
RESP3 return[next_cursor, [key, ...]]

FerricStore behavior: Cursor is the last key seen (alphabetic). "0" starts from the beginning. The prefix index is used for prefix:* patterns for O(matching) performance. Internal compound keys (H:, S:, Z:, T:, VM:, V:) are filtered out.

RANDOMKEY / DBSIZE / KEYS

CommandSyntaxReturn
RANDOMKEYRANDOMKEYRandom key or null
DBSIZEDBSIZEInteger -- key count (excludes internal keys)
KEYSKEYS patternArray of matching keys. Uses prefix index for prefix:* patterns.

EXPIRE / PEXPIRE / EXPIREAT / PEXPIREAT / TTL / PTTL / PERSIST

CommandSyntaxReturn
EXPIREEXPIRE key seconds1 (set) or 0 (key missing)
PEXPIREPEXPIRE key ms1 or 0
EXPIREATEXPIREAT key unix-ts1 or 0
PEXPIREATPEXPIREAT key unix-ts-ms1 or 0
TTLTTL keySeconds remaining, -1 (no expiry), -2 (missing)
PTTLPTTL keyMilliseconds remaining, -1, -2
PERSISTPERSIST key1 (removed), 0 (no expiry or missing)
EXPIRETIMEEXPIRETIME keyAbsolute Unix timestamp (seconds), -1, -2
PEXPIRETIMEPEXPIRETIME keyAbsolute Unix timestamp (ms), -1, -2

All Redis-compatible. Expiry uses HLC timestamps internally.

OBJECT

SubcommandReturnNotes
OBJECT ENCODING keyType-specific encodingReturns "embstr" (strings <= 44 bytes), "raw" (longer strings), "hashtable" (hashes), "quicklist" (lists), "skiplist" (sorted sets), "stream" (streams)
OBJECT HELPArray of help stringsRedis-compatible format
OBJECT FREQ keyInteger (LFU counter)Uses keydir LFU, not Redis logarithmic frequency
OBJECT IDLETIME keyInteger (idle seconds)Derived from LFU last-decrement-time. Returns elapsed seconds since last access.
OBJECT REFCOUNT key1Always 1

WAIT

RESP3 syntaxWAIT numreplicas timeout
RESP3 return0 (always)
Redis compatStub -- no replica acknowledgement. Always returns immediately.

Bitmap Commands

Bitmap operations work at the bit level on string values. Bits are numbered MSB-first: bit 0 is the MSB of byte 0 (value 128). Write operations (SETBIT, BITOP) perform a read-modify-write cycle.

CommandSyntaxReturnRedis compat
SETBITSETBIT key offset valueInteger (old bit value)Fully compatible
GETBITGETBIT key offsetInteger (0 or 1)Fully compatible
BITCOUNTBITCOUNT key [start end [BYTE|BIT]]Integer (count of set bits)Fully compatible including BYTE/BIT mode
BITPOSBITPOS key bit [start [end [BYTE|BIT]]]Integer (position or -1)Fully compatible
BITOPBITOP AND|OR|XOR|NOT destkey key [key ...]Integer (dest string length)Fully compatible

HyperLogLog Commands

HyperLogLog sketches are stored as 16,384-byte binary values (plain strings in Bitcask). No special type metadata.

CommandSyntaxReturnRedis compat
PFADDPFADD key element [element ...]1 (modified) or 0Fully compatible
PFCOUNTPFCOUNT key [key ...]Integer (estimated cardinality)Multi-key merges in memory without writing
PFMERGEPFMERGE destkey sourcekey [sourcekey ...]+OKFully compatible. Takes max across registers.

Bloom Filter Commands

Backed by mmap NIF resources. Each filter is a memory-mapped file at data_dir/prob/shard_N/KEY.bloom. Handles are cached in per-shard ETS tables.

CommandSyntaxReturnNotes
BF.RESERVEBF.RESERVE key error_rate capacity+OK or errorerror_rate: (0,1), capacity: positive int
BF.ADDBF.ADD key element1 (added) or 0Auto-creates with defaults (0.01, 100)
BF.MADDBF.MADD key element [element ...]Array of 1/0Auto-creates
BF.EXISTSBF.EXISTS key element1 (may exist) or 0Returns 0 for non-existent keys
BF.MEXISTSBF.MEXISTS key element [element ...]Array of 1/0Returns all 0s for non-existent keys
BF.CARDBF.CARD keyIntegerItems added count
BF.INFOBF.INFO keyArray: Capacity, Size, filters, items, expansion, error rate, hashes, bits

Redis compat: Compatible with RedisBloom module syntax. Optimal sizing uses m = -n*ln(p) / (ln(2))^2. No scaling/expansion support (single filter).


Cuckoo Filter Commands

Backed by mmap NIF resources at data_dir/prob/shard_N/KEY.cuckoo. Supports deletion (unlike Bloom).

CommandSyntaxReturnNotes
CF.RESERVECF.RESERVE key capacity+OK or errorBucket size: 4
CF.ADDCF.ADD key element1 or error (filter full)Auto-creates with capacity 1024
CF.ADDNXCF.ADDNX key element1 (added), 0 (already exists), or error
CF.DELCF.DEL key element1 (deleted) or 0 (not found)Deletes one occurrence
CF.EXISTSCF.EXISTS key element1 or 0
CF.MEXISTSCF.MEXISTS key element [element ...]Array of 1/0
CF.COUNTCF.COUNT key elementInteger (approximate count)Fingerprint occurrences
CF.INFOCF.INFO keyArray: Size, buckets, filters, items, deletes, bucket_size, fingerprint_size, max_kicks, expansion

Redis compat: Compatible with RedisBloom/Cuckoo module syntax.


Count-Min Sketch Commands

Backed by mmap NIF resources at data_dir/prob/shard_N/KEY.cms.

CommandSyntaxReturnNotes
CMS.INITBYDIMCMS.INITBYDIM key width depth+OKwidth and depth must be > 0
CMS.INITBYPROBCMS.INITBYPROB key error probability+OKwidth = ceil(e/error), depth = ceil(ln(1/prob))
CMS.INCRBYCMS.INCRBY key item count [item count ...]Array of countsEach count >= 1
CMS.QUERYCMS.QUERY key item [item ...]Array of estimated counts
CMS.MERGECMS.MERGE dst numkeys key [key ...] [WEIGHTS w ...]+OKAll sources must have same width/depth. Creates dst if missing.
CMS.INFOCMS.INFO key[width, W, depth, D, count, C]

Redis compat: Compatible with RedisBloom CMS module syntax.


TopK Commands

Backed by mmap NIF resources at prob/shard_N/KEY.topk. Uses Count-Min Sketch internally with a Heavy Keeper algorithm.

CommandSyntaxReturnNotes
TOPK.RESERVETOPK.RESERVE key k [width depth decay]+OKDefaults: width=8, depth=7, decay=0.9
TOPK.ADDTOPK.ADD key element [element ...]Array (evicted items or nil)
TOPK.INCRBYTOPK.INCRBY key element count [element count ...]Array (evicted items or nil)
TOPK.QUERYTOPK.QUERY key element [element ...]Array of 1/0
TOPK.LISTTOPK.LIST key [WITHCOUNT]Array of items (or interleaved items+counts)
TOPK.INFOTOPK.INFO key[k, K, width, W, depth, D, decay, D]

Redis compat: Compatible with RedisBloom TopK module syntax.


TDigest Commands

T-digests provide accurate rank-based statistics (quantiles, CDF, trimmed means) with bounded memory and high accuracy at the tails (P99, P99.9). Stored as tagged tuples {:tdigest, centroids, metadata} in Bitcask.

CommandSyntaxReturnNotes
TDIGEST.CREATETDIGEST.CREATE key [COMPRESSION c]+OKDefault compression: 100
TDIGEST.ADDTDIGEST.ADD key value [value ...]+OKAccepts floats and integers
TDIGEST.RESETTDIGEST.RESET key+OKClears data, preserves compression
TDIGEST.QUANTILETDIGEST.QUANTILE key q [q ...]Array of float stringsq must be in [0, 1]
TDIGEST.CDFTDIGEST.CDF key value [value ...]Array of float stringsCDF at each value
TDIGEST.RANKTDIGEST.RANK key value [value ...]Array of integersEstimated rank
TDIGEST.REVRANKTDIGEST.REVRANK key value [value ...]Array of integersReverse rank
TDIGEST.BYRANKTDIGEST.BYRANK key rank [rank ...]Array of float stringsValue at rank
TDIGEST.BYREVRANKTDIGEST.BYREVRANK key rank [rank ...]Array of float stringsValue at reverse rank
TDIGEST.TRIMMED_MEANTDIGEST.TRIMMED_MEAN key lo hiFloat stringlo must be < hi
TDIGEST.MINTDIGEST.MIN keyFloat string or "nan"
TDIGEST.MAXTDIGEST.MAX keyFloat string or "nan"
TDIGEST.INFOTDIGEST.INFO keyArray: Compression, Capacity, Merged/Unmerged nodes, weights, total_compressions, Memory usage
TDIGEST.MERGETDIGEST.MERGE dest numkeys key [key ...] [COMPRESSION c] [OVERRIDE]+OKOVERRIDE replaces dest; without it, merges into existing

Redis compat: Compatible with RedisBloom TDigest module syntax.


Geo Commands

Geo is implemented on top of Sorted Sets. Members are stored with 52-bit interleaved geohash scores (26 bits per axis, ~0.6mm precision), matching Redis's encoding. No new data structure is needed.

CommandSyntaxReturnNotes
GEOADDGEOADD key [NX|XX] [CH] lon lat member [...]Integer (added)Same flags as ZADD
GEOPOSGEOPOS key member [member ...]Array of [lon, lat] or null
GEODISTGEODIST key member1 member2 [M|KM|FT|MI]Bulk string (distance) or nullDefault unit: meters
GEOHASHGEOHASH key member [member ...]Array of 11-char base32 stringsStandard geohash alphabet
GEOSEARCHGEOSEARCH key FROMLONLAT lon lat|FROMMEMBER member BYRADIUS radius unit|BYBOX w h unit [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]ArrayFull Redis GEOSEARCH syntax
GEOSEARCHSTOREGEOSEARCHSTORE dest source [GEOSEARCH opts] [STOREDIST]Integer (stored count)

Redis compat: Fully compatible including all GEOSEARCH options.


JSON Commands

JSON commands use JSONPath (subset) for traversal. JSON values are stored as tagged tuples {:json, json_string} in Bitcask. Every operation deserializes, mutates, and re-serializes.

CommandSyntaxReturnNotes
JSON.SETJSON.SET key path value [NX|XX]+OK or nullNX/XX conditions
JSON.GETJSON.GET key [path ...]JSON stringMultiple paths returns object
JSON.DELJSON.DEL key [path]Integer (1 deleted, 0 not found)Omit path = delete entire key
JSON.NUMINCRBYJSON.NUMINCRBY key path numberString (new number)
JSON.TYPEJSON.TYPE key [path]String: object, array, string, number, boolean, null
JSON.STRLENJSON.STRLEN key [path]Integer or null
JSON.OBJKEYSJSON.OBJKEYS key [path]Array of strings
JSON.OBJLENJSON.OBJLEN key [path]Integer
JSON.ARRAPPENDJSON.ARRAPPEND key path value [value ...]Integer (new length)
JSON.ARRLENJSON.ARRLEN key [path]Integer
JSON.TOGGLEJSON.TOGGLE key pathInteger (0/1 for false/true)
JSON.CLEARJSON.CLEAR key [path]Integer (1 cleared, 0 not)Sets numbers to 0, arrays/objects to empty
JSON.MGETJSON.MGET key [key ...] pathArray of JSON strings

JSONPath subset (v1): $, $.field, $.field.subfield, $[0], $.field[0].name.

Redis compat: Compatible with Redis 8 / RedisJSON syntax for the supported subset.


FerricStore-Native Commands

These commands extend beyond the Redis command set with operations not available in standard Redis.

CAS (Compare-and-Swap)

Atomically sets a key only if its current value matches the expected value. Routed directly through Router.cas/4.

RESP3 syntaxCAS key expected new_value [EX seconds]
Embedded APIFerricStore.cas(key, expected, new_value)
RESP3 return1 (swapped), 0 (value mismatch), null (key missing)
Elixir return{:ok, true}, {:ok, false}, or {:ok, nil}

LOCK / UNLOCK / EXTEND

Distributed lock with owner identity and TTL. Routed through Router.lock/3, Router.unlock/2, Router.extend/3.

CommandSyntaxReturn
LOCKLOCK key owner ttl_ms+OK (acquired) or ERR (already held)
UNLOCKUNLOCK key owner1 (released) or ERR (wrong owner / not held)
EXTENDEXTEND key owner ttl_ms1 (extended) or ERR (wrong owner / not held)

RATELIMIT.ADD

Sliding window rate limiter. Routed through Router.ratelimit_add/4.

RESP3 syntaxRATELIMIT.ADD key window_ms max_count [count]
Embedded APIFerricStore.ratelimit_add(key, window_ms, max)
ReturnArray: [allowed (0|1), current_count, remaining, retry_after_ms]
Default count1

FETCH_OR_COMPUTE

Cache-aside with stampede protection. The first caller to a missing key is designated the "computer" -- all concurrent callers block until the value is available.

CommandSyntaxReturn
FETCH_OR_COMPUTEFETCH_OR_COMPUTE key ttl_ms [hint]["hit", value] or ["compute", channel]
FETCH_OR_COMPUTE_RESULTFETCH_OR_COMPUTE_RESULT key value ttl_ms+OK
FETCH_OR_COMPUTE_ERRORFETCH_OR_COMPUTE_ERROR key message+OK

KEY_INFO

Returns diagnostic metadata about a key.

RESP3 syntaxKEY_INFO key (or FERRICSTORE.KEY_INFO key)
ReturnArray: [type, T, value_size, N, ttl_ms, N, hot_cache_status, hot|cold, last_write_shard, N]

Server Commands

PING / ECHO

CommandSyntaxReturn
PINGPING [message]+PONG (no args), or bulk string (with message)
ECHOECHO messageBulk string

INFO

Returns server information. Supports sections: server, clients, memory, keyspace, stats, persistence, replication, cpu, namespace_config, raft, bitcask, ferricstore, keydir_analysis. Use all or everything for all sections.

RESP3 syntaxINFO [section]
FerricStore sectionsraft (per-shard role/term/commit), bitcask (per-shard file counts/sizes), ferricstore (raft committed, hot cache evictions), keydir_analysis (per-prefix key breakdown), namespace_config (group-commit settings)

The server section reports redis_version: 7.4.0 for client compatibility along with ferricstore_version: 0.3.3.

CONFIG

SubcommandSyntaxNotes
CONFIG GETCONFIG GET patternGlob pattern matching
CONFIG SETCONFIG SET key valueChanges logged to audit log
CONFIG SET LOCALCONFIG SET LOCAL key valueNode-local config override
CONFIG GET LOCALCONFIG GET LOCAL keyRead node-local config
CONFIG RESETSTATCONFIG RESETSTATResets stats + slowlog
CONFIG REWRITECONFIG REWRITEPersists config changes

SLOWLOG

SubcommandSyntaxReturn
SLOWLOG GETSLOWLOG GET [count]Array of [id, timestamp_us, duration_us, command]
SLOWLOG LENSLOWLOG LENInteger
SLOWLOG RESETSLOWLOG RESET+OK

COMMAND

SubcommandSyntaxReturn
COMMANDCOMMANDArray of command info tuples
COMMAND COUNTCOMMAND COUNTInteger
COMMAND LISTCOMMAND LISTArray of command names
COMMAND INFOCOMMAND INFO name [name ...]Array of info tuples (null for unknown)
COMMAND DOCSCOMMAND DOCS name [name ...]Interleaved [name, [summary]]
COMMAND GETKEYSCOMMAND GETKEYS cmd [args ...]Array of key arguments

CLIENT

Handled via dispatch_client/3 with per-connection state:

SubcommandSyntaxReturn
CLIENT IDCLIENT IDInteger (connection ID)
CLIENT SETNAMECLIENT SETNAME name+OK
CLIENT GETNAMECLIENT GETNAMEBulk string or null
CLIENT INFOCLIENT INFOInfo string for current connection
CLIENT LISTCLIENT LIST [TYPE type]Info string for all connections
CLIENT TRACKINGCLIENT TRACKING ON|OFF [REDIRECT id] [PREFIX ...] [BCAST] [OPTIN] [OPTOUT] [NOLOOP]+OK
CLIENT CACHINGCLIENT CACHING YES|NO+OK
CLIENT TRACKINGINFOCLIENT TRACKINGINFOTracking configuration
CLIENT GETREDIRCLIENT GETREDIRInteger (redirect target or 0)

Other Server Commands

CommandSyntaxReturnNotes
FLUSHDBFLUSHDB [ASYNC|SYNC]+OKBoth modes execute synchronously
FLUSHALLFLUSHALL [ASYNC|SYNC]+OKAlias for FLUSHDB
SELECTSELECT dbErrorSingle-database only
SAVESAVE+OKNo-op (Bitcask is always persisted)
BGSAVEBGSAVE+Background saving startedNo-op
LASTSAVELASTSAVEInteger (current timestamp)
LOLWUTLOLWUT [VERSION v]ASCII artFerricStore branding
DEBUG SLEEPDEBUG SLEEP seconds+OKTesting only. Logged to audit log.
MODULE LISTMODULE LISTEmpty arrayModules not supported
WAITAOFWAITAOF numlocal numreplicas timeout[0, 0]Stub
MEMORY USAGEMEMORY USAGE keyInteger (estimated bytes)

Transaction Commands

CommandSyntaxDescription
MULTIMULTIStart a transaction. Subsequent commands are queued (return +QUEUED).
EXECEXECExecute all queued commands atomically. Returns array of results. Returns null if WATCH detected a change.
DISCARDDISCARDDiscard queued commands, exit MULTI state.
WATCHWATCH key [key ...]Watch keys for changes. If any watched key is modified before EXEC, the transaction is aborted.
UNWATCHUNWATCHStop watching all keys.

Transactions work at the connection level. WATCH implements optimistic locking -- if a watched key is modified by another connection between WATCH and EXEC, EXEC returns null (transaction aborted).


Pub/Sub Commands

CommandSyntaxReturn
SUBSCRIBESUBSCRIBE channel [channel ...]Push messages: [subscribe, channel, count]
UNSUBSCRIBEUNSUBSCRIBE [channel ...]Push messages: [unsubscribe, channel, count]
PSUBSCRIBEPSUBSCRIBE pattern [pattern ...]Push messages: [psubscribe, pattern, count]
PUNSUBSCRIBEPUNSUBSCRIBE [pattern ...]Push messages: [punsubscribe, pattern, count]
PUBLISHPUBLISH channel messageInteger (subscribers that received)
PUBSUB CHANNELSPUBSUB CHANNELS [pattern]Array of active channels
PUBSUB NUMSUBPUBSUB NUMSUB [channel ...]Array of [channel, count, ...]
PUBSUB NUMPATPUBSUB NUMPATInteger (pattern subscriptions)

ACL Commands

CommandSyntaxDescription
ACL SETUSERACL SETUSER username [rule ...]Create/update user
ACL DELUSERACL DELUSER username [username ...]Delete user(s)
ACL GETUSERACL GETUSER usernameGet user info
ACL LISTACL LISTList all users
ACL WHOAMIACL WHOAMICurrent user
ACL SAVEACL SAVEPersist ACL to file
ACL LOADACL LOADLoad ACL from file
AUTHAUTH [username] passwordAuthenticate connection

Differences from Redis -- Summary

  1. Single database -- SELECT returns an error. FerricStore is single-database.
  2. RESP3 only -- HELLO 3 is required. RESP2 is not supported.
  3. No Lua scripting -- EVAL/EVALSHA are not implemented. Use CAS, LOCK, and FETCH_OR_COMPUTE for atomic operations.
  4. No blocking commands in embedded mode -- BLPOP, BRPOP, BLMOVE, BLMPOP, XREAD BLOCK require a TCP connection.
  5. Probabilistic structures are built-in -- no separate Redis Stack module needed. BF, CF, CMS, TopK, TDigest are all native.
  6. CAS is a native command -- no need for Lua scripts for compare-and-swap. WATCH/MULTI/EXEC is also supported.
  7. FETCH_OR_COMPUTE -- built-in cache stampede protection that Redis lacks.
  8. Group commit -- writes are batched for higher throughput. Individual write latency includes the batch window (default 1ms). Use hash tags {tag} to colocate related keys on the same shard for maximum batching -- see Best Practices.
  9. HLC timestamps -- expiry uses Hybrid Logical Clock timestamps instead of wall-clock time. Monotonic even during clock skew.
  10. Compound key storage -- hash fields, set members, and zset members are stored as individual Bitcask entries with structured key prefixes, enabling O(1) field-level access without deserializing the entire data structure.
  11. SCAN cursor -- uses alphabetic key position, not Redis's opaque hash-table cursor. Functionally equivalent but cursor values differ.
  12. OBJECT ENCODING -- returns type-specific encodings ("embstr", "raw", "hashtable", "quicklist", "skiplist", "stream") but does not use Redis's memory-optimized internal encodings like ziplist, listpack, intset, or skiplist (Redis's native C implementation).
  13. INFO sections -- includes FerricStore-specific sections: raft, bitcask, ferricstore, keydir_analysis, namespace_config.