Repository navigation
feat(asap-tools): Alibaba and Google trace replay builders #919
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,80 @@ | ||
| # Alibaba trace → random query workloads (handoff) | ||
|
|
||
| **Goal:** realistic, strong end-to-end results on Alibaba data for the eval section. | ||
|
|
||
| The motivation section (Prometheus, PromSketch, PilotDB; cost vs accuracy) should ideally use the same data, label mapping and similar queries, so the two line up. They don't need to match exactly. | ||
|
|
||
| ## Data the end-to-end runs feed | ||
|
|
||
| **Source:** Alibaba cluster-trace-microservices-v2022, MSMetrics, pod `cpu_utilization` (0–1). | ||
| - Columns: `timestamp, msname, msinstanceid, nodeid, cpu_utilization, memory_utilization`. | ||
| - 60 s step, about 470k pods per step, 28k distinct msname. | ||
| - Only about the first hour is local: `benchmarks/metrics_observability/data/alibaba-cluster-data/cluster-trace-microservices-v2022/0d0_0d1/data/MSMetrics/`. | ||
| - That is `MSMetricsUpdate_0.csv` plus `MSMetrics_1.tar.gz`. | ||
| - Rows are **not** sorted by time. | ||
|
|
||
| **Subset:** fit to our synthetic label shape, so the existing workloads, plans and configs apply unchanged. | ||
| - 4 region × 5 service × 10 job × 50 label_0 = 10,000 series. | ||
| - `job` = one microservice (msname). | ||
| - Seed 862 draws them from the 1,785 msnames with ≥50 pods reporting at all 60 steps. | ||
| - `label_0` = one of its pods (msinstanceid; the first 50 by pod number). | ||
| - `region` and `service` are synthetic groupings of microservices. | ||
| - The trace has neither level. | ||
| - `nodeid` cuts across msname, so it doesn't nest. | ||
| - Label values: region `r`, service `r.s`, job `r.s.j`, label_0 `r.s.j.k`. | ||
|
|
||
| **Values:** real pod CPU. | ||
|
|
||
| | Statistic | Value | | ||
| |---|---| | ||
| | min | 0.003 | | ||
| | median | 0.12 | | ||
| | p95 | 0.40 | | ||
| | p99 | 0.58 | | ||
| | max | 0.997 | | ||
|
|
||
| - No zeros. | ||
| - The busiest microservice is about 5× the median one. | ||
| - The top 10 of 200 microservices hold 15% of total CPU. | ||
| - Region totals are 20–26k. | ||
|
|
||
| **Time mapping:** a quick-result compromise. | ||
| - Replayed at 10 samples/s per series, the same as the synthetic base set. | ||
| - One trace minute becomes one 100 ms sample, so the 60-step hour loops every 6 s. | ||
| - Spread and skew across pods and services are real; each series' pattern over time repeats. | ||
| - A realistic run needs more trace (`fetchData.sh`) and a smaller speedup. | ||
|
|
||
| **Files** (generated, not tracked): | ||
| - `replay.csv`: `offset_ms,region,service,job,label_0,value`; 600k rows, sorted by offset. | ||
| - `replay_mapping.csv`: our labels → msname/msinstanceid. | ||
| - `replay_table.json`: a paced-feeder workload table with `values.replay`. | ||
| - Generator: `asap-tools/experiments/datasets/alibaba/make_replay.py`. | ||
|
|
||
| ## Query workloads | ||
|
|
||
| Seed 862, 10 random workloads; manifests are in `asap-tools/experiments/datasets/fake_metric/random_workloads/seed-862/`. | ||
|
|
||
| Each end-to-end run uses a workload's 7 `common-promql` queries: | ||
| - **Metric and groupings:** metric `data`, grouped by `(region)`, `(region, service)` or `(region, service, job)`. | ||
| - **Sums (2):** `sum by (G) (sum_over_time(data[W]))`. | ||
|
|
||
| | Window W | Repeated every | | ||
| |---|---| | ||
| | 1m | 1–2 s | | ||
| | 5m | 5–10 s | | ||
| | 10m | 10–20 s | | ||
| | 20m | 30–60 s | | ||
|
|
||
| - **Quantiles (3):** 100 s lookback, repeated every 1 s. | ||
| - PromQL can't express a pooled quantile, so it uses `avg by (G) (quantile_over_time(phi, data[100s]))`. | ||
| - Every system, ClickHouse and LiteOlly included, currently computes this same `avg` of per-series quantiles, so all answers agree. | ||
| - The intended pooled `quantile by (G)` over 100 s is being added (LiteOlly support merged in #887–890). | ||
| - **Top-k (2):** `topk(k, …)` over counts, with K capped at 1,000 keys. | ||
|
|
||
| On Alibaba data these read naturally: "a quantile of pod CPU per microservice or region over the last 100 s", and "total CPU per service over 5–20 min". | ||
|
|
||
| ## End-to-end status (2026-10-15) | ||
|
|
||
| - **Synthetic base:** 5 systems × 10 workloads, all valid. | ||
| - LiteOlly vs worst baseline: 6.2× less CPU than raw ClickHouse, and 11.6× lower p95 than Prometheus. | ||
| - **Alibaba:** pilot w01 × 5 systems is finishing. The full 10 workloads follow if it is valid. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| #!/usr/bin/env python3 | ||
| """A replay table for paced-feeder from Alibaba v2022 MSMetrics: pod CPU | ||
| utilization in the random workloads' label shape (4 region x 5 service x 10 | ||
| """A replay table for paced-feeder from Alibaba v2022 per-pod metrics (MSMetrics | ||
| CPU by default, or an MSRTMCR call rate with --column) in the random workloads' | ||
| label shape (4 region x 5 service x 10 | ||
| job x 50 label_0), so their plans apply unchanged. | ||
|
|
||
| Each job is one microservice and each label_0 one of its pods; region and | ||
|
|
@@ -17,29 +18,40 @@ | |
|
|
||
| Usage: python3 make_replay.py MSMetricsUpdate_0.csv MSMetricsUpdate_1.csv \ | ||
| --out replay [--table eval_table.json] | ||
| python3 make_replay.py MSRTMCR_*.tar.gz --column providerrpc_mcr --out calls | ||
| """ | ||
|
|
||
| import argparse | ||
| import csv | ||
| import io | ||
| import json | ||
| import random | ||
| import tarfile | ||
| from collections import defaultdict | ||
|
|
||
| REGIONS, SERVICES, JOBS, PODS = 4, 5, 10, 50 | ||
|
|
||
|
|
||
| def rows(paths): | ||
| """The CSV rows of `paths`: plain CSVs, or tarballs of them.""" | ||
| for path in paths: | ||
| with open(path, newline="") as f: | ||
| for r in csv.DictReader(f): | ||
| yield r | ||
| if path.endswith(".tar.gz"): | ||
| with tarfile.open(path) as tar: | ||
| for member in tar: | ||
| if member.isfile(): | ||
| text = io.TextIOWrapper(tar.extractfile(member), newline="") | ||
| yield from csv.DictReader(text) | ||
| else: | ||
| with open(path, newline="") as f: | ||
| yield from csv.DictReader(f) | ||
|
|
||
|
|
||
| def main(): | ||
| parser = argparse.ArgumentParser(description=__doc__) | ||
| parser.add_argument("csv", nargs="+") | ||
| parser.add_argument("--out", required=True) | ||
| parser.add_argument("--seed", type=int, default=862) | ||
| parser.add_argument("--column", default="cpu_utilization", help="the value column") | ||
| parser.add_argument("--table", help="workload table whose `data` gets the replay") | ||
| args = parser.parse_args() | ||
|
|
||
|
|
@@ -72,7 +84,7 @@ def main(): | |
|
|
||
| start = min(int(s) for s in steps) | ||
| out = sorted( | ||
| (int(r["timestamp"]) - start, *labels[r["msinstanceid"]], r["cpu_utilization"]) | ||
| (int(r["timestamp"]) - start, *labels[r["msinstanceid"]], r[args.column]) | ||
| for r in rows(args.csv) | ||
| if r["msinstanceid"] in labels | ||
| ) | ||
|
|
@@ -90,7 +102,7 @@ def write_table(table_path, out_path, rows, steps): | |
| paced-feeder's series order (mixed radix, label_0 fastest).""" | ||
| by_series = {} | ||
| for offset, *_, label_0, value in rows: # sorted by offset | ||
| by_series.setdefault(label_0, []).append(round(float(value), 6)) | ||
| by_series.setdefault(label_0, []).append(round(float(value or 0), 6)) | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The PR description says Alibaba values are scaled ×10⁴ because Count-Min top-k rounds weights to integers (#916), but nothing here scales them, and there's no |
||
| values = [] | ||
| for r in range(REGIONS): | ||
| for s in range(SERVICES): | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing values are handled inconsistently.
<out>.csvkeeps the raw empty string fromr[args.column], whilewrite_table(line 105) turns it into0, so the CSV and the JSON table no longer agree. Also,seen(line 62) counts a step even when the value is empty, so a pod with gaps still passes the "reports at every step" filter and its gaps are replayed as made-up zeros. Earlier this input crashed onfloat(''); now it silently corrupts the data. Consider skipping empty values when buildingseen, or filling them in one place before both outputs are written.