Key-Value Engine
Purpose-built hash-indexed store with O(1) point lookups, native TTL, and secondary indexes. Unlike a standalone KV store, data is SQL-queryable, joinable, and syncable via CRDTs.
When to Use
- Session state and tokens
- Feature flags and configuration
- Rate limiters and counters
- Caching without an external cache
- Leaderboards and rankings
SQL Usage
CREATE COLLECTION sessions (key TEXT PRIMARY KEY) WITH (engine='kv');
-- Insert with TTL; plain INSERT raises unique_violation (23505) on duplicate key.
INSERT INTO sessions { key: 'sess_abc', user_id: 'alice', role: 'admin', ttl: 3600 };
-- Set-or-overwrite (Redis SET semantics)
UPSERT INTO sessions { key: 'sess_abc', user_id: 'alice', role: 'admin', ttl: 3600 };
-- Set-if-absent (Redis SETNX semantics)
INSERT INTO sessions { key: 'sess_abc', user_id: 'alice', role: 'admin', ttl: 3600 }
ON CONFLICT DO NOTHING;
-- Conditional merge: bump counter on conflict, EXCLUDED references the incoming row.
INSERT INTO sessions (key, user_id, role, hits) VALUES ('sess_abc', 'alice', 'admin', 1)
ON CONFLICT (key) DO UPDATE SET
role = EXCLUDED.role,
hits = sessions.hits + 1;
-- Get by key
SELECT * FROM sessions WHERE key = 'sess_abc';
-- Analytical queries work too
SELECT role, COUNT(*) FROM sessions GROUP BY role;
-- Join with other collections
SELECT u.name, s.role FROM users u JOIN sessions s ON u.id = s.user_id;
Atomic Operations
SELECT KV_INCR('counters', 'page_views', 1);
SELECT KV_DECR('credits', 'user-123', 50);
SELECT KV_CAS('state', 'player-1', 'idle', 'in_match');
SELECT KV_GETSET('token', 'sess-1', 'new-token');
SELECT KV_INCR('daily_logins', 'user-1', 1, TTL => 86400);
Sorted Indexes (Leaderboards)
CREATE SORTED INDEX lb_global ON scores (score DESC, updated_at ASC) KEY player_id;
SELECT RANK(lb_global, 'player-123');
SELECT * FROM TOPK(lb_global, 10);
SELECT * FROM RANGE(lb_global, 100, 250.5);
SELECT SORTED_COUNT(lb_global);
-- Time-windowed leaderboard
CREATE SORTED INDEX lb_daily ON scores (score DESC, updated_at ASC) KEY player_id
WINDOW DAILY ON updated_at;
-- Fixed-range window: bounds are milliseconds or ISO 8601
CREATE SORTED INDEX lb_season ON scores (score DESC, updated_at ASC) KEY player_id
WINDOW CUSTOM START '2026-01-01' END '2026-04-01' ON updated_at;
Sort Order
Each sort column orders its values by type first, then by value:
| Order | Type | Order within the type |
| 1 | null | — |
| 2 | boolean | false before true |
| 3 | number | numeric value |
| 4 | string | byte order of the UTF-8 text |
| 5 | array or object | byte order of the JSON text |
- Integers and floats share one numeric order.
10.5sorts between10and11. - The order is exact for every 64-bit integer and every float. Integers above 2^53 keep their order.
- An integer and a float of equal value are equal.
11and11.0hold one position. -0.0equals0.- A non-finite float is stored as the text
NaN,Infinityor-Infinity, so it sorts as a string. RANGE(index, min, max)is inclusive at both ends and reads the first sort column. An integer bound matches a float row of equal value.DESCreverses all of the above for that column.
Window Rules
- The window timestamp column must be one of the sort columns.
CREATE SORTED INDEXrefuses the index otherwise (SQLSTATE42601). ONdefaults toupdated_atwhen the clause names no column.DAILY,WEEKLY(from Monday) andMONTHLYwindows start at 00:00 UTC of the current period.- A
CUSTOMwindow is the half-open range[START, END).ENDmust be afterSTART. - A
CUSTOMbound is integer milliseconds since the Unix epoch or an ISO 8601 date or instant. - A timestamp value is one of:
- an integer: milliseconds since the Unix epoch;
- a float: fractional milliseconds since the Unix epoch;
- a string: an ISO 8601 date or instant.
- Integers are milliseconds. A value in epoch seconds lands in January 1970 and falls outside current windows.
- A row whose timestamp is null, a boolean, an array, an object, or a non-ISO 8601 string is hidden from every window.
- A row without the timestamp field is not indexed.
Rate Gates
SELECT RATE_CHECK('attack_cooldown', 'player-123', 3, 10);
-- {"allowed": true, "remaining": 2, "resets_in_ms": 7500}
SELECT RATE_REMAINING('api_calls', 'tenant-1', 100, 60);
SELECT RATE_RESET('attack_cooldown', 'player-123');
Redis-Compatible Access (RESP)
Enable by setting ports.resp = 6381 in config. Any Redis client works:
redis-cli -p 6381
SET sess_abc '{"user":"alice"}' EX 3600
GET sess_abc
INCR page_views
ZADD leaderboard 1500 player-123
ZRANK leaderboard player-123
Supported: GET, SET, DEL, EXISTS, MGET, MSET, EXPIRE, TTL, SCAN, KEYS, HGET, HSET, INCR, DECR, ZADD, ZREM, ZRANK, ZRANGE, SUBSCRIBE, PUBLISH, and more.