idempotency

package
v1.12.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: Apache-2.0 Imports: 6 Imported by: 0

Documentation

Overview

Package idempotency provides command deduplication for the dashboard contract: a Store interface shaped for the dispatcher, and InMemoryStore, which keeps its entries in a middleware.IdempotencyStore. That is the same store the HTTP idempotency middleware (forge.WithIdempotency) uses, so one backend can serve both. Wrappers around dispatcher.Dispatch consult the store before invoking command handlers and return cached envelopes when the (key, identity) tuple matches a recent invocation. InMemoryStore is also a Claimer: it holds a key while a command runs, through the shared store's own claims, so two overlapping dispatches with one key never both run it.

Index

Constants

DefaultMaxEntries is the default cap for the store NewInMemoryStore builds.

View Source
const SharedScope = "dashboard.command"

SharedScope namespaces dashboard command keys inside a shared store, so a dashboard command and an HTTP route never collide even on one backend.

Variables

View Source
var ErrClaimHeld = errors.New("idempotency: key is held by a running command")

ErrClaimHeld is what Claim's error wraps when ctx ended while another caller still held the key.

View Source
var ErrClaimLost = errors.New("idempotency: claim lapsed before it ended")

ErrClaimLost is what End's error wraps when the claim's lease lapsed before End, so the claim was swept or passed to another caller and End stored nothing. The error also wraps middleware.ErrIdempotencyNotHolder.

Functions

This section is empty.

Types

type Cached

type Cached struct {
	// Status is the HTTP status the original handler returned.
	Status int
	// WireBody is the JSON envelope the original handler produced, ready to
	// write back verbatim.
	WireBody json.RawMessage
	// StoredAt is when this entry landed in the store.
	StoredAt time.Time
	// TTL is how long the entry is considered fresh.
	TTL time.Duration
}

Cached is one cached command response.

func (Cached) Expired

func (c Cached) Expired(now time.Time) bool

Expired reports whether c is past its TTL relative to now.

type Claim added in v1.12.2

type Claim struct {
	// Cached is the entry already stored for the key.
	Cached *Cached
	// End ends a claim the caller holds. Pass the entry to store under the
	// claim, or nil to store nothing and give the key back. A caller that
	// holds a claim must call End exactly once. When the claim lapsed before
	// End, End stores nothing and its error wraps ErrClaimLost.
	End func(ctx context.Context, c *Cached) error
}

Claim is what Claimer.Claim found.

type Claimer added in v1.12.2

type Claimer interface {
	Store

	// Claim takes (key, identity) for the caller. While another caller holds
	// the key it waits for that claim to end, until ctx ends. Exactly one of
	// the returned Claim's fields is set: Cached when an entry is stored for
	// the key, End when the caller now holds it. When ctx ends with the key
	// still held, the error wraps ErrClaimHeld.
	Claim(ctx context.Context, key, identity string) (Claim, error)
}

Claimer is a Store that can also hold a key while a command runs, so two overlapping dispatches with the same key and identity never both run it. The dispatcher finds it by type assertion; a plain Store keeps working without it.

type InMemoryStore

type InMemoryStore struct {
	// contains filtered or unexported fields
}

InMemoryStore is the dashboard's Store as a view over a middleware.IdempotencyStore. NewInMemoryStore gives it a private in-memory backend; NewSharedStore lets it share one with the HTTP idempotency middleware. Safe for concurrent use.

func NewInMemoryStore

func NewInMemoryStore(opts ...Option) *InMemoryStore

NewInMemoryStore returns a Store backed by its own in-memory middleware.IdempotencyStore.

func NewSharedStore added in v1.12.2

func NewSharedStore(shared middleware.IdempotencyStore) *InMemoryStore

NewSharedStore returns a Store that keeps its entries in shared.

func (*InMemoryStore) Claim added in v1.12.2

func (s *InMemoryStore) Claim(ctx context.Context, key, identity string) (Claim, error)

Claim implements Claimer over the shared store's own Begin, Complete and Release, so a dashboard command's claim is the same kind of claim an HTTP request holds.

func (*InMemoryStore) Lookup

func (s *InMemoryStore) Lookup(ctx context.Context, key, identity string) (*Cached, bool)

Lookup implements Store. A miss briefly claims the key and releases it at once, so it leaves nothing behind.

func (*InMemoryStore) Store

func (s *InMemoryStore) Store(ctx context.Context, key, identity string, c Cached) error

Store implements Store. A TTL of zero or less never expires, matching Cached.Expired.

type Option

type Option func(*options)

Option configures NewInMemoryStore.

func WithMaxEntries

func WithMaxEntries(n int) Option

WithMaxEntries caps the number of cached entries; oldest are evicted first.

type Store

type Store interface {
	Lookup(ctx context.Context, key, identity string) (*Cached, bool)
	Store(ctx context.Context, key, identity string, c Cached) error
}

Store deduplicates command invocations by (key, identity) tuple. Lookup returns a cached envelope if one is present and unexpired; the dispatcher writes back the cached envelope verbatim when found. Implementations MUST be safe for concurrent use.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL