dispatch

package
v0.2.16 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: Apache-2.0 Imports: 11 Imported by: 0

Documentation

Overview

Package dispatch is the controller's persistent record of in-flight dispatches. Every offer, claim, and terminal report appends an event to a JSONL write-ahead log, so a controller restart knows which dispatches were outstanding instead of losing all state (the v0.1 failure mode).

Lifecycle: offered -> claimed -> [started] -> resolved (done/failed/lost/expired). Offered records do not survive a restart: no credential was minted, the job is still queued on GitHub, and the poller will re-emit it. Claimed records do survive: a runner may exist, so the server's sweeps decide their fate.

Index

Constants

View Source
const (
	StateOffered = "offered"
	StateClaimed = "claimed"
	// StateStarted is a claimed dispatch whose runner has taken a job. GitHub
	// gives a JIT runner any queued job with matching labels, so the offered
	// job may still be queued: a started record holds its slot but no longer
	// holds that job.
	StateStarted = "started"
)

States a live record can be in.

Variables

View Source
var ErrDuplicate = errors.New("dispatch already live")

ErrDuplicate is returned by Offer when the job already has a live dispatch. Callers must distinguish it from WAL I/O errors: a duplicate means the offer was rejected, any other error means the record is live but unpersisted.

Functions

func DefaultPath

func DefaultPath() string

DefaultPath is where the WAL lives when config doesn't override it.

Types

type Ledger

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

Ledger tracks live dispatch records, optionally backed by a JSONL WAL.

func Open

func Open(path string) (*Ledger, error)

Open loads the ledger at path, replaying the WAL and compacting it down to the surviving records. Offered records are dropped during replay (see the package comment). An empty path yields a purely in-memory ledger.

func (*Ledger) Claim

func (l *Ledger) Claim(id string, at time.Time, wantOrgs []string) (Record, bool, error)

Claim transitions an offered record to claimed, returning it. ok is false if the record is unknown, not in the offered state (double claim), or owned by another tenant (wantOrg set and mismatched).

func (*Ledger) Close

func (l *Ledger) Close() error

Close closes the WAL file, if any.

func (*Ledger) ExpireOffer

func (l *Ledger) ExpireOffer(id string) bool

ExpireOffer removes id only if it is still offered (see Store.ExpireOffer).

func (*Ledger) Offer

func (l *Ledger) Offer(r Record) error

Offer records a new offered dispatch. Returns ErrDuplicate if the ID is taken or the job already has an unstarted dispatch under another ID. IDs embed the agent name (poll source) or are random (scaleset), so a re-emitted job offered to a different agent would otherwise slip past an ID-only check and double-provision.

func (*Ledger) Resolve

func (l *Ledger) Resolve(id, status string, wantOrgs []string) (Record, bool, error)

Resolve removes a record in any live state, returning it. ok is false if the record is unknown (already resolved, or a report for a pre-restart offered record) or owned by another tenant (wantOrg set and mismatched).

func (*Ledger) Snapshot

func (l *Ledger) Snapshot() []Record

Snapshot returns a copy of every live record.

func (*Ledger) Start added in v0.2.13

func (l *Ledger) Start(id, agent string, wantOrgs []string) (Record, bool, error)

Start transitions a claimed record to started, returning it. ok is false if the record is unknown, not claimed, or owned by another agent or tenant.

type Record

type Record struct {
	ID        string     `json:"id"`
	Agent     string     `json:"agent"`
	State     string     `json:"state"`
	Pending   domain.Job `json:"pending"`
	OfferedAt time.Time  `json:"offered_at"`
	ClaimedAt time.Time  `json:"claimed_at,omitzero"`
}

Record is one in-flight dispatch. ID is the runner name (fixed at offer time, before any mint) and doubles as the agent's slot handle.

type Store

type Store interface {
	Offer(r Record) error
	// Claim and Resolve take wantOrgs to scope the operation to one tenant: a
	// non-empty wantOrgs only matches a record whose org is one of them (case-
	// insensitively), so an enrolled agent can't claim or resolve another
	// tenant's dispatch by its (guessable) id. Empty wantOrgs skips the check
	// (OSS single-host / sweeps).
	Claim(id string, at time.Time, wantOrgs []string) (Record, bool, error)
	// Start marks a claimed dispatch's runner as working, which frees its
	// offered job to be offered again (see StateStarted). Only the agent that
	// holds the dispatch may start it: another agent of the same tenant would
	// otherwise free a booting runner's job for a second dispatch.
	Start(id, agent string, wantOrgs []string) (Record, bool, error)
	Resolve(id, status string, wantOrgs []string) (Record, bool, error)
	Snapshot() []Record
	// ExpireOffer removes a record only if it is still in the offered state,
	// returning true if it did. State-guarded so a claim landing between a
	// sweep's snapshot and its expiry call can't delete a live claimed record.
	ExpireOffer(id string) bool
}

Store is the dispatch persistence the control plane depends on. *Ledger is the in-memory + JSONL implementation used by the OSS single-host controller; the hosted control plane swaps in a Postgres-backed implementation with the same contract, so the server never knows which is behind it.

Directories

Path Synopsis
Package pg is a Postgres-backed dispatch.Store for the hosted control plane.
Package pg is a Postgres-backed dispatch.Store for the hosted control plane.

Jump to

Keyboard shortcuts

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