feat(asap-precompute-rs): otap full plugin (Phase 5 step C) - #259
Merged
Merged
Conversation
Phase 5 step C of the OTAP-Rust integration: full asap_sketches plugin lifecycle on top of the Phase B codec. Adds Tokio-driven input task + Wakeup-style flush ticker + control-channel poll task + graceful drain, with 5-sketch dispatch (DDSketch / KLL / HLL / CountSketch / CountMinSketch) selected by the sketch_type config knob, mirroring the Telegraf-side allsketches processor (#237). New code: - src/otap/config.rs: PluginConfig + 5-sketch sketch_type dispatch factory, mapping the user-facing string to (PrecomputeConfig, SketchFactory, SketchObserver). Mirrors Telegraf's toPrecomputeConfig + sketchFactory. - src/otap/records.rs: local OtapMetricRecords model with the bidirectional sibling-batch <-> flat-batch projection that Phase B deferred. flatten() lowers per-row attribute rows into top-level Strategy-B columns the codec consumes; lift() raises Strategy-B columns onto the per-row attribute child batch on emit so OTAP's strict schema validator (crates/pdata/src/schema/payloads.rs:: check_match) accepts the result. The upstream OtapArrowRecords binding lands in Phase D once the OTAP submodule is wired in. - src/otap/lifecycle.rs: AsapSketchesPlugin Tokio runtime — three concurrent tasks (input stream consumer, interval-driven flush ticker, control-channel poll) plus oneshot-driven graceful shutdown that invokes Precompute::drain before exit so terminating before the natural window boundary doesn't drop in-flight observations. - tests/otap_lifecycle.rs: end-to-end harness with one test per sketch_type (5 tests), drain-before-window-boundary test, control-channel plan-change test, plus shutdown smoke tests. Plugin shell scaffolding (Phase D wires the linkme registration): - otap-patch/plugins/asap_sketches/sample.toml: canonical TOML config block. - otap-patch/plugins/asap_sketches/README.md: user-facing config + lifecycle docs. - otap-patch/plugins/asap_sketches/src/mod.rs: placeholder for Phase D's linkme distributed-slice entry. Cargo.toml: tokio (rt + sync + time + macros) and futures gated under the existing `otap` feature; no new top-level features. NOT in this PR (Phase D / E): - build_sketchotap.sh build script. - otap-patch/all/mod.rs linkme registration. - Cross-host envelope parity test. Verification: - cargo test --release -p asap-precompute-rs --features otap: 127 tests pass (was 118; added 9 lifecycle tests). - cargo test --release -p asap-precompute-rs (no features): 88 tests pass — otap-gated code is absent from the default build. - cargo clippy --release -p asap-precompute-rs --features otap --all-targets -- -D warnings: clean. - RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p asap-precompute-rs --features otap: clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Phase 5 step C of the OTAP-Rust integration per
docs/design-asap-otap-rust-integration.md§5, §6, §11. Layers the fullasap_sketchesplugin lifecycle on top of the Phase B codec (#256): a Tokio-driven input-stream consumer,Wakeup-style flush ticker, control-channel poll task, and graceful drain — with all five sketch types (DDSketch / KLL / HLL / CountSketch / CountMinSketch) dispatched via thesketch_typeconfig knob. Mirrors the Phase 4 step C Telegraf-sideprocessors.allsketches(#237) structurally.The OTAP submodule wiring (
linkmedistributed-slice registration,build_sketchotap.sh,otap-patch/all/mod.rspatch) is deliberately deferred to Phase D per the §11 phase plan.Plugin shape
AsapSketchesPlugin::start()spawns three concurrent Tokio tasks:Stream<OtapMetricRecords>. Per batch:flatten()projects the sibling-batch family down to a flatRecordBatch→decode_batch()producesVec<Observation>→ each observation routes throughPrecompute::observe.tokio::time::interval(window_size)) callsPrecompute::tick(now_ms)→encode_batch()→lift()raises Strategy-B_asap_*columns onto the per-row attribute child batch (so the resulting batch passes OTAP's strictcrates/pdata/src/schema/payloads.rs::check_matchvalidator) → emits the lifted family on anmpscchannel.ControlChannel::poll()on a configurable interval, callsPrecompute::update_config()on plan change, thenacks the version.Graceful shutdown (
PluginHandle::shutdown().await) signals all three tasks via aoneshotcancel, joins them, runsPrecompute::drain()for the in-flight residue, and emits one final batch.File layout
New files:
asap-precompute-rs/src/otap/config.rsPluginConfig+ 5-sketchsketch_typedispatch factory (resolve()returns(PrecomputeConfig, SketchDispatch)).asap-precompute-rs/src/otap/records.rsOtapMetricRecordsmodel +flatten()/lift()bidirectional projection.asap-precompute-rs/src/otap/lifecycle.rsAsapSketchesPluginTokio runtime +PluginHandlegraceful-shutdown affordance.asap-precompute-rs/tests/otap_lifecycle.rsotap-patch/plugins/asap_sketches/sample.tomlotap-patch/plugins/asap_sketches/README.mdotap-patch/plugins/asap_sketches/src/mod.rslinkme::distributed_sliceentry.Modified files:
asap-precompute-rs/Cargo.tomltokio(rt,rt-multi-thread,sync,time,macros) andfuturesas optional deps gated under the existingotapfeature.asap-precompute-rs/Cargo.lockasap-precompute-rs/src/otap/mod.rsconfig,records,lifecycle).Test summary
cargo test --release --features otapruns 127 tests (up from 118 in Phase B); new coverage:lifecycle_ddsketch_emits_envelope_with_correct_sketch_type— DDSketch end-to-end: 5 scalar inputs → drain → assert envelope-bearing attribute row carrying_asap_sketch_type=DDSketch.lifecycle_kll_emits_envelope_with_correct_sketch_type— KLL end-to-end.lifecycle_hll_emits_envelope_with_correct_sketch_type— HLL end-to-end (single series, distinct values viavaluecolumn).lifecycle_countsketch_emits_envelope_with_correct_sketch_type— CountSketch end-to-end.lifecycle_countminsketch_emits_envelope_with_correct_sketch_type— CMS lifecycle smoke (CMSObserver requires Bytes-kind input; the test asserts the structural shape rather than the wrapper-level path that's already covered bytests/runtime.rs).drain_flushes_in_flight_observations_before_window_boundary— drain test: 60s window, shutdown immediately, assert envelope is emitted.control_channel_plan_change_acks_after_apply— feeds a one-shotControlChannel, waits for ack, asserts post-changemetric_nameis reflected on emitted batches.shutdown_without_inputs_is_clean_no_op— empty stream + shutdown produces no batches and doesn't panic.unknown_sketch_type_rejected_at_construction—from_plugin_configsurfacesUnknownSketchTypefor a bogus spelling.Plus 11 new unit tests across
config.rs/records.rs/lifecycle.rscovering dispatch case-insensitivity, zero-window rejection, flatten/lift round-trips, and the structural Strategy-B contract (no_asap_*columns on the metrics-side schema afterlift()).Phase B's existing 4 codec tests + 27 runtime tests + 11 api-surface tests + 7 cross-language-parity tests + 69 unit tests all still pass — no core module touched.
Cargo
[features]and dependency changesotapfeature now also gatestokio(sub-featuresrt,rt-multi-thread,sync,time,macros) andfutures. No new top-level features. No new default deps. The non-otapbuild (cargo test --release) is byte-identical to Phase B (43 unit + 11 + 7 + 27 = 88 tests, notokioin the closure).Verification
cargo test --release -p asap-precompute-rs --features otap— 127 tests, 0 failed.cargo test --release -p asap-precompute-rs(no features) — 88 tests, 0 failed.cargo clippy --release -p asap-precompute-rs --features otap --all-targets -- -D warnings— clean.RUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p asap-precompute-rs --features otap— clean.Design judgements
Local
OtapMetricRecordsmodel. The upstream OTAPOtapArrowRecordsRust type lives in theotap-dataflowsubmodule that Phase D wires in. Phase C tests run without the submodule, sorecords.rsdefines a local two-batch family (metrics + per-row attributes joined byparent_idUInt32) that captures the structural shape Phase D will bind to. Theflatten()/lift()API surface is the contract; Phase D adds a thin wrapper over the upstream type without changing it.Strategy-B attribute carrier choice.
lift()removes ALL Strategy-B_asap_*columns plus all non-reservedUtf8label columns from the metrics-side batch and re-emits them as(parent_id, key, typed-value)rows on the attributes batch. The non-reserved-label lift is necessary because OTAP'scheck_matchvalidator rejects ANY extension column on the metrics batch — not just_asap_*ones. Tests pin this contract (assert_no_strategy_b_top_level_columns).HttpPollChannel deferred. The design doc §10 notes the Rust
HttpPollChannelimpl was promised alongside the Vector adapter (a parallel Phase 5 effort). Phase C wires the existingControlChanneltrait — operators supply any impl. The lifecycle test ships aOnceChannelstub. When the productionHttpPollChannellands (separate PR), it slots in viaArc<dyn ControlChannel>without changes here.Single-instance plugin model. Each
AsapSketchesPlugininstance owns onePrecompute(one sketch type per thePrecomputeConfig::sketch_typerule). Multi-sketch deployments declare multiple[[pipelines.metrics.processors]]blocks with distinctidvalues — same shape as Telegraf'sallsketches.Tokio sub-feature gating. Pulled in
rt-multi-threadso the supervisor canspawnlong-running tasks;syncformpsc/oneshot/Notify;timeforinterval. Avoidedtokio-util(would have given a stockCancellationToken) by inlining a lightweightNotify-basedCancellation— keeps the dep surface minimal.Open question / signal for Phase D + doc-tightening
OtapMetricRecords↔ upstreamOtapPdatabinding. Phase D needs to map our two-batch family to whatever upstream representation OTAP'slocal::Processor<OtapPdata>trait expects. The shape is structurally compatible (twoRecordBatches with parent-id join), but Phase D should add a thinFrom/Intoadapter and update the doc §6 file layout to call out ourrecords.rsas the local model.HttpPollChannelRust impl as "shipped alongside the Vector adapter". The Vector adapter PR is in flight (perdocs/design-asap-vector-integration.md); Phase C ships the trait surface only. Worth a doc-tightening note that says "the trait alone unblocks Phase C; the prod impl ships with Vector".lift()+assert_no_strategy_b_top_level_columns— Phase D inherits it for free.Test plan
cargo test --release -p asap-precompute-rs --features otap— greencargo test --release -p asap-precompute-rs(no features) — greencargo clippy --release -p asap-precompute-rs --features otap --all-targets -- -D warnings— cleanRUSTDOCFLAGS="-D warnings" cargo doc --no-deps -p asap-precompute-rs --features otap— cleanbuild_sketchotap.shproduces asketchotapbinary with the plugin registeredsketchcollector/sketchtelegraf