Skip to main content

mz-console-scalability

Run the Console scalability suite against a replayed captured workload and measure what Console load costs mz_catalog_server: a replay-only baseline, the Playwright suite, the k6 ramp, and an optional sweep of N headless viewers fitted to CPU per viewer. Trigger: "console scalability test", "run the console against a captured workload", "what does this page cost catalog_server", "cost per viewer", "how many users fit on M.1-nano", "catalog server CPU under console load", or comparing two console versions on catalog_server cost. For one query's plan and latency use mz-query-perf. For SQL throughput benchmarks under test/scalability use mz-benchmark.

Informations de source

Dépôt
MaterializeInc/materialize
Dernière activité de la source
28 octobre 2026 à 15:01
Langue détectée de SKILL.md
anglais
Étoiles
6 391
Forks
516

Options d'installation

Le prompt qui vérifie d'abord la source est sélectionné par défaut. Vous pouvez passer à une commande directe ou télécharger une copie locale.

Vérifiez les fichiers source

Lisez SKILL.md et les fichiers associés affichés par SkillsMP avant de décider de l'installer.

Affichage de SKILL.md

SKILL.md
Instructions source · Aperçu en lecture seule
name
mz-console-scalability
description
Run the Console scalability suite against a replayed captured workload and measure what Console load costs mz_catalog_server: a replay-only baseline, the Playwright suite, the k6 ramp, and an optional sweep of N headless viewers fitted to CPU per viewer. Trigger: "console scalability test", "run the console against a captured workload", "what does this page cost catalog_server", "cost per viewer", "how many users fit on M.1-nano", "catalog server CPU under console load", or comparing two console versions on catalog_server cost. For one query's plan and latency use mz-query-perf. For SQL throughput benchmarks under test/scalability use mz-benchmark.
argument-hint
["workload-file"]
allowed-tools
["Bash","Read","Grep","Glob"]
# Console scalability and catalog_server cost The `console-scalability` workflow in `test/workload-replay/mzcompose.py` boots a captured workload (objects, initial data, hydration), keeps its ingestions and queries running, and drives the console bundled in the materialized image against it. Every console query runs on `mz_catalog_server`, so its CPU is what limits how many viewers an environment can hold. ## What a run produces Per workload, in order: | Phase | Load on top of the replay | Other output | |---|---|---| | `baseline` | none | | | `playwright` | the `console/e2e-tests/scalability` specs, 1 to 6 tabs | `console/test-results/<workload>/`, query tables in the log | | `k6` | the suite's captured queries, ramped to 100 virtual users over the SQL HTTP API | `console/e2e-tests/k6/{summary,queries}-<workload>.json` | | `baseline-before-viewers`, `viewers-<N>`, `baseline-after-viewers` | with `--viewers`, N headless viewers holding one page | | `console/test-results/catalog-server-<workload>.json` holds, per phase: * `cores.catalog_server`, `cores.environmentd`: process CPU from /proc in cores. Absolute, not above baseline. `null` when the process restarted during the phase. * `scheduled_cores`: time inside dataflow operators on catalog_server, split into `index`, `subscribe`, `introspection_subscribe` and `other`. * `subscribes`: open subscribes at the end of the phase, and the console subscribe dataflows' count and average operator count. * `top_dataflows`: the ten non-subscribe dataflows with the most scheduled time, with operator counts. * `succeeded`: `false` when the suite or k6 exited with an error. The phase still holds what ran before the failure, so check `seconds` against a normal run before reading it. With `--viewers`, `viewer_fit.<process>.mcpu_per_viewer` is a least-squares slope through the origin of each step's cores above the mean of the two viewer baselines, and `above_baseline` lists the points it was fitted to. The log ends with the same numbers as a table. The nightly `console-scalability` step runs with `--skip-large` and no `--viewers`. Its artifacts carry the first three phases for every workload, and it uploads one row per phase to the `console_scalability_result` table of test analytics. ## Run it locally ### Before you start * **Run from a commit whose images CI has built.** mzcompose pulls images by fingerprint and builds the ones ghcr lacks, and a local materialized build is a full Rust cross-compile (budget 100+ GiB of `target-xcompile` in a fresh worktree). The materialized image bundles the console, so any file under `console/` changes its fingerprint, untracked files included. Git-ignored files do not count. CI's build step pushes every image it builds, so a pushed branch becomes runnable once its build step for your architecture has finished. Check before starting: ```bash bin/mzcompose --find workload-replay config | sed -n 's/.*image: \(ghcr[^ ]*\)/\1/p' | sort -u | while read -r image; do docker image inspect "$image" > /dev/null 2>&1 || docker manifest inspect "$image" > /dev/null 2>&1 \ && echo "ok $image" || echo "MISSING $image" done ``` * **Python 3.13** must be the `python3` on PATH, since `bin/pyactivate` runs it. When `VIRTUAL_ENV` points at another venv, as VS Code terminals set it, `bin/pyactivate` installs into that venv. Set `VIRTUAL_ENV=$PWD/misc/python/venv`. * **Captured workloads** come from a private repository the workflow clones into `test/workload-replay/captured-workloads`. In a worktree, symlink the main checkout's copy. Workloads tagged `settings.large: true` take far longer and more memory. * **Keep the host awake and the run detached.** Laptop sleep was observed to halt environmentd (exit 166, a persist `Listen ... received a batch` halt after the timestamp jumped), and nothing restarts it, so the run is lost. A command an agent runs in the background is killed at the tool's time limit, which stops the replay. Start the run with `nohup caffeinate -dis ... &` on AC power and follow the log. `caffeinate` cannot stop a lid-close sleep on battery. * **One run per compose project.** A run starts by killing and removing every container of its project. Pass `--project-name <name>` to run beside another replay. ### Command ```bash PYTHONUNBUFFERED=1 nohup caffeinate -dis bin/mzcompose --find workload-replay \ run console-scalability --viewers 10,20,30 --cost-window 120 workload_ddnet.yml \ > console-scalability.log 2>&1 & grep -E '^\+\+\+|^-- catalog_server cost|Traceback' console-scalability.log ``` On a laptop this takes about 55 minutes: about 10 to boot and hydrate, 20 for the Playwright suite, 6 for k6, and the rest for the viewer sweep. | Flag | Default | Notes | |---|---|---| | `--catalog-server-size` | the bootstrap size | Same worker count and CPU limit as `M.1-nano`. Pin another size to compare. | | `--console-bundle` | the image's console | A `dist` directory to serve instead, copied in after hydration. See "Compare two console versions". | | `--viewers` | none | Comma-separated viewer counts. Use three or more to see whether cost grows linearly. | | `--viewer-path` | the `mz_catalog_server` cluster page | Console path the viewers hold, such as `/regions/local-flexible-deployment/clusters/u2/compute_cluster`. The page matters: on `workload_ddnet`, `main` measured 9.5 mCPU per viewer on the `mz_catalog_server` page and 7.3 on a user cluster's page. User cluster ids follow the order of `clusters` in the workload file, starting at `u2`. | | `--cost-window` | 90 | Seconds per measured window: the baselines and each viewer step. | | `--cost-settle` | 30 | Seconds of settling before each window. | | `--seed` | time | Seeds the replay's generated data and query choices. Fix it when comparing runs. | The replay's own `--factor-initial-data`, `--factor-ingestions`, `--factor-queries` and `--max-concurrent-queries` apply too. ## Read the numbers * **Subtract the baseline.** Every core figure is absolute and includes the replay's own queries. What the console costs is a phase minus `baseline`. * **Check the fit before quoting it.** The slope assumes cost grows in proportion to viewers. On `workload_ddnet` the cost per viewer was lower at 10 viewers than at 20 or 30, so treat `mcpu_per_viewer` as an average over the measured range and quote it with that range. A single step far off the others usually means its pages did not finish loading: look for `page-layout not found` in the log. * **Process CPU minus scheduled time** is work outside dataflow operators: session handling, peeks served from indexes, networking, and dataflows that were dropped before the phase ended. A gap that grows with polling load points at per-request cost. Each new SQL session was measured at about 35 ms of catalog_server CPU, and every HTTP `/api/sql` request opens one, where a WebSocket keeps one session. * **Operator counts explain most dataflow cost.** Measured on a one-worker catalog_server: about 3 microcores per operator for a maintained index over static input, and 3.5 to 5 microcores per operator per viewer for a console subscribe. A subscribe with hundreds of operators per viewer is the slope to cut, see `subscribes.avg_operators`. * **Local readings are not capped.** Observed: the process orchestrator does not enforce the replica's CPU limit, so catalog_server can read above the 0.5 cores an `M.1-nano` gets in production, up to about one core per worker. A reading above 0.5 means production would saturate. * **Do not read `mz_cluster_replica_metrics_history` locally.** It was observed reading 2.7 to 3.2 times the /proc figure. * **k6 over-polls queries it does not know.** `console/e2e-tests/k6/cluster-detail.ts` replays every label in the captured `queries.json` and polls a label missing from its `POLL_INTERVAL_MS` table every 5 seconds per virtual user. A console change that adds queries therefore reads costlier in the `k6` phase than it is. Diff the two runs' `queries-<workload>.json` labels, and add the new ones to `POLL_INTERVAL_MS` with the console's real cadence before comparing `k6`. The viewer sweep runs the real console and has no such gap. * **Browsers share the host.** The viewers and the suite run on the same machine as materialized. Compare runs on the same host, and repeat a run before trusting a difference of a few percent. ## Compare two console versions Keep the CI-built images and swap only the console. Build each version in its own worktree the way the image does, then run once per build with the same host, workload, `--seed` and flags: ```bash (cd console && CONSOLE_DEPLOYMENT_MODE=flexible-deployment yarn build) bin/mzcompose --find workload-replay run console-scalability --seed 1234 \ --viewers 10,20,30 --console-bundle /path/to/worktree/console/dist workload_ddnet.yml ``` Run the baseline side without `--console-bundle`, or with a build of the base commit. A feature behind a LaunchDarkly flag is on in this mode unless `disabledFlexibleDeploymentFlags` in `console/src/config/flexibleDeploymentFlags.ts` turns it off, so force it on in the build's worktree, never in a commit. To check the swap took, compare `curl http://127.0.0.1:<6874 port>/index.html` with the build's `dist/index.html`, and diff the two runs' `queries-<workload>.json` labels: the new page's queries should appear. Compare each phase minus its own run's baseline, and `mcpu_per_viewer`, not absolute cores.
Voir sur GitHub