Documentation
¶
Index ¶
Constants ¶
const ( StateInitializing = "initializing" StateInitialized = "initialized" StateStarting = "starting" StateRunning = "running" StateStopping = "stopping" StateStopped = "stopped" StateReloading = "reloading" StateRestarting = "restarting" StateError = "error" )
Variables ¶
var DepCtxKey = depCtxKey{}
DepCtxKey is used to store the call stack for cycle detection
Functions ¶
This section is empty.
Types ¶
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 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 (*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