Documentation
¶
Overview ¶
Package instance makes the single-instance constraint visible.
decisions.md #16 permits exactly one service instance against one state Space: the state backend (OpenCloud storage over CS3) has no compare-and-set, so the run lease cannot be made mutually exclusive between processes. Until now that was a sentence in a document, and the shipped deployment violated it on every rolling update.
A guard writes a short-lived record announcing itself and refuses to start while another instance's record is still live. **This is not a lock.** Two processes starting at the same moment can both read "nobody there" and both write; the same missing primitive that stops the lease from being a lock stops this from being one. What it does catch is the common case — a second instance starting while the first is running, which is exactly what a rolling update does — and it turns "unsupported" into "refused".
Index ¶
Constants ¶
const ( // DefaultTTL is how long a record stays live without renewal. Short, because // its only job is to answer "is somebody else running right now"; a stale // record delays a legitimate restart by at most this long. DefaultTTL = 2 * time.Minute )
Variables ¶
var ErrAnotherInstance = errors.New(
"instance: another instance of this service is running against this state space; " +
"only one is supported (decisions.md #16) — deploy with strategy Recreate")
ErrAnotherInstance reports that a live instance record belongs to someone else. It is a startup error: two instances against one state Space can mark each other's runs failed and dispatch the same Space twice.
Functions ¶
This section is empty.
Types ¶
type Guard ¶
type Guard struct {
// contains filtered or unexported fields
}
Guard claims and holds this process's instance record.
func (*Guard) Claim ¶
Claim announces this instance and returns the function that stands it down. It returns ErrAnotherInstance when someone else's record is still live.
An unreadable record counts as live: a state store that cannot be understood is not evidence that nobody is there, and refusing to start is the recoverable direction.
type Options ¶
type Options struct {
// TTL is the record lifetime; zero uses DefaultTTL.
TTL time.Duration
// ID identifies this process; zero generates one.
ID string
// Clock is injected for deterministic tests.
Clock Clock
// Logger receives diagnostics. It never sees key material.
Logger *slog.Logger
// After schedules renewals. Tests inject a channel they control.
After func(time.Duration) <-chan time.Time
}
Options configures a Guard. The zero value is usable.