progress

package
v0.3.1 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: 8 Imported by: 1

Documentation

Overview

Package progress defines the strategy-wide, machine-readable execution progress contract. It deliberately contains copy counters that native operations leave empty so copy-and-swap can implement the same contract.

Index

Constants

View Source
const FormatVersion = 3

FormatVersion identifies the snapshot contract. A consumer must reject a snapshot whose format_version it does not recognize rather than guess at field semantics. Adding a phase or operation value is a contract change and bumps this version, even when no field is added or renamed. Adding a field also bumps this version so strict consumers can detect the new shape.

Variables

View Source
var (
	// ErrNoActiveBuild is returned by CancelBuild when the tracker has no
	// concurrent index build in flight.
	ErrNoActiveBuild = errors.New("no concurrent index build is active")
	// ErrBuildNotRunning is returned by CancelBuild when the tracker has a
	// build but the server positively shows no statement running on its
	// backend: the build statement has not reached the server yet, or the
	// backend has already gone. PostgreSQL drops a cancel signal delivered
	// to an idle backend, so signalling now would silently do nothing; the
	// caller retries, or waits for the blocking executor call to return.
	ErrBuildNotRunning = errors.New("the concurrent index build's statement is not running on the server")
	// ErrBuildUnobservable is returned by CancelBuild when the server does
	// not expose the build backend's state — activity tracking is off, or
	// the backend is hidden from the tracker's role — so the tracker cannot
	// tell a running build from an idle backend. A signal sent blind could
	// be dropped on an idle backend while the caller reads nil as a cancel
	// that reached the build, so none is sent. The build is still
	// stoppable: an operator whose role can see the backend cancels it
	// directly, or the caller ends the context the build runs under.
	ErrBuildUnobservable = errors.New("the server does not expose the concurrent index build backend's state")
)

Functions

This section is empty.

Types

type Clock

type Clock interface {
	Now() time.Time
}

Clock supplies time to progress state so core executors remain deterministic.

type Detail

type Detail struct {
	Operation Operation `json:"operation,omitempty"`
	// Statement is the canonical, qualified SQL the executor is running for
	// the current step, never a rendered or prettified form. Terminal snapshots
	// retain it so observers can identify the statement that produced the outcome.
	Statement        string `json:"statement,omitempty"`
	ServerPhase      string `json:"server_phase,omitempty"`
	Active           bool   `json:"active"`
	Attempt          int    `json:"attempt,omitempty"`
	Work             *Work  `json:"work,omitempty"`
	CurrentLockerPID uint32 `json:"current_locker_pid,omitempty"`
}

Detail describes the operation currently executing.

type Operation

type Operation string

Operation is the current operation's execution class.

const (
	// OperationAdmitting is the pre-execution window in which a sequence's
	// steps are still being validated; no statement has run yet.
	OperationAdmitting Operation = "admitting"
	// OperationOptimistic is one bounded direct native attempt.
	OperationOptimistic Operation = "optimistic"
	// OperationBrief is a brief transactional sequence step.
	OperationBrief Operation = "brief"
	// OperationValidate is a constraint-validation scan.
	OperationValidate Operation = "validate-constraint"
	// OperationConcurrentIndex is a concurrent index build.
	OperationConcurrentIndex Operation = "concurrent-index-build"
)

type Phase

type Phase string

Phase is the overall execution phase.

const (
	// PhasePending means execution has not started.
	PhasePending Phase = "pending"
	// PhaseRunning means execution is active.
	PhaseRunning Phase = "running"
	// PhaseFinished means execution completed successfully.
	PhaseFinished Phase = "finished"
	// PhaseFailed means execution reached a terminal failure.
	PhaseFailed Phase = "failed"
)

type Snapshot

type Snapshot struct {
	FormatVersion int           `json:"format_version"`
	Phase         Phase         `json:"phase"`
	Step          int           `json:"step,omitempty"`
	TotalSteps    int           `json:"total_steps,omitempty"`
	Elapsed       time.Duration `json:"elapsed_ns"`
	StepElapsed   time.Duration `json:"step_elapsed_ns"`
	Detail        Detail        `json:"detail"`
}

Snapshot is one immutable progress observation. For a terminal phase the elapsed values are frozen at the instant Finish recorded, so a late poll reports the execution's duration, not the observation's age.

type Tracker

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

Tracker is a concurrency-safe progress source. The caller owns it; it has no goroutines. Progress performs the one read needed for an active index build, making polling lifetime identical to the caller's context.

Two locks split the tracker's concerns: mu guards the state fields and is held only for memory access, so the executor's own updates never wait for a database read; pollMu serializes observers, so the reserved session — a single pgx connection that is not safe for concurrent use — only ever carries one progress query at a time.

