Scan-cycle observability¶
Detailed design for the scan-cycle observability sub-feature
(Scan-cycle observability (FEAT_0021)). Two structural pieces: a fixed-bucket histogram
for percentile estimation (chosen for its allocation-free, bounded-time
per-sample update path), and per-task aggregate slots allocated at
Executor::build time.
Each completed (or faulted) scan cycle folds one observation into the per-task statistics through an allocation-free update path, then publishes both a raw push sample and an aggregated pull snapshot:
flowchart LR
Pre["pre_execute<br/>(task-logic start, telemetry clock)"]
Post["post_execute<br/>(took / actual_period / jitter / lateness)"]
subgraph Update["allocation-free per-sample update (REQ_0104)"]
Hist["Histogram.record(took_ns)<br/>fixed octave buckets (ADR_0060)"]
Deque["MinMaxDeque.record(took)<br/>exact windowed min/max (REQ_0105)"]
Jit["max_jitter_ns (REQ_0101)<br/>max_lateness_ns (REQ_0106)"]
Ovr["overrun_count (REQ_0102)"]
end
Push["on_cycle_stats(&CycleObservation)<br/>raw sample, once per scan attempt (REQ_0103)"]
Pull["Executor::stats_snapshot()<br/>p50 / p95 / p99 · min / max ·<br/>max_jitter · max_lateness · overrun"]
Gate["exact-extreme SLO gate (REQ_0851)<br/>pass/fail uses exact extremes, not buckets"]
Pre --> Post
Post --> Hist
Post --> Deque
Post --> Jit
Post --> Ovr
Post --> Push
Hist --> Pull
Deque --> Pull
Jit --> Pull
Ovr --> Pull
Deque --> Gate
Jit --> Gate
Context. Per-task latency percentiles (REQ_0100) requires p50 / p95 / p99 execute-duration percentiles per task over a sliding window, and Allocation-free telemetry u... (REQ_0104) requires the update path to be allocation-free with bounded per-sample latency. A window-of-raw-samples approach (keep the last N samples, sort on query) is allocation-free if N is fixed at build time but pays O(N log N) on every query. Streaming sketches (t-digest, CKMS) give tight p99 accuracy but their compaction step is amortised, not bounded, and they reshape memory as data arrives. Decision. Use a fixed-bucket log-linear histogram covering the
value range 100 ns … 10 s with at least three buckets per decade
(eight decades × three buckets ≈ 24 active buckets, padded to a
power of two for cheap indexing). The bucket layout is fixed at
compile time as a Alternatives considered.
Consequences. ✅ Per-sample update is O(1) and allocation-free
(per Allocation-free telemetry u... (REQ_0104)).
✅ Per-task memory footprint is bounded and known at build time
(~1 kB / task for the histogram + snapshots).
❌ Percentile values are bucket-quantised. With the shipped octave
layout the geometric-midpoint estimate is bounded at a factor of Amendment (:need:`REQ_0105`, :need:`REQ_0106`). The histogram is retained as the percentile estimator, but two quantities are added alongside it because the histogram cannot supply them:
Both additions preserve the allocation-free, bounded-time per-sample update contract of Allocation-free telemetry u... (REQ_0104). |
The histogram, deques, and atomic fields are provided by the shared
|
|
Concrete Rust changes that realise Per-task cycle statistics (BB_0050) and Statistics snapshot view (BB_0051). Shared primitive — ``taktora-stats`` crate (per Shared no_std taktora-stats... (ADR_0062)) The allocation-free Module ``crates/taktora-executor/src/stats/`` (thin wrapper)
The per-task aggregator ( In ``crates/taktora-executor/src/observer.rs``
In ``crates/taktora-executor/src/executor.rs``
Verification
|
Context. The allocation-free statistics primitive (fixed-bucket
histogram per Fixed-bucket histogram for ... (ADR_0060), the monotonic min/max deque of
Per-task exact min/max exec... (REQ_0105), the atomic aggregate fields) is needed in two
places: the executor’s scan-cycle stats (Per-task cycle statistics (BB_0050)) and the
connector’s cycle telemetry (Hybrid two-layer timing mea... (ADR_0063)). The connector seam
Decision. Extract the primitive into a new Alternatives considered.
Consequences. ✅ One allocation-free implementation, one Allocation-free telemetry u... (TEST_0194) audit,
reused at both layers.
✅ |
The
Consumed by Per-task cycle statistics (BB_0050) (executor) and the connector telemetry building blocks (Hybrid two-layer timing mea... (ADR_0063)). |