Skip to content

ADR 0003: Require Valkey for distributed runtime capabilities ​

  • Status: Accepted
  • Date: 2026-08-06

Context ​

Ovahol needs capabilities such as rate limiting, idempotency, short-lived caching, distributed scheduler locks, and temporary job progress. Some of these capabilities, including rate limiting and timer coordination, are currently implemented with process-local memory and goroutines. Those implementations do not coordinate across API or scheduler replicas and lose their state when a process restarts.

PostgreSQL remains the authoritative store for business state.

Decision ​

Adopt Valkey as a required runtime dependency for Ovahol deployments. Its connection lifecycle is owned by the platform layer under backend/internal/platform/cache, with purpose-specific adapters for rate limiting, idempotency, caching, distributed locks, transient job state, and similar coordination capabilities.

Business modules depend on narrow interfaces rather than a general-purpose Valkey client. Every key has an explicit prefix, tenant scope where applicable, and TTL where appropriate.

Production API, worker, and scheduler processes that require these capabilities fail startup readiness when Valkey is unavailable. Unit tests may use in-memory fakes, and narrowly scoped tooling commands that do not exercise a Valkey-backed capability do not need to connect to Valkey.

Development, staging, and production do not share a Valkey deployment.

Required responsibility map ​

CapabilityValkey responsibilityDurable authority
API and login rate limitingShared counters and windows across API replicasNone; limits are transient policy state
IdempotencyAtomic reservations and bounded replay metadataPostgreSQL where a durable business result must be recoverable
CachingExpiring reference, response, and computed-value cachesPostgreSQL or the owning external system
Scheduler coordinationDistributed leases/locks preventing duplicate executionPostgreSQL for business effects and execution records that require history
Job coordinationDeduplication, leases, transient progress, and wake-up signalsPostgreSQL outbox/job records for work that must not be lost
Refresh sessionsActive-session lookup, expiry, refresh-token rotation state and replay detectionPostgreSQL for durable session and security history
RevocationSession and token-family revocation checks across API replicasPostgreSQL for durable revocation and audit records
Security countersLogin attempts, temporary account/IP throttles and other expiring security statePostgreSQL for durable identity and security audit data

Existing process-local implementations of these distributed capabilities are transitional and should be replaced by the corresponding Valkey-backed adapter. In particular, the current in-memory rate limiter must not remain the production implementation once Valkey is introduced.

Valkey does not own business scheduling rules. The scheduler application and the relevant business module decide what should run and when; Valkey provides the distributed lease that ensures only one scheduler replica performs a due run. Likewise, Valkey is not a substitute for the transactional outbox when a job or event must survive eviction or data loss.

For authentication, short-lived access JWTs may be validated locally. Valkey provides the shared real-time state for refresh sessions, rotation, replay detection, revocation, and login throttling. PostgreSQL retains the durable session record and security audit history. Refresh and security-sensitive session mutations normally fail closed when Valkey is unavailable.

Valkey is not the authoritative store for work orders, audit events, payments, notifications requiring guaranteed delivery, or other durable business records.

Failure policy ​

Each adapter defines its runtime failure behaviour. This policy cannot be chosen globally because rate limiting, caching, locking, and idempotency have different correctness requirements. A cache operation may degrade to its authoritative source, while a scheduler lease or correctness-critical idempotency reservation normally fails closed.

Consequences ​

  • Valkey lifecycle, health, metrics, timeouts, and configuration are managed by application composition.
  • Valkey is included in local, test, staging, and production deployment definitions, although unit tests may use fakes.
  • Application readiness distinguishes Valkey failures from PostgreSQL failures.
  • Tenant identifiers are mandatory in keys for tenant-owned data.
  • Cache eviction must never delete authoritative business state.
  • Deployments should be split when workload, eviction, durability, security, scaling, or availability requirements diverge.

Ovahol developer documentation