instance

package
v0.1.0 Latest Latest
Warning

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

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

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

View Source
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

View Source
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 Clock

type Clock interface{ Now() time.Time }

Clock is the time source, injected so tests are deterministic.

func SystemClock

func SystemClock() Clock

SystemClock returns the real clock.

type Guard

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

Guard claims and holds this process's instance record.

func New

func New(st state.Store, opts Options) (*Guard, error)

New builds a Guard over the service's state store.

func (*Guard) Claim

func (g *Guard) Claim(ctx context.Context) (func(), error)

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.

func (*Guard) ID

func (g *Guard) ID() string

ID returns this instance's identifier, for diagnostics.

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.

Jump to

Keyboard shortcuts

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