source

package
v0.4.0 Latest Latest
Warning

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

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

Documentation

Overview

Package source defines the replication source contract. A source maps its native types into core.Schema and streams COLUMNAR batches (dataplane.Batch) through Reader.Next; it knows nothing about any sink. The contract is deliberately small: a source implements a handful of focused interfaces, and the driver registry resolves a spec's source kind into a concrete Source. Orchestration composes the interfaces it needs.

Row universe boundary (R6)

rowchange.Change is the INTERNAL CDC decode type (internal/rowchange): a binlog or JSON event is row-shaped, so the built-in decoders decode into it and the source boundary re-encodes to the wire batch. It is never part of this public contract — external plugins receive and produce dataplane.Batch only, and Go forbids importing internal/ from an external module anyway. There is no contradiction: the row universe is a private detail of the built-in decoders, and every public seam is columnar.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExpandTables added in v0.3.0

func ExpandTables(ctx context.Context, src Source, s *spec.Spec) ([]spec.Table, error)

ExpandTables returns the effective table list for a pipeline: the source's discovered tables when spec.Source.Postgres.Discover is set, else the spec's explicit list. A discovery pipeline lists no tables — the source enumerates them at boot — and each target is derived as "<sink namespace>.<relname>".

A derived-target collision (public.orders and analytics.orders both becoming ns.orders) is an error: two sources cannot share one target, and a silent pick would be data loss.

Types

type Capabilities

type Capabilities struct {
	Snapshot   bool
	ChunkQuery bool
	Stream     bool
	// MaxConnections is the upper bound on concurrent query connections
	// the source tolerates. 0 means no opinion (unlimited).
	MaxConnections int
	// Modes lists the synchronization modes this source supports.
	Modes []Mode
	// BeforeImage reports whether the source carries the row image that was
	// deleted on a DELETE. MySQL (binlog row) always does; Postgres only
	// per replica identity; Kafka does not (tombstones are null). Append
	// tables with onDelete: record depend on this.
	BeforeImage bool
	// MonotonicSequence reports whether the transport coordinate of a
	// message never reappears (Kafka offset, Kinesis sequence) — the
	// property append-idempotent relies on.
	MonotonicSequence bool
	// RecoverablePosition reports whether the source's position can be
	// used to resume exactly from where it left off. Sources without
	// replay (TCP firehose, webhooks) must declare false so the
	// coordinator does not attempt gapless recovery on them.
	RecoverablePosition bool
}

Capabilities declares what a source supports: snapshot via DBLog, chunk SELECT introspection, and/or replication streaming. It is registration data — static per kind, not per instance — so the driver registry holds it and the orchestrator reads it without opening a source.

type Chunk

type Chunk struct {
	Low  []any
	High []any
}

Chunk is a half-open primary-key range [Low, High). Low/High are tuples in PK column order.

type ChunkSource

type ChunkSource interface {
	// PK returns the primary key columns, in key order.
	PK() []string
	// Bounds returns the ordered chunk boundary keys.
	Bounds(ctx context.Context) ([][]any, error)
	// Scan reads one chunk, calling fn per row keyed by column name.
	Scan(ctx context.Context, ch Chunk, fn func(row map[string]any) error) error
}

ChunkSource is the chunk SELECT surface the snapshot orchestrator consumes. The SQL-backed chunkers implement it.

type Discoverer added in v0.3.0

type Discoverer interface {
	// Discover returns every table the connected user may SELECT, limited to
	// schemas when non-empty.
	Discover(ctx context.Context, schemas []string) ([]TableRef, error)
}

Discoverer is the optional source capability that lists the tables the pipeline should replicate when the spec omits an explicit table list (spec.Source.Postgres.Discover). The returned refs carry empty primary keys — Introspect derives them — and only Source is populated; the orchestrator derives Target from the sink namespace.

type IncrementalSource added in v0.3.0

type IncrementalSource interface {
	Incremental(ctx context.Context, t TableRef, cursor string, after string) (next string, rows []map[string]any, err error)
}

IncrementalSource is the optional source capability for tables in incremental mode (spec.Table.Mode == "incremental"): a cursor-bounded read with no replication slot. Incremental runs one pass and returns the new cursor value — the last row's cursor, or "" when no rows — plus the decoded rows. The cursor travels as a string; the source coerces it against the cursor column's type.

type Introspector

type Introspector interface {
	Introspect(ctx context.Context, t spec.Table) (core.TableRef, core.Schema, []core.Warning, error)
}

Introspector resolves one spec table into its ref and the SOURCE canonical schema — the shape as the source emits it, before any cast or metadata column is applied. The pipeline resolves the cast policy centrally (the sink is the cast executor), so the source never applies it. The warnings are source-level advisories; the cast warnings come from the resolver.

type Mode

type Mode uint8

Mode declares the synchronization mode a source supports.

