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 ¶
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 ¶
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.
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.