ServicesDistributed State

Distributed State

Coordination primitives for processes that share work: namespace-scoped state entries with TTL leases, optimistic concurrency via version tokens (compare-and-swap), and distributed locks with fencing tokens and TTL auto-release. Every example on this page was executed against a live server.

When to reach for it: leader election, singleton cron jobs across replicas, deploy/rollout phase flags that several workers read and update, checkpoints that must survive a crashed holder, any read-modify-write on shared state that must not lose updates.

When not to: general key-value storage - tagging, full-text search over values, counters, binary values - belongs to KV Store; schema-validated, environment-scoped configuration with inheritance belongs to Remote Config. The split: KV stores data, Distributed State coordinates writers.

Concepts

  • Namespace + key - every entry lives at {namespace}/{key}. A namespace groups one coordination domain (deploy, jobs) and is listable and deletable as a unit.
  • Every entry is a lease - a TTL always applies: default 1 hour, maximum 30 days. There are no permanent entries; an expired key reads as 404 key_not_found.
  • Version tokens - every successful write returns a fresh opaque version string. It is the handle for compare-and-swap, not a counter.
  • Conditional writes - if_not_exists makes the write create-only (409 key_already_exists if the key is there); if_version_matches makes it a compare-and-swap (409 version_mismatch if someone wrote in between).
  • Locks are a separate resource - each {namespace}/{key} also has a lock slot with its own lifecycle, fencing tokens, and TTL auto-release. They have their own page: Distributed locks.
  • State is project-shared, not per-user - verified: a second user's token reads and CAS-updates a key another user created. That is the point of a coordination store; do not put per-user private data here.

Store and read

snug distributed-state set -n docs4-config -k feature-flag -v '{"enabled":true,"rollout":25}' -t 3600
snug --output json distributed-state get -n docs4-config -k feature-flag
snug distributed-state delete -n docs4-config -k feature-flag

set both creates and replaces; the response's operation field says which happened (created or updated). Over HTTP the same write is a POST:

curl -X POST -H "Authorization: Bearer $SNUG_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"value": {"phase": "deploying"}, "ttl_seconds": 600}' \
  http://localhost:4000/api/v1/distributed-state/docs4-deploy/status
{
  "status": 201,
  "msg": "Created",
  "data": {
    "namespace": "docs4-deploy",
    "key": "status",
    "value": { "phase": "deploying" },
    "ttl_seconds": 600,
    "version": "FgLvDXmxYpwr",
    "created_at": "2026-08-28T04:35:33.991243Z",
    "updated_at": "2026-08-28T04:35:33.991243Z",
    "operation": "created"
  }
}

Compare-and-swap

The safe way to do read-modify-write without a lock: read the entry, keep its version, and write with --if-version. If another writer got there first, the write fails instead of clobbering. Verified - two writes with the same token, second one rejected:

snug --output json distributed-state get -n docs4-config -k feature-flag   # version: mQJnTDvDgJQs
snug distributed-state set -n docs4-config -k feature-flag \
  -v '{"enabled":true,"rollout":50}' --if-version mQJnTDvDgJQs             # ok, new version
snug distributed-state set -n docs4-config -k feature-flag \
  -v '{"enabled":true,"rollout":75}' --if-version mQJnTDvDgJQs             # stale token
{
  "status": 409,
  "msg": "Version mismatch for key docs4-config/feature-flag: expected mQJnTDvDgJQs, got CFCvYSgtZnBu",
  "error": "version_mismatch"
}

On 409 version_mismatch, re-read, re-apply your change to the fresh value, and retry with the new token. For create-only initialization use --if-not-exists; a losing racer gets 409 key_already_exists.

TTL

Every write carries a lease. -t/ttl_seconds sets it explicitly; when omitted, the server default applies (3600 seconds, verified in the write response). After expiry the key is simply gone:

snug distributed-state set -n docs4-config -k ephemeral -v '"soon gone"' -t 1
sleep 2
snug distributed-state get -n docs4-config -k ephemeral   # 404 key_not_found

Listing keys

Namespace listing returns full entries (values included) and speaks the shared search grammar - q, filter, sort_by, sort_order, and pagination:

snug --output json distributed-state list -n docs4-config -l 20
snug distributed-state list -n docs4-config -q replica   # matches key "replica-count"

Deleting a namespace

delete-namespace removes every state key in the namespace - there is no undo - so it is double-guarded: the CLI wants --confirm (or an interactive yes), and the HTTP DELETE /api/v1/distributed-state/{namespace} requires confirm=true. An optional pattern limits which keys die. Verified:

snug --output json distributed-state delete-namespace -n docs4-config --confirm
{ "deleted_count": 2, "namespace": "docs4-config", "pattern_used": "*" }

Locks

Mutual exclusion with owner identities, blocking waits, fencing tokens, renewal, and TTL auto-release when a holder crashes - the full lifecycle, each behavior reproduced live, is on the Distributed locks page.

Limits and configuration

Values up to 1 MB, 10,000 keys per namespace, lock metadata up to 16 KB, state TTL capped at 30 days and lock TTL at 1 hour by default - all tunable via the DISTRIBUTED_STATE_* variables in the Distributed State CONFIG reference.

Reference

On this page