Documentation
¶
Overview ¶
Package idempotency implements the HTTP Idempotency-Key request pattern for Celeris.
New returns a middleware that deduplicates retried writes. When a request carries the header named by Config.KeyHeader (default "Idempotency-Key"), the middleware acquires an atomic lock via KVStore, runs the handler once, persists the response, and replays it on subsequent requests with the same key. Concurrent duplicates return 409 Conflict (overridable via Config.OnConflict). Requests without the key header pass through unchanged.
Key types: Config controls all behaviour; KVStore is the store interface (store.KV + store.SetNXer); NewMemoryStore provides a sharded in-memory default suitable for single-instance deployments. For multi-instance deployments, supply a Redis-backed store and namespace it with store.Prefixed. Set Config.BodyHash to detect key reuse with different payloads (returns 422 on mismatch). ErrStoreMissingSetNX is returned by New when a provided store lacks atomic SetNX support.
Documentation ¶
Full guides and examples: https://goceleris.dev/docs/middleware-traffic
Package idempotency implements the HTTP Idempotency-Key pattern.
When a request carries an Idempotency-Key header, the middleware attempts to acquire an atomic lock on the key via store.SetNXer. The first request to acquire the lock runs the handler, persists the response under the same key, and releases the lock. Subsequent requests with the same key replay the stored response.
Concurrent duplicates that arrive while the original is still in-flight return 409 Conflict (configurable via Config.OnConflict). Crashed handlers leak a lock entry; the lock expires after Config.LockTimeout so the next request can retry.
When Config.BodyHash is enabled, the request body hash is stored alongside the response; mismatches on replay return 422 Unprocessable Entity to catch clients that reuse a key with different payloads.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrStoreMissingSetNX = errors.New("idempotency: store must implement store.SetNXer")
ErrStoreMissingSetNX is returned by New when Config.Store is set but does not satisfy KVStore.
Functions ¶
Types ¶
type Config ¶
type Config struct {
// Store persists completed responses and lock entries. Default:
// [NewMemoryStore]. Must implement [store.KV] + [store.SetNXer].
Store KVStore
// KeyHeader is the request header carrying the client-supplied
// idempotency key. Default: "Idempotency-Key".
KeyHeader string
// TTL is the lifetime of a completed response entry. Default: 24h.
TTL time.Duration
// LockTimeout is the maximum duration a lock is held before it
// expires (recovers after a crashed handler). Default: 30s.
LockTimeout time.Duration
// Methods lists HTTP methods the middleware applies to. Default:
// POST, PUT, PATCH, DELETE. Methods outside this list pass through.
Methods []string
// OnConflict runs when a duplicate request arrives while the
// original is still in-flight (lock held, no completed entry yet).
// Default: respond with 409 Conflict.
OnConflict func(*celeris.Context) error
// MaxKeyLength is the upper bound for the key header value.
// Default: 255.
MaxKeyLength int
// BodyHash, when true, hashes the request body (up to MaxBodyBytes)
// and compares it to the stored hash on replay. Mismatches return
// 422 Unprocessable Entity. Default: false (hash checking disabled).
BodyHash bool
// MaxBodyBytes caps the bytes hashed for BodyHash and the bytes
// stored in a completed response. Default: 1 MiB.
MaxBodyBytes int
// Skip defines a function to skip this middleware for certain
// requests.
Skip func(*celeris.Context) bool
// SkipPaths lists paths to skip (exact match).
SkipPaths []string
// CleanupContext stops the MemoryStore cleanup goroutine (no effect
// on Redis/Postgres stores).
CleanupContext context.Context
}
Config defines the idempotency middleware configuration.
Example ¶
ExampleConfig — minimum useful configuration. Clients send `Idempotency-Key: <uuid>` on POST/PUT/DELETE; retries within the TTL replay the cached response and concurrent duplicates while the original is still in-flight return 409 Conflict. `store.MemoryKV` satisfies idempotency.KVStore (KV + SetNXer) out of the box.
package main
import (
"time"
"github.com/goceleris/celeris/middleware/idempotency"
"github.com/goceleris/celeris/middleware/store"
)
func main() {
_ = idempotency.Config{
Store: store.NewMemoryKV(),
TTL: 5 * time.Minute,
}
}
Output:
type KVStore ¶
KVStore combines store.KV with store.SetNXer — idempotency requires both. The in-memory default satisfies this out of the box. Backends without SetNX cannot be used as an idempotency store.
func NewMemoryStore ¶
func NewMemoryStore(config ...MemoryStoreConfig) KVStore
NewMemoryStore returns an in-memory idempotency store. It wraps store.MemoryKV, which implements both store.KV and store.SetNXer — the minimum surface New needs.
type MemoryStoreConfig ¶
type MemoryStoreConfig = store.MemoryKVConfig
MemoryStoreConfig is a type alias for store.MemoryKVConfig provided for symmetry with other middleware packages.