Documentation
¶
Overview ¶
Package buildsignal implements the build poll loop. Each message names one build by the runner's own ID; the controller calls BuildRunner.Status, writes the latest status to that build's record, and wakes the speculate run for its batch, holding its delivery between polls while the build is still in flight.
The poll loop is also where builds are stopped. It follows every build to a terminal state anyway, so on each poll it checks whether anything still wants the build running — the batch not halted, the path's current attempt, with a status that is not a stop, linked to this very build — and asks the runner to cancel when nothing does. That makes cancellation level-triggered: the speculate run records intent in the path set and nothing more, no cancel message exists to go stale, and a check that misses one poll is remade on the next.
The path set is read here as that kill list, and never written — the speculate run stays its only writer, which is what lets a run hold one version of a head's paths across its whole decision without a poll invalidating it. The read must come from the primary: a stale replica read could report a wanted build unwanted, and a cancel is irreversible.
Each build partitions independently, so slow polls on one build do not block another, and successive polls of one build stay ordered. A webhook-capable backend can publish into this same topic — the controller cannot tell a poll-driven message from a push.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ( // PollDelayAcceptedMs is the delay between Status calls while the build // is queued by the runner but has not started executing. PollDelayAcceptedMs int64 = 5000 // PollDelayRunningMs is the delay between Status calls while the build // is executing. PollDelayRunningMs int64 = 2000 )
Poll delays for non-terminal statuses. Vars (not consts) so tests can shorten them; the orchestrator always uses the defaults.
TODO: make these poll delays configurable per queue via the queueconfig extension instead of package-level vars, so operators can tune poll cadence without a code change.
Functions ¶
This section is empty.
Types ¶
type Controller ¶
type Controller struct {
// contains filtered or unexported fields
}
Controller consumes build signal messages, polls BuildRunner.Status, persists the result, and drives the polling loop.
func NewController ¶
func NewController( logger *zap.SugaredLogger, scope tally.Scope, stores orchstorage.Factory, buildRunners buildrunner.Factory, registry consumer.TopicRegistry, topicKey consumer.TopicKey, consumerGroup string, ) *Controller
NewController creates a new build signal controller for the orchestrator.
func (*Controller) ConsumerGroup ¶
func (c *Controller) ConsumerGroup() string
ConsumerGroup returns the consumer group for offset tracking.
func (*Controller) Name ¶
func (c *Controller) Name() string
Name returns the controller name for logging and metrics.
func (*Controller) Process ¶
Process polls one attempt's build status, stops the build if nothing wants it running any more, persists the status, wakes the speculate run, and holds the delivery for the next poll while the build is still in flight. Returns nil to ack (success), or error to nack/reject.
There is deliberately no short-circuit for halted batches. A cancelling batch reaches its terminal state only once its paths stop, and this loop is the only thing watching them stop — and, now, the thing stopping them: speculate marks a path cancelling, the next poll here asks the runner to cancel, and a later poll observes CI actually stop and records it. Skipping the work — including the hold — for a halted batch would leave every cancelled batch stranded in Cancelling forever.
Error classification: deserialize, Status, the kill-list reads, the persistence writes, and the speculate publish stay non-retryable — they reject straight to DLQ on the first failure, where the operational republish path is the recovery mechanism. The Cancel call is best-effort instead: failing the message for it would kill the poll chain that is the only thing that will retry the cancel, so a failure is logged and the next poll remakes the whole check. The poll loop's continuation is a hold, not a publish: the framework postpones the delivery, and a failed postpone write lapses into a normal visibility-timeout redelivery, so the loop cannot stall on an enqueue.
func (*Controller) TopicKey ¶
func (c *Controller) TopicKey() consumer.TopicKey
TopicKey returns the topic key this controller subscribes to.