const (
	// ModeCDC is log-based change data capture — the only mode implemented.
	ModeCDC Mode = iota
	// ModeIncremental is cursor-column incremental (e.g. updated_at). It
	// exists in the contract now so adding it later does not change
	// signatures. Not implemented: it misses deletes and needs a cursor
	// column; it serves sources without replication access (RDS without
	// grants, replicas without binlog).
	ModeIncremental
	// ModeBackfillOnly is a single snapshot with no stream. Declared in the
	// contract; not implemented.
	ModeBackfillOnly
)

type PartitionSource

type PartitionSource interface {
	// Partitions returns n contiguous ranges covering the table's whole
	// primary-key domain, in order (Partitions(ctx,1)[0] is always the
	// single unbounded range). Returns an error if the primary key shape
	// isn't supported for range partitioning (e.g. more than one column).
	Partitions(ctx context.Context, n int) ([]Chunk, error)
}

PartitionSource is the optional chunker capability that splits a table's primary-key domain into n contiguous, ordered ranges for worker partitioning (spec.Table.WorkerCount() > 1) — the SAME range values govern both the DBLog snapshot (each chunk falls within its owning partition's range) and live-stream routing, so a key can never switch partition ownership between the two phases. Not every ChunkSource implements this; the coordinator fails boot loudly, rather than silently skipping partitioning, when a table declares more than one worker and its source chunker doesn't support this.

type Positioner

type Positioner interface {
	// InitialPosition is the stream start for a first boot (no resume).
	InitialPosition(ctx context.Context) (position.Position, error)
	// ParsePosition decodes a stored cdc.position string.
	ParsePosition(s string) (position.Position, error)
}

Positioner resolves the stream start position: the first-boot start and the codec for a stored cdc.position.

type QuerySource

type QuerySource interface {
	// NewChunker builds the chunk SELECT source for one table.
	NewChunker(source, pk string, chunkSize int) (ChunkSource, error)
	// CloseQuery releases the source's query connection.
	CloseQuery() error
}

QuerySource is the SQL-backed surface of a relational source: the chunk SELECT source for one table and the query-connection lifecycle. MySQL and Postgres implement it; a stream source (kafka) does not. The source owns its query connection — opened at driver construction, released on CloseQuery — so the orchestration never touches *sql.DB.

type Reader

type Reader interface {
	SourceReader
	// Start begins replication from `from`. Called once before the first
	// Next. Sources that need a resume position (MySQL GTID set, Postgres
	// LSN, Kafka offsets) apply it here; sources without replay ignore it.
	Start(ctx context.Context, from position.Position) error
	// Next returns the next columnar batch, or (nil, nil) at a clean end.
	// The caller owns the returned batch and must Release() it.
	Next(ctx context.Context) (*dataplane.Batch, error)
	Close()
	// SetConfirmed installs a callback that returns the minimum position
	// committed to the sink across all tables. The Postgres reader uses
	// this to advance the slot's confirmed_flush_lsn only to the point
	// that has been durably written — never past it.
	SetConfirmed(f func() position.Position)
}

Reader is the replication reader surface a driver drives: the DBLog watermark surface plus the columnar pull stream. A Reader is a handle obtained from Source.Open; the stream is pulled via Next (M4: sources produce Arrow RecordBatches, one batch per call).

type Runtime

type Runtime struct {
	ServerID  uint32
	Heartbeat time.Duration
	Logger    *slog.Logger
}

Runtime carries the replication knobs a driver passes through to the source reader.

type SchemaSetter

type SchemaSetter interface {
	SetSourceSchemas(schemas map[string]core.Schema)
}

SchemaSetter is an optional Reader capability: the coordinator/runner holds the RESOLVED canonical schema (spec-declared for stream sources, cast-applied) and injects it after Open. The source boundary then gates on drift against the native row shape and encodes stable batches — never per-drain inference.

type Source

type Source interface {
	Streamer
	Positioner
	Introspector
}

Source is the minimal surface every source provides: it streams changes, resolves positions, and introspects schemas. It deliberately carries NO SQL shape; the SQL-only surface (the chunked snapshot SELECT) lives on QuerySource, which only relational sources implement, so a stream source never stubs methods it cannot do.

type SourceReader

type SourceReader interface {
	Synced() position.Position
	Master(ctx context.Context) (position.Position, error)
	// OpenWindow starts the DBLog window for chunkID: the reader captures
	// its own source watermark and, from now on, tags decoded events whose
	// position is strictly past that watermark InWindow for the chunk.
	// Events at or before the watermark are already reflected in the chunk
	// SELECT and must not be tagged. ClearWindow ends the window.
	OpenWindow(ctx context.Context, chunkID uint32)
	ClearWindow()
}

SourceReader is the reader surface the snapshot orchestrator needs: positions for the watermarks, and the window that tags events InWindow at decode time — so no event can escape the window by racing a channel pull.

type Streamer

type Streamer interface {
	Open(ctx context.Context, refs []TableRef) (Reader, error)
}

Streamer opens the replication reader over the pipeline's tables. The source owns its own connections; the reader carries no SQL shape.

type TableRef

type TableRef = core.TableRef

TableRef maps one source table to its target and primary key. It is an alias of the canonical core.TableRef — the pipeline-wide table identity.

Jump to

Keyboard shortcuts

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