lifecycle

package
v0.1.0-proto2m Latest Latest
Warning

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

Go to latest
Published: Aug 8, 2026 License: MPL-2.0 Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	StateInitializing = "initializing"
	StateInitialized  = "initialized"
	StateStarting     = "starting"
	StateRunning      = "running"
	StateStopping     = "stopping"
	StateStopped      = "stopped"
	StateReloading    = "reloading"
	StateRestarting   = "restarting"
	StateError        = "error"
)

Variables

View Source
var DepCtxKey = depCtxKey{}

DepCtxKey is used to store the call stack for cycle detection

Functions

This section is empty.

Types

type Action

type Action string
const (
	ActionInitialize Action = "initialize"
	ActionStart      Action = "start"
	ActionStop       Action = "stop"
	ActionRestart    Action = "restart"
	ActionReload     Action = "reload"
)

type Checkpointer

type Checkpointer interface {
	// Checkpoint writes a checkpoint of the runner's process into dir,
	// which must already exist.
	//
	// On success the process is STOPPED. That is deliberate: the image
	// is the rollback artifact for a migration, and a source that keeps
	// executing past the point its image captured has already diverged
	// from it.
	Checkpoint(ctx context.Context, dir string) error

	// Restore recreates the process from the checkpoint in dir and
	// adopts it, so the runner's Pid reports the restored process and
	// Stop can terminate it.
	//
	// The restored process is not a child of this program - CRIU spawns
	// it - so it is tracked by pid and reparents to the supervisor's
	// subreaper, which is how its eventual exit is observed.
	Restore(ctx context.Context, dir string) error
}

Checkpointer is an optional capability for runners whose process can be checkpointed and restored with CRIU (GOBLIN-DIV-018). Runners that cannot be dumped - anything running in-process rather than as a separate program - simply do not implement it, and an orchestrator asserts the capability once at admission rather than discovering at migration time that a workload was never movable.

Both methods take a context on the same terms as Start: it bounds the call, not the lifetime of the process the call produces.

type Controller

type Controller struct {
	GraceStop time.Duration
	WaitStop  time.Duration

	// WaitStart WAS ONE FIELD DOING THREE JOBS, and GAPI-DIV-107 is the
	// entry for the first two. It was 10s for every agent of every
	// language, read at three call sites, and each site was bounding a
	// different phenomenon:
	//
	//   - the spawn call itself (fork, exec, pipes, sockets)
	//   - first frame to RUNNING, the agent's own start()
	//   - the post-RELOAD wait, which is not a start at all
	//
	// Splitting them is the point of the entry. Naming them separately
	// is what stops the next change from moving one and silently taking
	// the other two with it - twice in one day this project moved a
	// value to where it belonged and removed a job it had been doing in
	// secret (STARTING was also the concurrency guard; WaitStart was
	// also the silence budget).
	//
	// ReadinessBudget is per-agent and is the one a descriptor can
	// declare. SilenceBudget and SpawnBudget are supervisor policy and
	// are not declarable; see core/budget for why.
	ReadinessBudget time.Duration
	SilenceBudget   time.Duration
	SpawnBudget     time.Duration
	// contains filtered or unexported fields
}

func NewController

func NewController(id, host string, r Runner, bus *TypedBus, deps DependencyResolver) *Controller

func (*Controller) Apply

func (c *Controller) Apply(a Action) error

func (*Controller) ApplyWithContext

func (c *Controller) ApplyWithContext(ctx context.Context, a Action) error

func (*Controller) State

func (c *Controller) State() string

type DependencyResolver

type DependencyResolver interface {
	DepsOf(id string) []string
	IsRunning(id string) bool
	EnsureStarted(ctx context.Context, id string) error
}

type FirstFrameReporter

type FirstFrameReporter interface {
	// FirstFrameLatency reports exec to first control frame for the
	// current run, or 0 if the child has not spoken.
	FirstFrameLatency() time.Duration
}

