Documentation
¶
Index ¶
- type CutOver
- type CutOverSource
- type CutoverResult
- type CutoverResultCallback
- type Move
- type ReverseFeed
- func (rf *ReverseFeed) AllChangesFlushed() bool
- func (rf *ReverseFeed) Close()
- func (rf *ReverseFeed) Err() error
- func (rf *ReverseFeed) Flush(ctx context.Context) error
- func (rf *ReverseFeed) Positions() []string
- func (rf *ReverseFeed) Run(ctx context.Context, window time.Duration) error
- func (rf *ReverseFeed) Start(ctx context.Context) error
- type ReverseFeedConfig
- type ReverseSource
- type Runner
- func (r *Runner) Cancel()
- func (r *Runner) Close() error
- func (r *Runner) DumpCheckpoint(ctx context.Context) error
- func (r *Runner) Progress() status.Progress
- func (r *Runner) Result() status.WorkflowResult
- func (r *Runner) Run(ctx context.Context) (retErr error)
- func (r *Runner) SetCutover(cutover func(ctx context.Context) error)
- func (r *Runner) SetCutoverWithResult(cutover CutoverResultCallback)
- func (r *Runner) SetLogger(logger *slog.Logger)
- func (r *Runner) SetMetricsSink(sink metrics.Sink)
- func (r *Runner) SetReverseCutover(fn func(ctx context.Context) error)
- func (r *Runner) SetReverseCutoverWithResult(fn CutoverResultCallback)
- func (r *Runner) Status() string
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type CutOver ¶
type CutOver struct {
// contains filtered or unexported fields
}
func NewCutOver ¶
func NewCutOver(sources []CutOverSource, cutoverFunc func(ctx context.Context) error, dbConfig *dbconn.DBConfig, logger *slog.Logger) (*CutOver, error)
NewCutOver creates a new CutOver that handles multiple sources.
func (*CutOver) SetCutoverWithResult ¶ added in v0.17.0
func (c *CutOver) SetCutoverWithResult(fn CutoverResultCallback)
SetCutoverWithResult installs a result-bearing forward cutover callback. It is mutually exclusive with the legacy error-only callback.
func (*CutOver) SetPostSwitch ¶ added in v0.16.0
SetPostSwitch registers a hook to run once under the source locks, after the traffic switch and before the source rename. See CutOver.postSwitch.
type CutOverSource ¶ added in v0.13.0
CutOverSource holds per-source state needed for the cutover.
type CutoverResult ¶ added in v0.17.0
CutoverResult reports authoritative evidence from a caller-owned cutover callback, including failures after a durable mutation and failures whose ownership outcome cannot be determined.
type CutoverResultCallback ¶ added in v0.17.0
type CutoverResultCallback func(context.Context) (CutoverResult, error)
CutoverResultCallback is the result-bearing cutover callback form.
type Move ¶
type Move struct {
// Each source/target *sql.DB owns a pool at this limit; worker counts do
// not grow it. Dedicated monitor/advisory pools are separate.
MaxConnections int `` /* 155-byte string literal not displayed */
SourceDSN string `name:"source-dsn" help:"Where to copy the tables from." default:"spirit:spirit@tcp(127.0.0.1:3306)/src"`
TargetDSN string `name:"target-dsn" help:"Where to copy the tables to." default:"spirit:spirit@tcp(127.0.0.1:3306)/dest"`
// TargetChunkSize is the in-memory byte budget the buffered copier sizes each
// copy chunk against (see table.DefaultTargetChunkBytes). Move always uses the
// buffered copier. A zero value means "use the default" (NewRunner fills it
// in). The Kong default below must stay equal to table.DefaultTargetChunkBytes.
TargetChunkSize uint64 `name:"target-chunk-size" help:"In-memory byte budget per copy chunk (in bytes)." default:"16777216"`
Threads int `name:"threads" help:"How many chunks to copy in parallel" default:"2"`
WriteThreads int `name:"write-threads" help:"How many concurrent write threads to use per target" default:"4"`
DeferCutOver bool `` /* 148-byte string literal not displayed */
DeferSecondaryIndexes bool `name:"defer-secondary-indexes" help:"Create target tables without secondary indexes, add them before cutover" default:"false"`
CheckpointMaxAge time.Duration `name:"checkpoint-max-age" help:"Maximum age of a checkpoint before refusing to resume from it" optional:"" default:"168h"`
// Force makes the runner wipe the target tables and start the copy fresh when
// it cannot resume from a checkpoint (e.g. the checkpoint is from an
// incompatible spirit version, or the target is in a state resume can't
// validate). Without it, an unresumable non-empty target is a hard error.
Force bool `` /* 152-byte string literal not displayed */
// ReverseWindow, when > 0, keeps the move alive after cutover in change-only
// reverse mode (targets→source) for this long, so the move can be rolled back
// before the source is retired. 0 (the default) is a normal cutover. During
// the window the source's now-retired _old tables are kept current from the
// targets; an operator rolls back by creating the _spirit_move_revert table on
// the first target (see revertmarker.go), otherwise the window elapses and the
// move finalizes forward. A sharded (multi-DSN) source additionally requires
// ReverseShardingProvider and SourceKeyRanges so the reverse feed can route
// rows back to the correct source shard — see the guard in Runner.Run. The
// data plane is ReverseFeed (reversefeed.go); the post-cutover driver is
// reverseWindow (reversewindow.go).
ReverseWindow time.Duration `` /* 166-byte string literal not displayed */
// SourceTables optionally specifies a list of tables to move.
// If empty, all tables in the source database will be moved.
// This is useful for Vitess MoveTables operations where only specific tables should be moved.
SourceTables []string
// ShardingProvider optionally provides vindex metadata for resharding operations.
// If nil, tables will not have vindex configuration (suitable for simple MoveTables 1:1 operations).
// For resharding operations (1:many), this should be set to provide sharding key information.
// The provider is called during table discovery to configure ShardingColumn and HashFunc
// on each TableInfo.
// SourceDSNs optionally specifies multiple source DSNs for N:M resharding operations.
// When set, each DSN represents a separate source shard. All sources must have identical
// table schemas. If empty, SourceDSN is used as the single source.
SourceDSNs []string `kong:"-"`
// SourceKeyRanges optionally specifies each source shard's Vitess-style key
// range ("-80", "80-", ...), parallel to SourceDSNs (SourceKeyRanges[i] is
// SourceDSNs[i]'s range). Required, together with ReverseShardingProvider,
// when ReverseWindow > 0 and the source is sharded (len(SourceDSNs) > 1):
// the reverse feed routes rows flowing back from the targets to the source
// shard whose range contains the row's hash. Unused otherwise.
SourceKeyRanges []string `kong:"-"`
ShardingProvider table.ShardingMetadataProvider `kong:"-"`
// ReverseShardingProvider provides the SOURCE keyspace's sharding metadata
// (vindex column + hash) for the reverse feed of a reverse-window move with
// a sharded source. It is consulted for each moved table when the window
// opens; a table without metadata is a hard error there, because reverse
// writes could not be routed to a source shard. Note the asymmetry with
// ShardingProvider, which describes the TARGET keyspace for the forward copy.
ReverseShardingProvider table.ShardingMetadataProvider `kong:"-"`
Targets []applier.Target `kong:"-"`
}
func (*Move) Validate ¶ added in v0.15.0
Validate is called by Kong after parsing to check for invalid flag values. Zero values mean "use the default" (NewRunner fills them in), so they are not rejected here; only explicitly-negative or otherwise invalid values are caught. Mirrors migration.Migration.Validate.
type ReverseFeed ¶ added in v0.16.0
type ReverseFeed struct {
// contains filtered or unexported fields
}
ReverseFeed is a running change-only reverse feed: one change.Source per ReverseSource, all sharing a single applier that writes to U (mirrors the forward move, where N sources share one applier).
func NewReverseFeed ¶ added in v0.16.0
func NewReverseFeed(ctx context.Context, cfg ReverseFeedConfig) (_ *ReverseFeed, err error)
NewReverseFeed wires the feeds and their shared applier. It does not open any binlog stream — call Start or Run for that — but it does query each source server once: change.NewAutoClient selects (and validates) the change-source coordinate scheme per source, so e.g. a GTID-set Position on a server that no longer has GTIDs enabled fails here with a clear error rather than as a stream failure at Start.
func (*ReverseFeed) AllChangesFlushed ¶ added in v0.17.0
func (rf *ReverseFeed) AllChangesFlushed() bool
AllChangesFlushed reports whether every feed has applied its whole buffer.
Flush returning nil is not the same question, and callers that are about to discard the buffer must ask this one instead. A drain may decline to finish — lock contention it could not resolve in its budget, or a drain cut short to bound how long it holds the flush mutex — and reports that by leaving the changes buffered rather than by erroring, because for the periodic flusher "try again next tick" is the right response and failing the whole change is not. Flush's own loop compounds it: it exits once the backlog is merely *trivial*, not empty, so a residual below that threshold returns nil by design.
Mirrors the forward cutover's check in cutover.go, which pairs the two calls for exactly this reason.
func (*ReverseFeed) Close ¶ added in v0.16.0
func (rf *ReverseFeed) Close()
Close stops periodic flush and closes all feeds. Safe to call more than once. The applier is never Started (the subscription apply path is synchronous), so there is nothing to Stop on it.
func (*ReverseFeed) Err ¶ added in v0.16.0
func (rf *ReverseFeed) Err() error
Err returns the first fatal feed error, if any.
func (*ReverseFeed) Flush ¶ added in v0.16.0
func (rf *ReverseFeed) Flush(ctx context.Context) error
Flush drains all feeds synchronously (e.g. before a health check or a reverse cutover, so U reflects everything written to the sources so far).
func (*ReverseFeed) Positions ¶ added in v0.16.0
func (rf *ReverseFeed) Positions() []string
Positions returns each source's current safe-to-resume position, in the same order as the configured sources. Intended for the caller's checkpoint so the window can resume in reverse mode after a restart.
func (*ReverseFeed) Run ¶ added in v0.16.0
Run opens the feeds and holds the rollback window for the given duration, keeping U current. It returns:
- nil when the window elapses normally (after a final flush);
- ctx.Err() if the context is cancelled;
- a fatal error if any feed dies (rollback is then unsafe and the caller must complete-forward).
Run does not Close the feeds; the caller does that after deciding the terminal action (complete-forward or roll back), since a reverse cutover needs the feeds flushed one last time first.
type ReverseFeedConfig ¶ added in v0.16.0
type ReverseFeedConfig struct {
Sources []ReverseSource
// Target is the U side when the former move source was a single database.
// Target.DB's default database MUST be U's schema (see ReverseSource.DB).
// Mutually exclusive with Targets.
Target applier.Target
// Targets is the U side when the former move source was SHARDED: one entry
// per former source shard, each with its Vitess-style key range set. Rows
// are routed by the WATCHED table's sharding metadata, so every
// ReverseSource table must have ShardingColumn and HashFunc set (the source
// keyspace's vindex) — NewReverseFeed fails otherwise. Each Targets[i].DB's
// default database MUST be that shard's schema (see ReverseSource.DB).
// Mutually exclusive with Target.
Targets []applier.Target
// TargetTables maps each watched (reverse-source) table NAME to the U-side
// TableInfo it is written to, built on Target.DB (with Targets, on any one
// shard: the schemas are identical and the name is unqualified, so each
// shard's own connection determines where the write lands). It is a map,
// not a slice, because the names can differ: after a forward cutover the
// source tables are renamed to their _old form, so a watched "t1" is
// written to "t1_old".
TargetTables map[string]*table.TableInfo
Logger *slog.Logger
DBConfig *dbconn.DBConfig
Threads int // applier write threads; 0 => default (4)
FlushInterval time.Duration // 0 => change.DefaultFlushInterval
}
ReverseFeedConfig configures a ReverseFeed.
type ReverseSource ¶ added in v0.16.0
type ReverseSource struct {
DB *sql.DB // connection to S (default DB == its schema)
Addr string // host:port for the binlog syncer
User string // binlog syncer user
Password string // binlog syncer password
Tables []*table.TableInfo // S-side tables to watch, built on DB
// Position is the opaque change.Source position to resume from (captured at
// cutover). Empty means start from the source's current head. Its encoding
// also selects the change-source coordinate scheme, exactly like a
// checkpoint resume (see change.NewAutoClient): a GTID set resumes through
// the GTID client (and requires the server to still have GTIDs enabled), a
// file:offset position through the binlog client, and the empty head-start
// case probes the server so the scheme matches what a cutover capture on
// that server would have produced.
Position string
}
ReverseSource is one reverse source: a former move target (shard S) whose binlog is tailed to keep the former move source (U) current.
DB's default database MUST be this source's schema. table.TableInfo.SetInfo resolves columns via information_schema WHERE table_schema=DATABASE() — the connection's default database, not the SchemaName argument — so a connection defaulting to the wrong schema silently loads the wrong table's definition, surfacing only at apply time as a fatal column-count mismatch.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
func (*Runner) DumpCheckpoint ¶
DumpCheckpoint is called approximately every minute. It writes the current state of the migration to the checkpoint table, which can be used in recovery. Previously resuming from checkpoint would always restart at the copier, but it can now also resume at the checksum phase.
func (*Runner) Result ¶ added in v0.17.0
func (r *Runner) Result() status.WorkflowResult
Result returns correctness evidence retained from the most recent Run invocation. It is intentionally separate from phase metrics.
func (*Runner) SetCutoverWithResult ¶ added in v0.17.0
func (r *Runner) SetCutoverWithResult(cutover CutoverResultCallback)
SetCutoverWithResult installs a result-bearing forward cutover callback. It is mutually exclusive with SetCutover; the most recent setter wins.
func (*Runner) SetMetricsSink ¶ added in v0.17.0
SetMetricsSink installs the destination for this run's metrics, including the workflow phase transitions reported by status.Tracker. It must be called before Run; a nil sink is ignored.
func (*Runner) SetReverseCutover ¶ added in v0.16.0
SetReverseCutover registers the legacy rollback traffic switch used if a revert is requested during the reverse window.
func (*Runner) SetReverseCutoverWithResult ¶ added in v0.17.0
func (r *Runner) SetReverseCutoverWithResult(fn CutoverResultCallback)
SetReverseCutoverWithResult installs the result-bearing reverse cutover callback. It is mutually exclusive with SetReverseCutover.
func (*Runner) Status ¶
Status returns the periodic report on the whole move: a header line plus one indented row per subsystem (see status.Block). It deliberately absorbs what used to be separate periodic lines from the change feeds (flushes, rotations) and the checkpoint dumper — see github.com/block/spirit/issues/329.