Skip to content

Commit bd516c5

Browse files
committed
Restack PR #766 with implementation before standalone acceptance
1 parent 7caa863 commit bd516c5

9 files changed

Lines changed: 889 additions & 47 deletions

File tree

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Runtime overhead contracts
2+
on:
3+
pull_request:
4+
paths:
5+
- 'data_plane/**'
6+
- 'crates/asap_types/**'
7+
- 'Cargo.toml'
8+
- 'Cargo.lock'
9+
- '.github/workflows/overhead-inspection.yml'
10+
workflow_dispatch:
11+
jobs:
12+
contracts:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- uses: dtolnay/rust-toolchain@stable
17+
- uses: Swatinem/rust-cache@v2
18+
- run: sudo apt-get update && sudo apt-get install -y protobuf-compiler
19+
- run: cargo test --locked -p data_plane --lib runtime_config::tests
20+
- run: cargo test --locked -p data_plane --example overhead_inspect
21+
- run: >-
22+
cargo run --locked -p data_plane --example overhead_inspect --
23+
--workers 1,2 --concurrency 1,2 --requests 20 --warmup 2 --repeats 1
24+
--log-level off --disable-console-log --disable-file-log
25+
--output target/overhead-smoke
26+
- uses: actions/upload-artifact@v4
27+
if: always()
28+
with:
29+
name: overhead-smoke
30+
path: target/overhead-smoke

‎Cargo.lock‎

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎data_plane/Cargo.toml‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ fs2 = "0.4"
8787
# none of them.
8888

