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 ¶
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 ¶
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.
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 ¶
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 ¶
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) Exists ¶
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 ¶
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 ¶
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.