checkpoint

package
v0.17.0 Latest Latest
Warning

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

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

Documentation

Overview

Package checkpoint owns the checkpoint table shared by all three runners (migration, move, datasync): its schema, lifecycle (create / drop / exists), and row read/write. A checkpoint records where the row copy and (for migrate/move) the checksum got to, plus the change-feed position, so an interrupted run can resume instead of starting over.

The table holds a single row, overwritten in place each dump (REPLACE on id=1), and resume reads it. Two Modes cover the runners' differing lifecycles — Transient (one finite run) and Persistent (a continuous run); see Mode. The package owns the table mechanics; callers own the interpretation of the watermarks and the resume policy (staleness max-age, statement match, table-name collision, and — for datasync — using Exists as the resume signal) via the returned Record, Age, and Exists.

Index

Constants

This section is empty.

Variables

View Source
var ErrNotFound = errors.New("checkpoint: no checkpoint found")

ErrNotFound is returned by ReadLatest when the table holds no checkpoint row to resume from (it is empty). It is a normal state, distinct from a read failure.

Functions

func IsIncompatible

func IsIncompatible(err error) bool

IsIncompatible reports whether err means the checkpoint table can't be read with this spirit version's schema — a missing column (ER_BAD_FIELD_ERROR, what a table written by an incompatible version looks like) or the table having vanished (ER_NO_SUCH_TABLE). Callers use it to tell an unusable checkpoint (recover / start fresh) apart from a transient read failure (permission, server gone), which must not be mistaken for "no checkpoint".

Types

type Mode

type Mode int

Mode selects how a Table is created, matching spirit's checkpoint lifecycles. Either way Write keeps a single row (REPLACE on id=1) and ReadLatest reads it.

const (
	// Transient is a checkpoint for one finite run: it lives only as long as the
	// migration/move that owns it. Create DROPs and recreates the table (so it
	// always matches this version's schema and carries no stale rows) and Drop
	// drops it. Used by single-table and atomic multi-table migrations (only one
	// of the latter runs per schema at a time, so the shared _spirit_checkpoint
	// has a single owner) and by move.
	Transient Mode = iota
	// Persistent is a checkpoint for a continuous, never-ending run (datasync):
	// the table outlives any single run, so Create is idempotent and never
	// clears, and the caller decides fresh-vs-resume from Exists and ReadLatest
	// (not by recreating the table) — Create must never destroy the rows a
	// resume needs. Drop drops it.
	Persistent
)

type Record

type Record struct {
	// CopierWatermark is the row-copy resume point.
	CopierWatermark string
	// ChecksumWatermark is the checksum resume point ("" when not in / not past
	// the checksum phase, or deliberately blanked to force re-verification).
	ChecksumWatermark string
	// Position is the change-feed resume position: a single opaque position for
	// a single source (migration), or a JSON map of per-source positions for a
	// multi-source move. Stored in the binlog_position column.
	Position string
	// Statement is the migration's ALTER statement, which the runner uses to
	// match a resume to its checkpoint (ErrMismatchedAlter). Empty for move and
	// datasync.
	Statement string
	// OriginalTableName is the untruncated source table name, stored so a
	// single-table migration can detect the rare case where two long table
	// names truncate to the same checkpoint table name. Empty otherwise.
	OriginalTableName string
	// Phase is the move's reverse-window lifecycle: "" (copying — the default,
	// and the only value migration/datasync ever use), "reverse_window" (forward
	// cutover done, reverse feed live), or "reverting" (reverse cutover under
	// way). It gates resume so a restart neither re-copies nor re-cuts-over.
	// Stored in move_phase.
	Phase string
	// CutoverAt is when the forward cutover completed, used to compute the
	// reverse-window deadline across a resume. Zero when not past cutover; stored
	// in cutover_at as an RFC3339 string ("" when zero).
	CutoverAt time.Time
	// CreatedAt is when the row was written (UTC; spirit connections use
	// time_zone="+00:00").
	CreatedAt time.Time
}

Record is one checkpoint row. Watermarks and Position are opaque strings the caller produces and consumes; this package never interprets them.

func (Record) Age

func (r Record) Age() time.Duration

Age reports how long ago the checkpoint was written. Callers compare it to their configured max age to decide whether replaying from here is worthwhile.

type Table

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

Table manages a checkpoint table named name on db. Operations are unqualified and run against db's currently-selected schema (DATABASE()), so callers point the connection at the right schema rather than passing one — this is what lets it work under Vitess, and it matches pkg/sentinel. mode selects its create / clear / read behaviour; see Mode.

func NewTable

func NewTable(db *sql.DB, name string, mode Mode) *Table

NewTable returns a handle to the checkpoint table name on db (in db's selected schema), with the lifecycle selected by mode.

func (*Table) Create

func (t *Table) Create(ctx context.Context) error

Create prepares the checkpoint table for a run. Behaviour depends on Mode:

  • Transient (single-table / atomic multi-table migration, move): DROP + CREATE, so the table always matches this spirit version's schema and carries no stale rows. Safe for the shared multi-table _spirit_checkpoint because only one multi-table migration runs per schema at a time.
  • Persistent (datasync): CREATE IF NOT EXISTS and nothing else. The table is long-lived and its existence is the caller's resume signal, so it is never dropped or cleared here.

func (*Table) Drop

func (t *Table) Drop(ctx context.Context) error

Drop removes this run's checkpoint (DROP TABLE IF EXISTS).

func (*Table) Exists

func (t *Table) Exists(ctx context.Context) (bool, error)

Exists reports whether the checkpoint table exists in db's selected schema (DATABASE()). datasync uses this as its resume signal: the table is created before any rows are copied, so its presence means a prior run already owns the target — even one that died before writing its first checkpoint row. Because it keys on DATABASE(), the connection must have the target schema selected (it cannot be a no-database admin connection).

func (*Table) ReadLatest

func (t *Table) ReadLatest(ctx context.Context) (Record, error)

ReadLatest returns the most recent checkpoint row, or ErrNotFound when there is none.

Columns are selected explicitly (not SELECT *): a checkpoint table written by an incompatible spirit version that is missing a column surfaces as a read error, so resume fails safely rather than silently misreading.

func (*Table) Write

func (t *Table) Write(ctx context.Context, rec Record) error

Write records a checkpoint row, keeping a single row by overwriting it in place (REPLACE on the fixed primary key id=1). The table has one logical owner, so there is no value in accumulating history, and REPLACE is one atomic statement — a crash mid-write leaves either the old row or the new one, never none. created_at is (re)assigned by the server on each write.

Jump to

Keyboard shortcuts

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