FirstFrameReporter answers HOW LONG the current run's child took to write its first control frame (GAPI-DIV-107).

SpeechReporter answers whether; this answers when, and the two are different questions with different consumers. The silence deadline needs the first - a child that has said nothing has no latency to report, so 0 could not be told from "instantly". A start that failed needs the second: an agent that spoke at 1ms and then hung inside its own start() and one whose ADK took most of the budget to come up both present as Silent=false, and only the measured latency separates them.

Optional, on the same terms as SpeechReporter: an in-process runner has no control channel and simply does not implement it.

type LifecycleStateMachine

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

func NewLifecycleStateMachine

func NewLifecycleStateMachine(agentID, hostname string, bus *eventbus.EventBus[*anypb.Any]) *LifecycleStateMachine

func (*LifecycleStateMachine) CurrentProtoState

func (lsm *LifecycleStateMachine) CurrentProtoState() protopkg.AgentState

func (*LifecycleStateMachine) GetState

func (lsm *LifecycleStateMachine) GetState() string

func (*LifecycleStateMachine) TransitionTo

func (lsm *LifecycleStateMachine) TransitionTo(newState string) error

type RunIDSetter

type RunIDSetter interface {
	SetRunID(string)
}

Optional capability for runners to support per-start correlation.

type Runner

type Runner interface {
	// Start spawns the runner's process. The context bounds the start
	// operation only - it is cancelled as soon as Start returns, so an
	// implementation must NOT tie the spawned process's lifetime to it.
	// exec.CommandContext here means the process is SIGKILLed the moment
	// the start call completes (GAPI-DIV-028); use exec.Command and let
	// Stop own the process.
	Start(ctx context.Context) error
	Stop(ctx context.Context) error
	Reload(ctx context.Context) error
	Reset()
}

type SpeechReporter

type SpeechReporter interface {
	// HasSpoken reports whether any valid control frame has arrived
	// since the current run was started. It is reset by Start, so it
	// answers about this run and not the agent's history.
	HasSpoken() bool
}

SpeechReporter answers whether the current run's child has written any control frame at all (GAPI-DIV-104).

A START DEADLINE THAT EXPIRES HAS TWO CAUSES AND THEY NEED DIFFERENT ANSWERS. A child that spoke and has not yet reached RUNNING is slow; a child that has said nothing is either hung before its first report or built against an ADK that never opened the descriptor. Since GAPI-DIV-099 the supervisor learns state only from frames the agent writes, so silence is the sole evidence for the second case - and without this the timeout names neither, reporting only that the wait ended.

Optional, on the same terms as RunIDSetter: an in-process runner has no control channel and simply does not implement it, which is distinct from implementing it and answering false.

type StartTimeout

type StartTimeout struct {
	AgentID      string
	RunID        string
	Waited       time.Duration
	Silent       bool
	SilenceKnown bool
	FirstFrame   time.Duration
}

StartTimeout is the start deadline expiring, as data rather than as a sentence (GAPI-DIV-104).

Silent is the discriminator the old bare timeout could not express: a child that has written nothing is hung before its first report or was built against an ADK that never opened the control descriptor, while one that has spoken and not reached RUNNING is merely slow. Those want different operator responses, and a caller must be able to branch on the difference without matching on a message.

SilenceKnown is separate from Silent because "the runner cannot answer this question" is a third state, not a quiet false. An in-process runner has no control channel at all.

FirstFrame is how long exec-to-first-speech actually took, when the runner can say (GAPI-DIV-107). It is the difference between an agent that spoke at 1ms and then hung inside its own start(), and one whose ADK took most of the budget to come up at all - two failures with the same Silent=false and completely different answers.

func (*StartTimeout) Error

func (e *StartTimeout) Error() string

type TypedBus

type TypedBus = eventbus.EventBus[*anypb.Any]

Jump to

Keyboard shortcuts

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