A GenDurable.Limiter backend on Redis — no Postgres locks, no bucket table.
Concurrency is a lease-scored ZSET per key (
gd:conc:<key>): members are the holders (thegen_durablerow id), scores are lease-expiry timestamps.admitprunes expired members first (ZREMRANGEBYSCORE … now) then admits up tocap - ZCARD. A crashed holder's lease simply expires and is pruned on the next admit — the slot self-heals with no Postgres reconcile. The heartbeat bumps live holders' scores (renew), so a long step is never pruned mid-flight.Rate is a token bucket per key (
gd:rate:<key>, hash{t, s}): refill by elapsed time, grant the priority prefix that fits, no credit (time refills it). An idle bucket isPEXPIREd so a swept key reads as cold/full — mirroring the PG lazy-mint.
admit/renew are single Lua EVALs (Redis is single-threaded → atomic); now comes from
Redis TIME so prune and lease share one clock. credit is a ZREM pipeline.
Config (caps/rates) lives in :persistent_term, seeded by sync_config/2 at startup —
not in Redis — so admit needs no config round-trip and all nodes agree from the same app
config. The handle is %{conn: redix_name, lease_ttl_ms: ttl, cfg_key: term}.
Single-node Redis only. admit touches every batched key in one script; on Redis
Cluster those keys would span hash slots and the EVAL would be rejected. (The engine's
{:redis, _} poke transport assumes a single Redis too.)