reserved

package
v0.30.38 Latest Latest
Warning

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

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

Documentation

Overview

Package reserved implements the reserved-commit facility (@D05b): the engine-level lifecycle that cal proposals, bal holds, blob leases, and entity staged writes are all instances of. Participants supply only a conflict predicate and a weight policy; the facility owns the tentative-row convention, the state walk, and the sweep.

The convention

A guard-bearing table adopts three columns (see sql.go for the DDL fragment):

txn_id           TEXT     NULL      -- owning transaction; set at reserve
state            TEXT     NOT NULL DEFAULT 'committed'
reserve_deadline INTEGER  NULL      -- unix nanoseconds; set while reserved

Only two states are ever persisted: 'committed' (the default — every ordinary row) and 'reserved' (a tentative row inside its TTL window). The four terminal outcomes of §D05b's walk

reserved → confirmed | released | expired | invalidated

are transitions, not resting states: Confirm CAS-flips the row to 'committed'; Release deletes it; the sweeper deletes it after its deadline (expired); and under optimistic weight a competing transaction's confirmation is what makes the loser's own confirmation fail (invalidated) — the loser learns its fate at its next CAS, and its rows are released by its coordinator or collected at TTL. bal's journal state column (@B10) is this convention's first schema instance, promoted here to substrate-wide practice.

The two weights

Each participant declares how its reserved rows count in admission:

  • Pessimistic (2PS semantics): reserved rows are honoured like real commits by every guard — a reserved debit reduces available balance, a reserved slot refuses competitors — until confirmation or expiry.
  • Optimistic (3PS semantics): reserved rows are invisible to admission; conflicting reservations coexist, and the first confirmer wins.

Deadline authority

The deadline is authoritative; the sweeper is only hygiene. Guard predicates compare reserve_deadline inline (see GuardPredicate), so a lapsed reservation stops counting the instant it expires and cannot be confirmed even while unswept. This is what makes coordinator death survivable: an abandoned reservation self-releases by clock, not by cleanup.

Doctrine: guards never use accelerators

Enforcement reads go to the guard plane — the SQL tables carrying this convention — always. In-memory accelerators are commit-fed and serve application traversal only; a commit-fed accelerator answering a tentative-aware question would answer wrongly (@D05c tier 1, and the G-12 lesson written down as law).

Visibility taxonomy (@D05c)

tier 1  guard plane      tentative-aware, mandatory (per weight)
tier 2  advisory planes  weight-optional, sweep-cleaned
tier 3  analytic planes  commit-fed, strictly

See VisibilityTier and PredicateFor.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ApplicationPredicate

func ApplicationPredicate() string

ApplicationPredicate returns the SQL fragment for application (non- guard) reads: committed rows only, unconditionally. Application reads never see reservations regardless of weight (@D05b visibility rule).

func ConventionColumns

func ConventionColumns() string

ConventionColumns returns the three column definitions of the tentative-row convention, for embedding in a participant table's CREATE TABLE. The state default is 'committed' so that every row written outside the reserve path is real by construction — adopting the convention changes nothing for existing writers.

func ConventionIndexes

func ConventionIndexes(prefix, table string) string

ConventionIndexes returns CREATE INDEX statements for a participant table: a partial index over reserved rows by txn_id (the CAS transitions' access path) and one by deadline (the sweeper's). prefix follows the house table-prefix pattern; table is the full table name.

func Deadline

func Deadline(now ot.Instant, ttl time.Duration) int64

Deadline computes a reservation deadline as now + ttl, in the canonical persisted representation (unix nanoseconds, UTC). A non-positive ttl yields a deadline already in the past, which the guard plane treats as lapsed immediately — reserving with one is a caller error that the clock, not a validation branch, punishes.

func GuardPredicate

func GuardPredicate(w Weight) (fragment string, binds int)

GuardPredicate returns the SQL fragment a guard-plane read appends to its WHERE clause, per the participant's declared weight. The caller binds nowNano (unix nanoseconds) once for the pessimistic form; the optimistic form takes no bind.

Pessimistic: committed rows plus reserved rows still inside their window — a live reservation is honoured like a commit. The deadline comparison is inline because the deadline is authoritative: a lapsed reservation stops counting here, immediately, sweeper or no sweeper.

Optimistic: committed rows only — reservations are invisible to admission, and conflicts resolve at confirmation (first confirmer wins).

func PredicateFor

func PredicateFor(tier VisibilityTier, w Weight) (fragment string, binds int)

