sharedState
AIOpen as plain markdown for AI
When running multiple eRPC instances, each instance would independently poll upstreams for the latest and finalized block numbers, wasting requests. sharedState lets the cluster share that information through a common backing store — so one instance's discovery is immediately visible to all others.
You can configure:
- Cluster isolation —
clusterKeyscopes shared state to a named group; different clusters don't bleed into each other - Backing store — memory (single-instance default), Redis (recommended for production), or PostgreSQL
- Fallback timeout — how long to wait for the backing store before falling back to local state
- Distributed locking —
lockTtl,lockMaxWait,updateMaxWaitcontrol coordination latency budget
Minimum useful config
A single Redis connector — the recommended production setup. Memory is the default when no connector is specified and works for single-instance deployments only.
database: sharedState: clusterKey: "my-cluster-1" # isolates state from other clusters connector: driver: redis redis: uri: "redis://username:password@host:6379/0?pool_size=10" fallbackTimeout: 3sAlways set a unique clusterKey when running multiple independent eRPC deployments (e.g. separate Kubernetes clusters). Without it, all instances default to "erpc-default" and would share state across unrelated clusters.
Full config with locking tuned
database: sharedState: clusterKey: "my-cluster-1" connector: driver: redis redis: uri: "redis://:some-secret@redis.svc.cluster.local:6379/?pool_size=10&dial_timeout=5s&read_timeout=1s&write_timeout=2s" fallbackTimeout: 3s lockTtl: 2s # TTL for distributed locks; keep short lockMaxWait: 100ms # max time to try acquiring lock before proceeding locally updateMaxWait: 50msStorage footprint is tiny — typically less than 1 MB per upstream regardless of chain traffic volume.
Copy for your AI assistant — full sharedState referenceExpand for every option, default, and edge case — or copy this entire section into your AI assistant.
Full config skeleton
database:
sharedState:
# Unique key scoping this cluster's shared state.
# Default: "erpc-default". Set explicitly when running multiple clusters.
clusterKey: string
# Storage backend. Exactly one connector (not an array).
connector:
driver: memory | redis | postgresql
# Driver-specific block — only the one matching driver is used:
memory: {} # no config; single-instance only
redis:
uri: string # redis://[user:pass@]host:port/db[?options]
postgresql:
connectionUri: string # postgres://user:pass@host:5432/dbname
table: string # optional, default "erpc_shared_state"
# Max time to wait for the backing store before falling back to local state.
# Applies to get/set/publish operations.
# Recommended: 3s
fallbackTimeout: duration
# TTL for distributed locks stored in the backing store.
# Keep short — this is a best-effort coordination path, not a hard guarantee.
# Default: 4s
lockTtl: duration
# Max time to try acquiring a distributed lock before proceeding locally.
# Default: 100ms
lockMaxWait: duration
# Max time to run a shared-state refresh function before returning the
# locally cached value.
# Default: 50ms
updateMaxWait: durationField reference
| Field | Default | Notes |
|---|---|---|
clusterKey | "erpc-default" | Namespace for this cluster's state keys in the backing store. Use a unique value per independent eRPC deployment. Collisions between clusters cause incorrect shared block numbers. |
connector.driver | memory | memory for single-instance; redis recommended for multi-instance; postgresql available but higher latency than Redis. |
fallbackTimeout | none | Duration before the backing-store call is abandoned and the local value is used. Protects the request path from a slow or unavailable store. |
lockTtl | 4s | How long a distributed lock key lives in the store. A crashed instance holding a lock releases it when TTL expires. Setting it too long causes unnecessary serialization under failover; too short risks the lock expiring before the holder finishes writing. |
lockMaxWait | 100ms | How long an instance will spin-wait trying to acquire a lock before giving up and proceeding with its own local value. Keeps the foreground path bounded. |
updateMaxWait | 50ms | Deadline for the shared-state refresh callback (the function that updates block numbers, finality info, etc.). On expiry, the in-process cached value is returned unchanged. |
Connector options
memory — no config block needed. All state is in-process; invisible to other instances. Use only for single-instance deployments or local development.
redis — recommended for production. The uri field uses standard Redis URI syntax:
redis://[user:password@]host:port[/db][?option=value&...]Useful query-string options:
pool_size— connection pool size (default 10 is usually fine)dial_timeout— TCP connect timeout (e.g.5s)read_timeout— per-read timeout (e.g.1s)write_timeout— per-write timeout (e.g.2s)
Redis Sentinel / Cluster URIs are also supported via the redis-sentinel:// and redis-cluster:// schemes (where available).
The Redis connector's lockRetryInterval field (default 500ms) controls the backoff between lock-acquisition attempts for sharedState. See the full Redis driver reference for all timing knobs.
postgresql — use when Redis is unavailable or a single SQL store is preferred. Higher per-operation latency than Redis; set fallbackTimeout generously (e.g. 5s).
When to enable sharedState
Enable it any time you run more than one eRPC instance serving the same networks:
- Kubernetes Deployment with
replicas > 1 - Multiple Fly.io machines in the same app
- Active-active multi-region deployments
With memory (the default), each instance independently tracks the latest/finalized block — every instance polls upstreams separately and may make routing decisions based on slightly stale or inconsistent data. With a shared Redis, one instance's observation is immediately visible to all others, cutting upstream polling by roughly 1/N where N is the replica count.
For single-instance deployments, sharedState with memory has no downside but also no benefit. You can omit the section entirely.
Deployment patterns
Single cluster, Redis:
database:
sharedState:
clusterKey: "prod"
connector:
driver: redis
redis:
uri: "redis://:secret@redis.internal:6379/?pool_size=20"
fallbackTimeout: 3s
lockTtl: 2s
lockMaxWait: 100ms
updateMaxWait: 50msTwo independent clusters sharing one Redis instance — use distinct clusterKey values so keys don't collide:
# cluster A
database:
sharedState:
clusterKey: "erpc-mainnet"
connector:
driver: redis
redis: { uri: "redis://redis.internal:6379/0" }
# cluster B (separate erpc.yaml)
database:
sharedState:
clusterKey: "erpc-testnet"
connector:
driver: redis
redis: { uri: "redis://redis.internal:6379/0" }Common pitfalls
clusterKeycollisions — two unrelated eRPC clusters pointing at the same Redis without distinctclusterKeyvalues will cross-contaminate block-number state, causing incorrect routing decisions (e.g. treating mainnet finalized block as testnet finalized block).lockTtltoo long — if an instance crashes while holding a lock, other instances waitlockTtlbefore the lock is released. A value like30smeans a 30-second stall for one shared-state refresh after any crash. Keep it at 2–5s.lockTtltoo short — if the backing-store round-trip is slower thanlockTtl, the lock expires before the holder finishes writing, allowing concurrent writers and defeating the coordination guarantee. UselockTtl >= 2 * fallbackTimeoutas a rule of thumb.fallbackTimeouttoo tight — a very short timeout (e.g.50ms) on a cross-region Redis will cause near-constant fallback to local state, defeating the purpose of shared state. Set it to at least 2–3× the expected P99 round-trip to the store.- Using
memoryconnector in a multi-instance deployment — the defaultmemoryconnector is completely in-process; no state is shared. If you're running replicas and wondering why shared state seems not to work, check that you've configured Redis or PostgreSQL. - PostgreSQL table not created — the PostgreSQL driver creates the table automatically on first run, but requires
CREATE TABLEpermission on the target schema. Grant it or pre-create the table.
Fallback semantics
All operations against the backing store are best-effort. If the store is down or slow:
- The call respects
fallbackTimeoutand returns the locally cached value. - eRPC continues serving requests using its own in-process state.
- When the store recovers, state sync resumes automatically — no restart needed.
This means a Redis outage degrades multi-instance coordination but does not take eRPC down. The worst case is that instances temporarily diverge on their view of the latest block, which may cause a small amount of redundant upstream polling until the store recovers.
Append .llms.txt to this URL (or use the AI link above) to fetch the entire expanded reference as plain markdown for an AI assistant.