Documentation
¶
Overview ¶
Package runs persists run state to a JSON file with flock protection.
Index ¶
- Variables
- type PrepareOutcome
- type Run
- type Status
- type Store
- func (s *Store) ActiveByMachine(name string) (*Run, bool)
- func (s *Store) Create(r *Run) error
- func (s *Store) FindByTestDirHashAndMachine(hash, machine string) ([]*Run, error)
- func (s *Store) Get(id string) (*Run, error)
- func (s *Store) List() ([]*Run, error)
- func (s *Store) PrepareAtomic(machine, hash string, mk func() *Run) (*Run, PrepareOutcome, error)
- func (s *Store) Update(r *Run) error
Constants ¶
This section is empty.
Variables ¶
var ErrRunNotFound = errors.New("run not found")
ErrRunNotFound is returned by Get / Update when the requested run ID does not exist in the store. It is a sentinel so callers can branch with errors.Is.
Functions ¶
This section is empty.
Types ¶
type PrepareOutcome ¶
type PrepareOutcome int
PrepareOutcome classifies the result of PrepareAtomic.
const ( // PrepareCreated: a fresh pending run was created (returned run is new). PrepareCreated PrepareOutcome = iota // PrepareReuse: an existing non-terminal run with the same (machine,hash) // was found — idempotency hit (returned run is the existing one). PrepareReuse // PrepareBusy: the machine already has a non-terminal run for a DIFFERENT // hash (returned run is the occupying run). PrepareBusy )
type Run ¶
type Run struct {
ID string `json:"id"`
Machine string `json:"machine"`
TestDir string `json:"test_dir"`
TestDirHash string `json:"test_dir_hash"`
Status Status `json:"status"`
ExitCode *int `json:"exit_code"`
StartedAt *time.Time `json:"started_at"`
EndedAt *time.Time `json:"ended_at"`
ArtifactsPath string `json:"artifacts_path"`
LogsPath string `json:"logs_path"`
ErrorCode string `json:"error_code"`
ErrorMsg string `json:"error_msg"`
// BatchID is the owning batch's ID when this run was dispatched as part of a
// batch; empty for single `run` dispatches. omitempty keeps v1 single-run
// JSON byte-identical for existing consumers.
BatchID string `json:"batch_id,omitempty"`
// PlanEntry is the plan entry name this run fulfills within its batch; empty
// for single dispatches.
PlanEntry string `json:"plan_entry,omitempty"`
}
Run is the persisted record for one test dispatch.
JSON field names are snake_case to match the public CLI/JSON contract. Pointer fields (ExitCode, StartedAt, EndedAt) are nil while the run is still pending, becoming non-nil as the lifecycle progresses; they serialize as `null` then.
type Status ¶
type Status string
Status is the lifecycle state of a Run.
String-backed so JSON serialization is human-readable and stable for the machine-readable contract. CLI tooling branches on these values.
func (Status) IsTerminal ¶
IsTerminal returns true when the run has reached a final state and will not transition further. Idempotency in the runner layer keys off this distinction: non-terminal (pending/running) re-dispatches dedupe; terminal re-dispatches create a fresh run.
type Store ¶
type Store struct {
// contains filtered or unexported fields
}
Store persists runs to a JSON file with flock-based cross-process locking.
The store is safe for concurrent use within a process (mu) and across processes (flock). Every public method takes the in-proc mutex and then acquires/releases the on-disk lock; this keeps the critical section bounded and avoids fd leaks.
func NewStore ¶
NewStore prepares the state directory and returns a Store rooted at it. The directory must already be createable (caller typically uses config.Config.StateDir which mkdir -p's with 0700).
func (*Store) ActiveByMachine ¶
ActiveByMachine returns the first run on the given machine whose status is pending or running. The bool is false when no such run exists.
MVP assumes a single active run per machine (enforced at the runner layer via machine_busy); if multiple ever exist, the earliest-inserted wins.
func (*Store) Create ¶
Create assigns a unique ID to r (overwriting any existing value) and appends it to the store.
func (*Store) FindByTestDirHashAndMachine ¶
FindByTestDirHashAndMachine returns every run that matches (hash, machine).
NOTE: the runner's preflight no longer calls this — the idempotency + busy check + Create now happen atomically inside PrepareAtomic (issue #10). This remains a standalone query for callers that want the full match set.
func (*Store) PrepareAtomic ¶
PrepareAtomic performs the machine-busy check, the idempotency check, and the pending-run Create in a SINGLE critical section. Two concurrent dispatchers (e.g. a foreground batch first-wave and a still-live detached worker, or two different batches targeting the same machine) can therefore never both create a run for the same machine — which previously left an orphan run unreferenced by any batch item (issue #10). mk is invoked only on the create path and must return the pending run to persist; its ID is assigned here.
This subsumes the older separate ActiveByMachine + FindByTestDirHashAndMachine sequence the runner used to call across distinct lock acquisitions: a non-terminal run on the machine is always observed here first (single-active- per-machine invariant), so a same-hash twin reuses and a different-hash run reports busy, all under one held lock.