PredicateFor returns the read predicate for a visibility tier (@D05c). TierGuard defers to the weight; TierAdvisory with a pessimistic weight may ingest live reservations (and thereby joins the sweeper's cleanup obligations); TierAnalytic is commit-fed, strictly.

func Release

func Release(ctx context.Context, q Querier, table, txnID string) (int64, error)

Release deletes every reserved row owned by txnID in table — the explicit return of a reservation. Committed rows are untouched: release after a successful confirm is a no-op, not a rollback. Returns the number of rows released.

Types

type Outcome

type Outcome int

Outcome classifies the result of a Confirm attempt. Exactly one outcome is returned per attempt; OutcomeConfirmed is the only success.

const (
	// OutcomeConfirmed: the CAS fired — the reservation's rows are now
	// committed.
	OutcomeConfirmed Outcome = iota

	// OutcomeAlreadyConfirmed: the rows are already committed under
	// this txn_id — an idempotent retry of a confirmation that won.
	OutcomeAlreadyConfirmed

	// OutcomeExpired: the reservation's deadline lapsed before the CAS.
	// The deadline is authoritative: this outcome is returned even if
	// the sweeper has not yet collected the rows.
	OutcomeExpired

	// OutcomeGone: no rows carry this txn_id — the reservation was
	// released, invalidated and swept, or never existed here.
	OutcomeGone
)

func Confirm

func Confirm(ctx context.Context, q Querier, table, txnID string, now ot.Instant) (Outcome, int64, error)

Confirm CAS-flips every live reserved row owned by txnID in table to committed:

UPDATE <table> SET state='committed', reserve_deadline=NULL
 WHERE txn_id=? AND state='reserved' AND reserve_deadline > <now>

txn_id is retained on the committed rows, which is what makes retries classifiable (OutcomeAlreadyConfirmed) and gives the audit trail its thread. Rows-affected is checked; when the CAS does not fire, the outcome is classified — never guessed:

  • rows exist, reserved, deadline lapsed → OutcomeExpired
  • rows exist, committed under this txn → OutcomeAlreadyConfirmed
  • no rows carry this txn_id → OutcomeGone

The deadline comparison makes expiry authoritative at the moment of confirmation: a reservation past its window cannot be confirmed even if the sweeper has not run.

func (Outcome) String

func (o Outcome) String() string

String returns the outcome's name for logs and errors.

type Querier

type Querier interface {
	ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
	QueryContext(ctx context.Context, query string, args ...interface{}) (*sql.Rows, error)
	QueryRowContext(ctx context.Context, query string, args ...interface{}) *sql.Row
}

Querier is the subset of database/sql shared by *sql.DB and *sql.Tx. Every transition in this file takes a Querier so that participants can run it inside their own transactions — the guard-locality law (@C04a): a guarded transition's read and write commit together.

type State

type State string

State is the persisted lifecycle state of a row under the convention. Only StateCommitted and StateReserved are ever stored; the terminal outcomes of the reserved walk are transitions (see Outcome).

const (
	// StateCommitted marks an ordinary, fully real row — the column
	// default, and the only state application reads ever see.
	StateCommitted State = "committed"

	// StateReserved marks a tentative row inside its TTL window, owned
	// by the transaction named in txn_id.
	StateReserved State = "reserved"
)

func ReserveValues

func ReserveValues(txnID string, deadlineNano int64) (string, State, int64)

ReserveValues returns the column values a participant's reserve-path INSERT binds for the convention columns: (txn_id, state, reserve_deadline). The INSERT itself belongs to the participant — the facility does not know its table shape — but the tentative marking is uniform.

type Sweeper

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

Sweeper collects lapsed reservations from every registered adopter of the tentative-row convention. It implements gc.Sweeper and plugs into the existing GC worker abstraction.

The sweeper is hygiene, not enforcement: the deadline is authoritative (guard predicates and Confirm compare reserve_deadline inline), so a lapsed reservation has already stopped counting everywhere before the sweeper ever sees it. What the sweep reclaims is storage and index space — finiteness (@D04b), not correctness.

func NewSweeper

func NewSweeper() *Sweeper

NewSweeper creates an empty Sweeper. Adopting tables register with Register; a sweeper with no registrations sweeps nothing and reports zeroes, which is valid (gc.Report documents zero values as such).

func (*Sweeper) Register

func (s *Sweeper) Register(db *sql.DB, table string)

Register adds a convention-adopting table to the sweep set. Safe for concurrent use with Sweep.

func (*Sweeper) Sweep

func (s *Sweeper) Sweep(ctx context.Context) (gc.Report, error)

Sweep deletes reserved rows whose deadline has lapsed, across every registered table. Per-table failures are counted in Report.Errors and do not abort the cycle — one adopter's trouble must not starve the others' hygiene. Examined counts reserved rows seen; Collected counts rows deleted. Duration is stamped by the gc.Worker.

type VisibilityTier

type VisibilityTier int

VisibilityTier names the three planes of the @D05c taxonomy. It exists so that read paths can declare which tier they serve and take their predicate from PredicateFor rather than hand-rolling one.

const (
	// TierGuard is the guard plane: tentative-aware, mandatory. Sees
	// committed rows plus live reserved rows per the declared weight.
	TierGuard VisibilityTier = iota

	// TierAdvisory covers derived availability-type structures (cal's
	// occupancy index). May ingest reservations when the participant's
	// weight is pessimistic; correctness never depends on this tier.
	TierAdvisory

	// TierAnalytic covers rollups, FTS, events, caches, and the
	// in-memory graph: commit-fed, strictly. A reserved row is
	// invisible here; it becomes visible at confirm.
	TierAnalytic
)

type Weight

type Weight int

Weight is a participant's declared admission policy for its reserved rows (@D05b "the two weights").

const (
	// Pessimistic gives 2PS semantics: guards honour reserved rows like
	// commits until confirmation or expiry.
	Pessimistic Weight = iota

	// Optimistic gives 3PS semantics: guards ignore reserved rows;
	// conflicting reservations coexist and the first confirmer wins.
	Optimistic
)

func (Weight) String

func (w Weight) String() string

String returns the weight's name for logs and errors.

Jump to

Keyboard shortcuts

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