#[non_exhaustive]pub enum Event<'a> {
Admitted {
queue: &'a str,
count: usize,
},
Rejected {
queue: &'a str,
policy: &'a str,
count: usize,
},
Completed {
kind: &'a str,
ms: u64,
},
Quarantined {
fingerprint: &'a str,
crashes: u32,
},
Evicted {
queue: &'a str,
count: u64,
},
JobSpan {
job_id: &'a str,
kind: &'a str,
queue: &'a str,
attempt: u32,
outcome: &'a str,
started_at_ms: i64,
ms: u64,
trace: Option<&'a TraceContext>,
},
WorkerSaturation {
worker: &'a str,
inflight: u32,
capacity: u32,
utilization: f64,
empty_poll_ratio: f64,
polls: u64,
empty_polls: u64,
},
WorkerMemory {
worker: &'a str,
used_bytes: u64,
limit_bytes: u64,
restart_requested: bool,
},
}Expand description
Events emitted through the telemetry facade.
#[non_exhaustive] lets the facade grow without breaking exhaustive downstream
matches. Job spans and worker-saturation gauges were both additive signals, and
without this attribute every such addition is a breaking change for anyone who wrote an exhaustive match
in their bridge. That is the wrong incentive: it makes “do not emit the signal” the
cheap option. Adding a variant is now additive; changing an existing variant’s
fields still is not, which is why the two additions below are new variants rather
than new fields on Completed.
Variants (Non-exhaustive)§
This enum is marked as non-exhaustive
Admitted
Rejected
Completed
Quarantined
Evicted
Eviction is always observable through both this event and a counter.
JobSpan
Emitted exactly once per attempt after the handler returns, carrying everything
an OTel-bridged deployment needs to build
one span: identity, outcome, and — the point of the addition — the traceparent
the PRODUCER put on the envelope, already parsed.
It fires at the END and carries started_at_ms + ms rather than firing at the
start, because a facade has no span object to hand back: a start-only callback
would force every bridge to keep its own job-id→span map and to leak one whenever
a worker is killed mid-attempt. An OTel span builder takes explicit start and end
timestamps, so one event is enough and nothing has to be remembered.
trace is None when the envelope carried no traceparent OR carried an
invalid one — see parse_traceparent. A bridge then starts a root span.
Fields
outcome: &'a strsuccess | retry | skip | revoke | snooze | undecodable
| rate_limited — the Outcome the runtime acked (or would have).
trace: Option<&'a TraceContext>WorkerSaturation
Worker-saturation gauges emitted by the runner on
every heartbeat, alongside the registry upsert that already happens — so the
same numbers reach a metrics exporter and GET /cluster from one place and
cannot disagree. This is a SIGNAL, not an autoscaler: headgate never sizes a
fleet, it only publishes the two numbers that decide the direction.
utilization=inflight / capacity— scale UP when it is high AND the backlog’s time-to-drain is growing (backlog metrics).empty_poll_ratio= admits that returned zero / total admits, over the runner’s rolling window — scale DOWN when it is high: the fleet is asking for work that is not there.
Its own variant rather than fields on Admitted for the reason in the type’s
doc: Admitted is per-admission and these are per-worker levels.
Fields
WorkerMemory
Process-memory sample emitted by the worker guard. restart_requested is true
only for the threshold-crossing sample that starts graceful shutdown.