GenDurable.Limiter.Redis (gen_durable v0.2.12)

Copy Markdown View Source

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 (the gen_durable row id), scores are lease-expiry timestamps. admit prunes expired members first (ZREMRANGEBYSCORE … now) then admits up to cap - 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 is PEXPIREd 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.)