New: prepared statements, UPDATE and DELETE, and key lookups that seek — 0.143 ms through the gateway.Read the update

Node protocol v0

ufd serves one storage engine over a Unix socket. Protocol v0 is small, checksummed and strict. It is the engine's local interface, not the client protocol: applications connect through the gateway over the PostgreSQL protocol (see SQL and psql).

v0 is temporary. It will be replaced by a protobuf node protocol, and these numbers may change.

Running the server

ufd --dir <data dir> --socket <path> [--memtable-mb N] [--cache-mb N] [--query-mb N]
    [--jwks <file> --issuer <iss> [--audience <aud>]] [--snapshot-retention-secs N]

With --jwks, every connection must log in with a token before anything else (see Login). Without it, the server trusts its local callers, and the socket's file permissions are the only boundary.

Old versions are kept readable for --snapshot-retention-secs (default 60). After that the garbage-collection horizon moves forward on its own, and compaction drops what it released while the server is idle. With 0, the horizon moves only by request 7.

Frames

frame    = body_len:u32 LE | crc32c(body):u32 LE | body      (body ≤ 16 MiB)
request  = kind:u8 | id:u64 | payload
response = status:u8 (0 ok, 1 error) | id:u64 | payload
error    = code:u16 | message (rest of the body, UTF-8)
bytes    = len:u32 | data
opt      = flag:u8 (0 or 1) | [value]

Decoders reject oversized frames, checksum mismatches, unknown kinds and trailing bytes, and never crash on malformed input.

Requests

KindRequestReturns
1PingPong
2SnapshotThe current read timestamp
3Get (snapshot, key)The value, or none
4Write (batch of puts and deletes)The commit timestamp
5Write idempotent (client key, batch)Applied, or the earlier outcome
6Scan (snapshot, start, end, limit)Key and value pairs; at most 10,000
7Set GC horizon (timestamp)Done
8FlushDone
9StatsTables, memory, cache and compaction counters
10MetricsPrometheus text, including per-request latency
11Create collection (name, definition)Done
12Insert (collection, rows)The commit timestamp
13Query (plan, parameters, tenant, policy version, clearance)Result rows
14CatalogCatalog and policy versions, and every collection with its fields and primary key
15Login (token)Done
16Insert new (collection, rows)The commit timestamp; an existing primary key is code 109 and nothing is written

Writes that arrive together are committed as a group with one disk sync, so many small writers do not each pay for their own sync.

Collection rows are not part of the raw keyspace. Raw requests that name a row key are refused with code 108, and raw scans skip the rows: they return the raw keys before and after them, in order.

Queries

A query carries a plan in its canonical binary form, built against a catalog version. The node decodes it, rejecting any other encoding; checks the catalog version; runs the verifier for the requester; optimizes it; and executes it at a fresh snapshot. Each result row is the canonical encoding of the row as a list.

Login

With --jwks, a connection starts with Login and an RS256 token. The node checks the signature against the key set, the issuer and audience, and the validity window. It then holds that identity for the life of the connection:

  • a query or an insert for any other tenant is refused (codes 110 and 112);
  • creating collections, raw key requests and administrative requests need the admin role.

ufd listens only on its Unix socket. Network clients go through the gateway.

Error codes

CodeMeaning
1Write-ahead log failure; the log no longer accepts writes
2Table file failure
3I/O error
4Invalid batch
5Snapshot older than the GC horizon; read at a newer snapshot
6Invalid GC horizon
7Overloaded; retry after a pause
8Idempotency key reused with a different batch
9Corruption detected
100The request could not be decoded
101The query exceeded its memory budget or result limits
102The plan was built against another catalog version; rebuild it
103The verifier rejected the plan
104Evaluation error: overflow, division by zero, a failed cast or a bad parameter
105No such collection
106The collection already exists
107An invalid collection definition, or a row that does not fit its collection
108A raw Get or Write touched a collection row key; use Query and Insert
109The primary key already exists: always for Insert new, and for Insert into a columnar collection (columnar collections are append-only)
110A row belongs to another tenant than the logged-in identity
111Not logged in, or the token was refused
112Not allowed for this identity, or a request that needs the admin role