Documentation
¶
Overview ¶
Package engine orchestrates branch lifecycle as sagas over the registry, cow planner, and runtime driver. The CLI (P1) and branchd (P2) both embed it.
Index ¶
- Variables
- type DiffResult
- type Engine
- func (e *Engine) AddSource(ctx context.Context, s *registry.Source, password string) error
- func (e *Engine) BranchUsage(ctx context.Context, name string) (int64, error)
- func (e *Engine) CreateBranch(ctx context.Context, name, sourceName string, ttl time.Duration) (*registry.Branch, error)
- func (e *Engine) CreateBranchFrom(ctx context.Context, name, parentName string, ttl time.Duration) (*registry.Branch, error)
- func (e *Engine) DestroyBranch(ctx context.Context, name string) error
- func (e *Engine) DiffBranch(ctx context.Context, name string) (*DiffResult, error)
- func (e *Engine) ReapExpired(ctx context.Context, now time.Time) (destroyed []string, err error)
- func (e *Engine) Reconcile(ctx context.Context) error
- func (e *Engine) RefreshSource(ctx context.Context, name, password string) error
- func (e *Engine) RemoveSource(ctx context.Context, name string) error
- func (e *Engine) ResetBranch(ctx context.Context, name string) (*registry.Branch, error)
- func (e *Engine) RunReaper(ctx context.Context, interval time.Duration, ...)
- type Option
- type TableDelta
Constants ¶
This section is empty.
Variables ¶
var ErrInvalidName = errors.New("invalid branch name")
ErrInvalidName rejects branch names that cannot be used across runtimes (docker container names, k8s pod names — RFC 1123 after the pgbranch-br- prefix). The API maps it to 400.
Functions ¶
This section is empty.
Types ¶
type DiffResult ¶ added in v0.3.0
type DiffResult struct {
SchemaDiff string `json:"schema_diff"`
Tables []TableDelta `json:"tables"`
}
DiffResult is what changed in a branch relative to its base: a unified schema diff (pg_dump --schema-only of base vs branch; empty = identical) and per-table row-estimate deltas.
type Engine ¶
type Engine struct {
// contains filtered or unexported fields
}
func NewWithPlanner ¶
func NewWithPlanner(reg *registry.Registry, drv runtime.Driver, defaultImage string, planner cow.Planner, opts ...Option) *Engine
NewWithPlanner selects the copy-on-write backend (branchd --cow).
func (*Engine) BranchUsage ¶
BranchUsage measures a branch's copy-on-write layer in bytes (the branch's own writes, not the shared source data). Overlay: `du -sb` on the rw volume; zfs: the clone's `used` property (space unique to the clone). It is a helper-container roundtrip — cheap, but not free.
func (*Engine) CreateBranch ¶
func (e *Engine) CreateBranch(ctx context.Context, name, sourceName string, ttl time.Duration) (*registry.Branch, error)
CreateBranch is a saga: every step registers a compensation that runs (in reverse order) if a later step fails. No orphans, ever. ttl 0 means the branch never expires.
func (*Engine) CreateBranchFrom ¶
func (e *Engine) CreateBranchFrom(ctx context.Context, name, parentName string, ttl time.Duration) (*registry.Branch, error)
CreateBranchFrom creates a branch whose base is another (ready) branch's current state — branch-from-branch.
Overlay backend: a freeze saga. The parent's rw volume cannot be shared writable, so it is frozen into an immutable layer:
CHECKPOINT parent -> stop parent -> fresh parent rw volume -> restart parent on [frozen rw, …its old chain…, source] (wait ready) -> start child on the same chain -> commit (layer row + parent rw swap, atomic) -> child ready.
The parent gets a new container (and so possibly a new host port; the wire router resolves live, so dbname@parent connections just reconnect). On any failure before the commit the parent is restored to its original rw volume and chain and restarted; if even that fails it is marked failed — never half-frozen. The layer row is committed only after both restarts succeeded.
ZFS backend: block-level CoW — snapshot the parent's clone and clone that. No freeze, no stop, no layer rows.
CSI backend: the child's PVC is a clone of the parent's PVC. No freeze or layer rows either, but the parent is briefly stopped around the clone for crash consistency (see provisionCSI).
func (*Engine) DestroyBranch ¶
func (*Engine) DiffBranch ¶ added in v0.3.0
DiffBranch reports what changed in a ready branch relative to its base. It provisions an internal throwaway branch ("diff-<6 hex>") from the target's OWN base — the recorded source volume/generation and frozen-layer chain, not the source's current generation — then runs pg_dump --schema-only and a row-estimate query inside both instances over the local socket (no credentials involved, so rotated branch passwords don't matter) and diffs host-side. The throwaway is a normal registry row (TTL'd, so the reaper cleans strays if branchd dies mid-diff) and is destroyed before returning, success or not. Expect a few seconds of wall time: a full branch provision plus two dumps.
func (*Engine) ReapExpired ¶
ReapExpired destroys every ready/failed branch whose TTL has passed. Called by branchd's reaper loop; now is injected for testability.
func (*Engine) Reconcile ¶
Reconcile aligns the registry with reality at startup: stuck 'creating' branches are failed and their resources cleaned; managed containers with no registry row are removed.
func (*Engine) RefreshSource ¶
RefreshSource re-seeds a source into a fresh generation volume. Existing branches keep the volume they were created from; only new branches see the new generation. The previous generation's volume is GC'd once no live branch references it. A failed seed leaves the current generation intact.
func (*Engine) RemoveSource ¶
RemoveSource deletes a source's volume, its orphaned frozen layers, and the registry rows. Refused while any live branch still uses the source or (defensively) while any layer is still referenced.
func (*Engine) ResetBranch ¶
ResetBranch throws away a ready branch's writes and reprovisions it from its recorded source volume on the same registry row (ready -> resetting -> ready; new container id and host port).
func (*Engine) RunReaper ¶
func (e *Engine) RunReaper(ctx context.Context, interval time.Duration, logf func(format string, args ...any))
RunReaper destroys expired branches every interval until ctx is done. branchd runs it as a goroutine; logf (optional, nil = silent) receives destroy/error reports.
type Option ¶ added in v0.3.0
type Option func(*Engine)
Option configures optional engine behavior at construction time.
func WithCredentialRotation ¶ added in v0.3.0
func WithCredentialRotation() Option
WithCredentialRotation turns on per-branch credential rotation: every branch create and reset generates a fresh password, applies it inside the branch and stores it on the branch row (returned by the API as `password`).
type TableDelta ¶ added in v0.3.0
type TableDelta struct {
Table string `json:"table"`
BaseRows int64 `json:"base_rows"`
BranchRows int64 `json:"branch_rows"`
Delta int64 `json:"delta"`
}
TableDelta is one table's row-estimate comparison between a branch and its base. Counts come from pg_class.reltuples — planner estimates, not exact counts (fresh never-analyzed tables report 0).