idempotency

package
v0.2.0 Latest Latest
Warning

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

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

Documentation

Overview

Package idempotency makes a repeated write harmless.

The rule from api-guidelines.md §5: a POST carrying an Idempotency-Key is executed once, and every repeat of it gets the first answer back unchanged. That is what lets an automation platform or an agent retry a request whose answer it never saw - which, over a network, is every request that timed out.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Answer

type Answer struct {
	Status     int
	Body       []byte
	DetailCode string
}

Answer is what the operation produced, as far as the guard needs to know it: the status, the body to replay, and the detail code where the body is a problem document.

func (Answer) Replayable

func (a Answer) Replayable() bool

Replayable reports whether the answer is an outcome of the intent, which is what a repeat may be served from.

Two kinds are not. A 5xx is not a decision the server stands behind - it is one it may make differently next time. And auth.step_up_required is not a decision about the request at all: it says the request was not attempted for want of a proof, and the retry that carries the proof is, by the client's own account, the same intent under the same key. Replaying the demand at it would make every privileged action impossible from a client that keeps its key, which is exactly the client the key exists for (api-guidelines.md §5).

type Attempt

type Attempt struct {
	// Replay is set when a previous attempt already answered. The caller sends it as-is and does
	// not run the operation.
	Replay *repository.Record
}

Attempt is what the caller does next.

func (Attempt) IsReplay

func (a Attempt) IsReplay() bool

IsReplay reports whether the operation has already run.

type Guard

type Guard struct {
	Store      repository.Store
	UnitOfWork persistence.UnitOfWork
}

Guard reserves keys and stores answers.

func (Guard) Begin

func (g Guard) Begin(
	ctx context.Context,
	actor appshared.ActorContext,
	key repository.Key,
	requestHash []byte,
) (Attempt, error)

Begin claims the key for this request.

It runs in a transaction of its own, which commits before the operation starts. That is the whole mechanism: two requests arriving together race for the same insert, one wins, and the loser sees a reserved row rather than a second execution.

func (Guard) Complete

func (g Guard) Complete(
	ctx context.Context,
	actor appshared.ActorContext,
	key repository.Key,
	answer Answer,
) error

Complete stores the answer so the next repeat can be served from it - or, where the answer is not one to replay, lets the reservation go so the next repeat reaches the operation. Leaving the reservation standing would be worse than either: the repeat would be refused as in progress for as long as the record lives.

Jump to

Keyboard shortcuts

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