8989
[dev-dependencies]
90+
libc = "0.2"
9091
asap-aware-mapping.workspace = true
9192
tempfile = "3.20.0"
9293
criterion = { version = "0.5", features = ["html_reports"] }
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
use anyhow::{ensure, Result};
2+
use asap_sketchlib::{DdSketch, MessagePackCodec};
3+
use data_plane::drivers::query::{
4+
adapters::AdapterConfig, servers::http::validate_and_build_runtime_plan,
5+
};
6+
use data_plane::query_engines::{
7+
routing::query_engine_routing::QueryEngine, QueryForwardingPolicy, QueryResult,
8+
};
9+
use data_plane::storage_engines::{
10+
sketch_db::index::*,
11+
types::{ActivePhysicalPlanHandle, BackendStorageRouting},
12+
};
13+
use data_plane::{ASAPQueryEngine, HttpServer, HttpServerConfig};
14+
use std::{
15+
collections::{BTreeMap, BTreeSet},
16+
sync::Arc,
17+
};
18+
19+
#[path = "../../tests/support/physical_fixture.rs"]
20+
#[allow(dead_code)]
21+
mod physical_fixture;
22+
23+
pub const QUERY: &str = "quantile_over_time(0.5, overhead_values[1s])";
24+
pub const TIME: u64 = 600_000;
25+
pub struct Fixture {
26+
pub raw: Vec<f64>,
27+
pub sketch: DdSketch,
28+
pub expected: f64,
29+
pub exact_expected: f64,
30+
pub engine: Arc<ASAPQueryEngine>,
31+
pub server: HttpServer,
32+
}
33+
pub fn exact(values: &[f64]) -> f64 {
34+
let mut sorted = values.to_vec();
35+
let middle = sorted.len() / 2;
36+
let (_, upper, _) = sorted.select_nth_unstable_by(middle, f64::total_cmp);
37+
let upper = *upper;
38+
if sorted.len().is_multiple_of(2) {
39+
(sorted[..middle]
40+
.iter()
41+
.copied()
42+
.max_by(f64::total_cmp)
43+
.unwrap()
44+
+ upper)
45+
/ 2.0
46+
} else {
47+
upper
48+
}
49+
}
50+
impl Fixture {
51+
pub fn new(samples: usize) -> Result<Self> {
52+
ensure!(samples > 0, "samples must be positive");
53+
// Deliberately unsorted, reproducible input; exact includes scratch allocation
54+
// and selection, while prebuilt sketch construction is outside measurement.
55+
let raw: Vec<_> = (0..samples)
56+
.map(|i| 1.0 + ((i * 7919) % 10007) as f64)
57+
.collect();
58+
let mut sketch = DdSketch::new(0.01);
59+
for value in &raw {
60+
sketch.update(*value);
61+
}
62+
let expected = sketch.quantile(0.5).unwrap();
63+
let exact_expected = exact(&raw);
64+
ensure!(
65+
(expected - exact(&raw)).abs() <= exact(&raw) * 0.02,
66+
"sketch accuracy gate failed"
67+
);
68+
let config: asap_types::PrecomputeMaterialization = serde_json::from_value(
69+
serde_json::json!({
70+
"aggregation_type":"DDSketch", "aggregation_sub_type":"", "metric":"overhead_values",
71+
"window_size":1,"slide_interval":1,"window_type":"tumbling","num_aggregates_to_retain":10,
72+
"parameters":{"alpha":0.01},"pane_origin_ms":0,"partitioning":"per_entity",
73+
"window_layout":{"kind":"pane","pane_secs":1},"grouping_labels":{"labels":[]},
74+
"aggregated_labels":{"labels":[]},"rollup_labels":{"labels":[]},
75+
"spatial_filter":"","spatial_filter_normalized":"","original_yaml":""
76+
}),
77+
)?;
78+
let plan = physical_fixture::artifact_from_materializations(vec![config.clone()]);
79+
let store = Arc::new(SketchStore::new());
80+
store
81+
.install_precompute_plan(
82+
Arc::new(plan.summary_catalog.clone()),
83+
&plan.precompute_plan,
84+
)
85+
.map_err(anyhow::Error::msg)?;
86+
let skconfig = SketchConfig::DDSketch {
87+
relative_accuracy: 0.01,
88+
};
89+
store.register(SummarySeriesMetadata {
90+
storage_handle: 1,
91+
metric_name: "overhead_values".into(),
92+
group_by_keys: BTreeSet::new(),
93+
capability: Some(Capability::QuantileApprox(Some(SketchAlgorithm::DDSketch))),
94+
accuracy: Some(AccuracyBound::from_config(&skconfig)),
95+
agg_kind: AggKind::Sketch {
96+
algorithm: SketchAlgorithm::DDSketch,
97+
config: skconfig,
98+
spatial_filter_canonical: String::new(),
99+
},
100+
first_seen_unix_ms: 0,
101+
retired_at_ms: None,
102+
expires_at_ms: None,
103+
policy_fp: config.policy_fingerprint(),
104+
});
105+
ensure!(
106+
store.append_sample(
107+
1,
108+
BTreeMap::new(),
109+
(TIME - 1000, TIME),
110+
SketchSampleState {
111+
bytes: sketch.to_msgpack().map_err(|e| anyhow::anyhow!("{e}"))?,
112+
encoding: SketchEncoding::MsgpackFull,
113+
}
114+
),
115+
"sample admission failed"
116+
);
117+
let active = ActivePhysicalPlanHandle::new(
118+
validate_and_build_runtime_plan(plan, Arc::new(BackendStorageRouting::empty()))
119+
.map_err(anyhow::Error::msg)?,
120+
);
121+
let engine = Arc::new(
122+
ASAPQueryEngine::new(1000)
123+
.with_sketch_index(store.clone())
124+
.with_active_physical_plan(active)
125+
.with_query_forwarding_policy(QueryForwardingPolicy::Disabled),
126+
);
127+
let server = HttpServer::new(
128+
HttpServerConfig {
129+
port: 0,
130+
handle_http_requests: true,
131+
adapter_config: AdapterConfig::prometheus_promql(String::new(), false)
132+
.with_query_forwarding_policy(QueryForwardingPolicy::Disabled),
133+
},
134+
engine.clone(),
135+
store,
136+
);
137+
Ok(Self {
138+
raw,
139+
sketch,
140+
expected,
141+
exact_expected,
142+
engine,
143+
server,
144+
})
145+
}
146+
pub async fn backend(&self) -> Result<f64> {
147+
match self.engine.execute_at(QUERY, TIME).await? {
148+
QueryResult::Vector(v) if v.values.len() == 1 => Ok(v.values[0].value),
149+
other => anyhow::bail!("unexpected result {other:?}"),
150+
}
151+
}
152+
}

0 commit comments

Comments
 (0)