Skip to content

Storage backends

All backends implement the idempo.Store interface, so you can pick one of the three provided or write your own.

// In-memory: single instance or testing.
import "github.com/eben-vranken/idempo/inmem"
store := inmem.New(24*time.Hour, 5*time.Minute)

// Redis: distributed, high throughput.
import (
    "github.com/eben-vranken/idempo/redis"
    goredis "github.com/redis/go-redis/v9"
)
store := redis.New(&goredis.Options{Addr: "localhost:6379"}, 24*time.Hour, 5*time.Minute)

// Postgres: durable, ACID.
import "github.com/eben-vranken/idempo/pg"
if err := pg.RunMigration(connStr); err != nil { /* ... */ }
store, err := pg.New(connStr, 24*time.Hour, 5*time.Minute)

The two TTLs

Every backend constructor takes the same two time.Duration arguments, in this order:

Argument Meaning
lockTTL How long an in-flight claim is held. While a key is pending, duplicates get 409. If the owning request never completes (crash, lost connection), the claim expires after lockTTL and the key can be claimed again.
retentionTTL How long a completed response stays replayable. After a request finishes, its stored response is retained for retentionTTL; later requests with the same key and request fingerprint replay it. Once it expires, the key is treated as new again.

In the examples above, an in-flight claim is held for 24 hours and a completed response is replayable for 5 minutes.

Choosing a backend

Backend Use when Notes
In-memory Single instance or testing State is lost on restart and not shared across instances.
Redis Distributed, high throughput Atomicity via an atomic Lua script.
Postgres Durable, ACID Atomicity via INSERT ... ON CONFLICT.

See Write your own backend for the Store contract if none of these fit.