Skip to content
PostRESP
Esc
navigateopen⌘Jpreview
On this page

Commands

Supported Redis commands — full matrix, notes, and TTL behaviour.

PostRESP implements a growing Redis command surface over Postgres storage. Status meanings:

Badge Meaning
Supported Implemented with Redis-like semantics for the common path
Partial Works for basic usage; some options, types, or edge cases missing
Stub Minimal reply for client/harness compatibility — not a full implementation
Not supported Returns an unknown-command error today

Redis compatibility is validated against a pinned Valkey test suite (currently 8.1.4). See the live per-test tracker on Compatibility.

Command matrix

Connection & server

Command Status Notes
PING Supported
ECHO Supported
QUIT Supported
AUTH Supported Postgres LOGIN role; required by default (RequireClientAuth)
SELECT Partial Only database 0
INFO Supported default / all / named sections (cpu, replication, commandstats, …); redis_version matches the Valkey pin; pg_resp_* fields
HELLO Partial RESP2 only (HELLO / HELLO 2); HELLO 3NOPROTO; supports AUTH / SETNAME options
CLIENT Partial REPLY, SETNAME, GETNAME, SETINFO (lib-name / lib-ver)
CONFIG Partial GET returns real values (databases=1, …); SET accepted as no-op except databases; RESETSTAT clears INFO counters
FUNCTION Stub FLUSH accepted for harnesses
COMMAND Stub Empty array reply
FLUSHDB Supported
FLUSHALL Supported

Strings & keys

Command Status Notes
SET Supported NX / XX / GET / IFEQ / EX / PX / EXAT / PXAT / KEEPTTL
SETNX Supported
SETEX / PSETEX Supported
GET Supported Binary-safe; empty string ≠ missing key
GETDEL Supported
GETEX Supported
GETSET Supported
DEL Supported
UNLINK Supported Same as DEL today (synchronous delete; reply is deleted count)
EXISTS Supported
TYPE Supported none / string / list / set (hash when hash storage exists)
KEYS Partial Redis glob MATCH; expensive — prefer SCAN for large keyspaces
RANDOMKEY Supported Null bulk when DB is empty
STRLEN Supported
APPEND Supported
GETRANGE / SUBSTR Supported SUBSTR aliases GETRANGE
SETRANGE Supported
MGET / MSET / MSETNX Supported
INCR / DECR / INCRBY / DECRBY Supported
INCRBYFLOAT Supported
DBSIZE Supported
MEMORY USAGE Partial Approximate payload size (key + value)
SETBIT / GETBIT Supported
LCS Supported

Hashes

Command Status Notes
HSET / HMSET Supported Variadic field/value pairs; HMSET is legacy OK reply
HSETNX Supported
HGET / HMGET Supported Missing fields → null bulk
HDEL Supported Empty hash deletes the key
HEXISTS / HLEN Supported
HGETALL / HKEYS / HVALS Supported
Other hash commands (HINCRBY, HSCAN, HRANDFIELD, …) Not supported

Sets

Command Status Notes
SADD Supported Returns newly added member count
SREM Supported Deletes key when the set becomes empty
SISMEMBER Supported
SMISMEMBER Supported
SCARD Supported
SMEMBERS Supported
SPOP Partial Optional count; random member(s); no replication rewrite
SUNION Supported Missing keys treated as empty
Other set commands (SINTER, SDIFF, SSCAN, …) Not supported

Lists

Command Status Notes
LPUSH / RPUSH Supported Variadic
LPOP / RPOP Supported Optional count
LLEN Supported
LRANGE Supported
LINDEX Supported
LTRIM Supported Empty result deletes the key
LSET Supported ERR no such key / ERR index out of range
LREM Supported Count head/tail/all semantics
LINSERT Supported BEFORE / AFTER
Blocking / move (BLPOP, LMOVE, …) Not supported

TTL / expiry

Command Status Notes
EXPIRE / PEXPIRE Supported NX / XX / GT / LT; lazy + active purge
EXPIREAT / PEXPIREAT Supported Absolute unix seconds / milliseconds
EXPIRETIME / PEXPIRETIME Supported
TTL / PTTL Supported
PERSIST Supported
TIME Supported Server clock for clients / harnesses
DELEX Supported IFEQ only (safe lock unlock); not IFNE / digest
SCAN Supported Cursor + MATCH / COUNT / TYPE (string/list/set/zset/hash/stream)

Pub/Sub

Command Status Notes
PUBLISH Supported In-process fan-out (single gateway instance)
SUBSCRIBE / UNSUBSCRIBE Supported RESP2 subscribed-mode filter; RESP2 PING → ["pong", …]; RESP3 PING normal
PSUBSCRIBE / PUNSUBSCRIBE Supported Redis glob patterns (* / ? / \)
PUBSUB Supported CHANNELS / NUMSUB / NUMPAT / HELP
CLIENT REPLY Supported ON / OFF / SKIP — silences command replies; pub/sub pushes still delivered
CLIENT SETNAME / GETNAME Supported Per-connection name
CLIENT SETINFO Supported lib-name / lib-ver (Lettuce handshake)

Known limitation — process-local delivery

Pub/Sub fan-out is in-process only (one hub per gateway process). That matches the default single-instance / in-process RESP path: every client shares the same BGWorker.

If you later run multiple gateway processes against one Postgres (several standalone redis_gateway pods, or more than one RESP listener), a PUBLISH on instance A will not reach subscribers on instance B until we add a cross-process bus — typically PostgreSQL LISTEN/NOTIFY on a single broadcast channel. Key/value data already shared via SQL is unaffected; only live subscription delivery is process-scoped today.

Not yet implemented (families)

Area Status Notes
Hashes Partial Field basics; no incr/scan/rand yet
Sets Partial Basics + SUNION; no SINTER/SDIFF/SSCAN yet
Sorted sets Not supported
Streams Not supported
Keyspace notifications Not supported
Sharded pub/sub Not supported
Transactions (MULTI / EXEC) Not supported
Scripting / modules Not supported Beyond harness stubs
Clustering / replication admin Not supported Single logical DB (SELECT 0)

Distributed locks

Single-node Redis-style locks (not Redlock / multi-node consensus) work without Lua or MULTI/EXEC:

Step Command Notes
Acquire SET resource_name <unique-token> NX PX <ttl-ms> Null bulk if already held
Refresh SET resource_name <unique-token> IFEQ <unique-token> PX <ttl-ms> Extends TTL only while you still hold the token
Unlock DELEX resource_name IFEQ <unique-token> Integer 1 deleted / 0 if token mismatch or missing

Use a random per-acquire token so an expired holder cannot delete another client’s lock. Prefer DELEX IFEQ over DEL / GET+DEL. SQL storage serializes per-key mutations (pgresp.lock_key) so concurrent SET NX and DELEX IFEQ stay atomic across sessions.

TTL / expiry

  1. Lazy — expiry checked on read/write paths so stale keys are not returned.
  2. Activepg_cron periodically batch-deletes expired rows.

Known limitation — naive active expiry

Path What we do now Gap vs Redis
SQL (pg_cron) Delete up to batch × rounds rows per minute Coarser than Redis sampling cadence

SQL sweeper GUCs:

GUC Default Meaning
pg_resp.ttl_delete_batch_size 1000 Keys per DELETE round
pg_resp.ttl_delete_max_rounds 10 Rounds per CALL pgresp.delete_expired_keys()

Correctness (lazy-on-access + eventual active delete) is fine. Cost under very large key counts is not — raise the GUCs to drain backlogs faster, or prefer access patterns that rely on lazy expiry.

Was this page helpful?