New: SQL transactions — BEGIN, COMMIT, ROLLBACK and savepoints, all or nothing.Read the update

UltraFabric KV

A key-value store for sessions, feature flags, counters and caches that lives in the same database as your SQL tables, under the same tokens and the same tenant isolation. There is no second system to secure, back up or migrate.

UltraFabric KV runs in the repository's end-to-end tests today. There is no public build or hosted endpoint yet; early access teams get the first hosted databases.

What you can rely on

  • Strongly consistent. Once a write is acknowledged, every later read sees it. There is no propagation delay to wait out.
  • Compare-and-set. Every entry has a version. A write or delete can require the version you read, or that the key is absent, so counters, leases and idempotency records are safe under concurrency. In our end-to-end test, 64 clients made 3,200 concurrent increments to one counter and none was lost.
  • All-or-nothing batches. A bulk request of puts and deletes is applied completely or not at all, including its conditions.
  • Expiry. An entry with a time to live is absent for every read, list and condition from the moment it expires, also across restarts.
  • Your tenant only. The tenant comes from your verified token, never from a header. Another tenant can neither read, list, count, overwrite nor detect your namespaces or keys, even with identical names. A namespace can require a clearance, and a token below it cannot use it.
  • Nothing cut short. A key, value, batch or metadata over a limit is refused whole.

Requests

Every request carries Authorization: Bearer <token>. Errors are JSON: {"error": {"code": "...", "message": "..."}}.

RequestWhat it does
GET /v1/namespacesYour namespaces, with their class and creation time.
PUT /v1/namespaces/{ns}Create a namespace (optional body {"class":"internal"}). 201 when created, 200 when it already exists with that class, 409 with another.
DELETE /v1/namespaces/{ns}Delete a namespace and every entry in it.
GET /v1/namespaces/{ns}/values/{key}The value, with ETag (its version), UF-Expires-At and UF-Metadata. HEAD for the headers only.
PUT /v1/namespaces/{ns}/values/{key}Write the body as the value. ?ttl=<seconds> or ?expires_at=<RFC 3339>; UF-Metadata: <json>; If-Match: "<version>" or If-None-Match: *.
DELETE /v1/namespaces/{ns}/values/{key}Delete a key (also fine when absent); If-Match makes it conditional.
GET /v1/namespaces/{ns}/keys?prefix=&cursor=&limit=Keys in order, with version, expiry and metadata. Follow cursor until complete is true; a page can be short while more keys follow.
POST /v1/namespaces/{ns}/bulkPuts and deletes, each optionally conditional, applied as one step.
StatusWhen
401 / 403No or refused token / a namespace above your clearance, or a token without a tenant.
404No live entry (not_found) or no such namespace (namespace_not_found).
412A condition did not hold; nothing was written.
413 / 400Over a limit, or a malformed request; nothing was written.
502 outcome_unknownThe service lost its connection after sending your write, so it may or may not have been applied. It is never retried for you; read the key before trying again.

Limits

Key1–512 bytes of UTF-8
Value4 MiB
Metadata1,024 bytes of JSON
Bulk request10,000 items, 15 MiB
List page1,000 keys
Namespaces per tenant1,000
Time to live1 second to 10 years

Go SDK

c := kv.New("https://kv.example", kv.StaticToken(token))
_ = c.CreateNamespace(ctx, "sessions", kv.Internal)
ns := c.Namespace("sessions")

v, err := ns.Put(ctx, "u:42", data, kv.TTL(30*time.Minute))
e, err := ns.Get(ctx, "u:42")                           // kv.ErrNotFound when absent
_, err = ns.Put(ctx, "u:42", next, kv.IfVersion(e.Version)) // kv.ErrConflict if it changed

for it := ns.List(ctx, kv.Prefix("u:")); it.Next(); {
    fmt.Println(it.Item().Name)
}

Speed

Measured through the HTTP service with 16 clients on a shared development server (indicative only): about 9,400 reads per second with a median of 0.74 ms, and 8,700 operations per second for a 95 % read mix. Write-heavy mixes are slower today, about 2,200 operations per second at half writes, because each write is made durable on its own; batching those writes together is the next step on the roadmap.