StopConcurrentBuild is the one state change that takes pollMu, because it is the fence between the build and the pool: the executor calls it before the build's session can be released, so an observation or cancel signal still in flight completes against a backend the build still owns. Start, StartStep and Finish also clear the build fields, but as resets under mu alone — by the time they run the build's step has already passed through StopConcurrentBuild, and a reset that waited behind an observation would make polling a gate on execution.

func NewTracker

func NewTracker(clock Clock) (*Tracker, error)

NewTracker constructs an idle tracker using clock.

func (*Tracker) CancelBuild added in v0.3.0

func (t *Tracker) CancelBuild(ctx context.Context) error

CancelBuild asks PostgreSQL to cancel the active concurrent build's statement, from the tracker's reserved session. The tracker is the only party that knows whether the build is still live, so the cancel is issued here rather than by handing out the backend PID: it serializes against StopConcurrentBuild, which the executor calls before the build's session can return to the pool, so the PID it signals still belongs to the build — never to an unrelated statement that reused the same pooled backend. The signal is sent only to a backend the server reports active in the same statement as the read, which rules out the common way a cancel is lost — a signal landing on an idle backend — without making the signal itself observable.

A nil return means pg_cancel_backend accepted the signal for a backend the same statement had just read as active. It does not mean the build has stopped, and it cannot rule out the build finishing in the instant between that read and the signal, in which case the signal lands on an idle backend and is dropped: the server acts on a cancel at its next interrupt check, and the build returns through the executor's normal path — failed with its catalog verdict, or finished — either way on the blocking executor call, not here. The caller's ctx gates whether the signal is attempted; the signal itself runs bounded and detached from it, because the session it rides on is the one the build's verdict needs intact.

The reserved session's role must be allowed to signal the build's backend: the same role, or a member of pg_signal_backend. Otherwise pg_cancel_backend raises an error rather than returning false, and that error is returned wrapped; it is a permanent condition of the role, not one a retry clears.

func (*Tracker) Finish

func (t *Tracker) Finish(err error)

Finish records a terminal execution outcome and the instant it happened; elapsed values in later snapshots freeze at that instant.

func (*Tracker) Now

func (t *Tracker) Now() time.Time

Now returns the tracker's injected time for executor duration accounting.

func (*Tracker) Progress

func (t *Tracker) Progress(ctx context.Context) (Snapshot, error)

Progress returns a snapshot and, for an active concurrent index build, queries PostgreSQL's progress view by the executor-owned backend PID. On a query error the snapshot still carries the last-known tracker state. The state lock is released before the query, so concurrent pollers serialize only against each other (and StopConcurrentBuild), never against the executor's own state updates.

func (*Tracker) SetAttempt

func (t *Tracker) SetAttempt(attempt int)

SetAttempt records the current bounded retry attempt.

func (*Tracker) SetConcurrentBuild

func (t *Tracker) SetConcurrentBuild(session dbconn.RowQuerier, pid uint32)

SetConcurrentBuild enables on-demand server progress for pid. The executor supplies its reserved verdict session so polling cannot starve behind the build session even when the pool has only two connections.

func (*Tracker) Start

func (t *Tracker) Start(total int, operation Operation)

Start records the beginning of an execution. It resets all per-execution state, so a reused tracker never leaks a prior run's step, terminal time, or session into the new run's snapshots.

func (*Tracker) StartStep

func (t *Tracker) StartStep(step int, operation Operation, statement string)

StartStep advances a sequence to a 1-based step, records the exact SQL the executor will run, and drops any build session from a prior step, so a later step can never poll a stale build.

func (*Tracker) StopConcurrentBuild

func (t *Tracker) StopConcurrentBuild()

StopConcurrentBuild waits for an in-flight observation or cancel signal and releases the reserved session back to the executor before its catalog verdict. It is the fence that keeps CancelBuild's target honest: the executor calls it before the build's own session can return to the pool, so no signal that read the build's PID completes after that backend could be running someone else's statement.

type WallClock

type WallClock struct{}

WallClock reads the process wall clock.

func (WallClock) Now

func (WallClock) Now() time.Time

Now returns the current wall-clock time.

type Work

type Work struct {
	RowsCopied   uint64 `json:"rows_copied"`
	RowsTotal    uint64 `json:"rows_total"`
	BytesCopied  uint64 `json:"bytes_copied"`
	BytesTotal   uint64 `json:"bytes_total"`
	BlocksDone   uint64 `json:"blocks_done"`
	BlocksTotal  uint64 `json:"blocks_total"`
	TuplesDone   uint64 `json:"tuples_done"`
	TuplesTotal  uint64 `json:"tuples_total"`
	LockersTotal uint64 `json:"lockers_total"`
	LockersDone  uint64 `json:"lockers_done"`
}

Work reports server-observed work. It is present only when the server published a progress row, and then every counter marshals explicitly — a fresh build reports honest zeros, never an empty object a consumer must guess at. Rows and bytes are reserved for copy-and-swap; native operations do not fabricate them.

Jump to

Keyboard shortcuts

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