perf: reduce backend precompute engine CPU - #316
Merged
Merged
Conversation
The OTLP warm-tier ingest path ran `reconcile_from_streaming_config` on *every* ingest batch, and that function deep-cloned every `SketchInstanceMetadata` in the catalog via `snapshot_instances()` (each carries a `String` + `BTreeSet<String>` + `AggKind` strings). Live `perf` on node2's `asap-backend` showed this as the dominant backend CPU cost: `BTreeMap::clone::clone_subtree` + `String::clone` + `SketchInstanceMetadata::clone` + drops, plus the malloc/free churn they drive (~23% in malloc/cfree alone), all under `reconcile_from_streaming_config`. Two focused changes, both correctness-preserving: 1. Eliminate the per-reconcile catalog clone. Add `SketchStore::for_each_instance` (lock-held visitor) and rewrite reconcile to derive each sid's signature under the read lock into a reused scratch buffer, collecting only the `u64` orphan sids, then retiring them after the lock drops. No `SketchInstanceMetadata` clone, no per-sid signature `Vec` allocation. 2. Skip reconcile entirely when the config is unchanged. The streaming config is an `Arc<ArcSwap<StreamingConfig>>` whose `Arc` identity only changes on a (rare) control-plane swap. New `reconcile_if_config_changed` gates on the `Arc` data pointer via an `AtomicUsize` on `SketchStore`, collapsing the steady-state per-batch reconcile to one relaxed atomic load. Measured (criterion `reconcile_per_batch`, full un-gated scan): 100 sids: 35.2 us -> 12.6 us (2.8x) 1000 sids: 388 us -> 134 us (2.9x) 10000 sids: 3.99 ms -> 1.45 ms (2.75x) The pointer gate additionally takes the steady-state per-batch cost to ~one atomic load (reconcile runs only on config swaps). Reconcile semantics are unchanged: it only ever transitions Active->Retired on signature mismatch against the config (no time-based expiry — that stays in the eviction service), so an unchanged config produces identical results regardless of wall clock. All 9 reconcile lib tests pass (incl. 2 new gate tests + the HTTP config-swap path test); 228 sketch_db lib tests + edge-runtime adapter tests green. 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.
Path located
The asapquery-backend precompute/ingest path that consumes the agents' warm-tier OTLP output (delta Sums + sketch envelopes) and merges into the sketch store:
data_plane/src/drivers/ingest/otel.rs— OTLP gRPC/HTTP receiver;route_otlp_to_precompute+route_modified_otlp_sketches_to_precomputerun per ingest batch.data_plane/src/storage_engines/sketch_db/lifecycle/reconcile.rs—reconcile_from_streaming_config, called from both ingest routines on every batch.data_plane/src/storage_engines/sketch_db/index/mod.rs—SketchStore+SketchInstanceMetadata(the cloned type).Profiling method
Live
perfon node2's runningasap-backend(PID 429550, ~116% CPU under sustained agent load).perfwas absent on node2; installedlinux-tools-5.15.0-168-generic, thenperf record -F 199 -g -p <pid> -- sleep 30(6906 samples). The release binary symbolized cleanly.Top hotspots (before)
All under
reconcile_from_streaming_config->SketchStore::snapshot_instances(), which deep-clones everySketchInstanceMetadata(String + BTreeSet + AggKind strings) on each ingest batch:malloc17.9% +cfree5.7% (~23% allocation churn)BTreeMap::clone::clone_subtree19.6% (children)SketchInstanceMetadata::clone6.9% (children)String::clone5.0%drop_in_place::<SketchInstanceMetadata>2.1%reconcile_from_streaming_configitself 2.0% self / drives the aboveWhat changed
SketchStore::for_each_instance(lock-held visitor); reconcile now derives each sid's signature under the read lock into a reused scratch buffer, collects onlyu64orphan sids, and retires them after the lock drops. No metadata clone, no per-sid signatureVecalloc.Arc<ArcSwap<StreamingConfig>>whoseArcidentity only changes on a (rare) control-plane swap. Newreconcile_if_config_changedgates on theArcdata pointer via anAtomicUsizeonSketchStore, collapsing the steady-state per-batch reconcile to one relaxed atomic load.Measured after
Criterion
reconcile_per_batch(full un-gated scan, isolating change #1):Change #2 takes the steady-state per-batch cost to ~one atomic load (reconcile runs only on config swaps), removing this hotspot from the per-batch ingest path almost entirely.
Correctness
Reconcile only ever transitions Active->Retired on signature mismatch vs. the config (no time-based expiry — that stays in the eviction service), so an unchanged config yields identical results regardless of wall clock; a newly-minted sid matches a live signature by construction so it is never wrongly orphaned. Query answers are unaffected. All 9 reconcile lib tests pass (incl. 2 new gate tests + the HTTP config-swap path test); 228 sketch_db lib tests + edge-runtime adapter tests green.
Test plan
cargo test -p data_plane --lib reconcilecargo test -p data_plane --lib sketch_dbcargo bench -p data_plane --bench sketch_db -- reconcile_per_batchasap-backendto confirm the reconcile/clone frames are gone from the per-batch path.Generated with Claude Code