## What this fixes The `SLO-Measurement` shard (`slo-timeslice.spec.js` + `slo-alerts.spec.js`) has been taking **~26 minutes** ## Root cause Both specs seed **8 days of history** and create SLOs with a **7-day rolling window**, then block in `beforeAll` until the SLO is actually measured (`waitForSloMeasured`). The server backfills history in `ZO_SLO_BACKFILL_CHUNK_SECS` chunks — default **86400s (1 day)** — and only **one chunk at a time** (`ZO_SCHEDULER_SLO_BACKFILL_CONCURRENCY=1`, by design, so bulk backfill never starves real-time alerts). The shard backfills **3 SLOs** across the two specs, each needing 7 daily chunks to cover its 7-day window: - `slo-timeslice.spec.js` `beforeAll`: 2 SLOs (`sloLt`, `sloGt`) × 7 chunks = 14 - `slo-alerts.spec.js` `beforeAll`: 1 SLO × 7 chunks = 7 **21 chunks total, strictly serial**, at roughly 40s/chunk ≈ **~14 minutes of pure backfill wait**, before any UI assertion runs. This is the server behaving correctly and exactly as designed for production — the test is just paying for it the slow way. ## Fix Neither spec asserts on per-chunk timing or partial coverage, only on the final measured state, so raising the chunk size to cover the whole window in one pass is free wall-clock, not a behavior change. - Threaded `slo_backfill_chunk_secs` through `build-ci-matrix.js`'s existing per-shard passthrough, same pattern already used for `ingest_allowed_upto` / `quick_mode_enabled`. - Set it to `604800` (7 days) for the `SLO-Measurement` shard only — the server-wide default (`86400`) is untouched everywhere else. - Documented the new knob in `tests/ui-testing/ci-matrix/README.md`. ## Validation **Synthetic (API-only), same seed shape as the real specs** (8 days, 1-min interval, deterministic 3h fault window), against a local build: | | Default (86400s chunk) | Fixed (604800s chunk) | |---|---|---| | Time to measured | 246.9s (~4.1 min) for **one** SLO, stepping 1/7 → 2/7 → ... every ~40s | **3.0s** | | Final SLI | 98.21428571428571% | 98.21428571428571% (**identical**) | Same math, same result — only the number of scheduler round-trips changes. **Real shard, end-to-end**, run exactly as CI invokes it (`npx playwright test slo-timeslice.spec.js slo-alerts.spec.js --workers=1`) against a server mirroring this shard's full env including the fix: - **25/25 tests passed**, 0 regressions - Total wall-clock (server start → global teardown): **4m Avg** Local timing won't map 1:1 onto the GitHub Actions runner (different disk/scheduler characteristics — even the "before" baseline was faster locally than the ~26min/~5min-per-spec seen in CI), so the authoritative number is whatever this PR's own CI run reports. But the structural win — 1 chunk instead of 7 per SLO — is verified correct and regression-free independent of machine. ## Companion PR The enterprise repo's `playwright.yml` independently consumes per-shard matrix fields (it explicitly mirrors OSS's `ingest_allowed_upto` handling for the same reason), so it needs the equivalent env passthrough or it'll keep paying the old cost even after this merges. Companion PR: openobserve/o2-enterprise#2460
Playwright CI shard matrices — single source of truth
Manifest index
Each Playwright workflow builds its matrix from one of these JSON files (via a
generate_matrix job). Shared workflows use an OSS base + an ENT overlay
(*.ent.json, in the enterprise repo); ENT-only workflows use a standalone manifest.
| Manifest | Drives workflow | Kind |
|---|---|---|
ci_matrix.json (+ ENT ci_matrix.ent.json) |
playwright.yml (PR gate) |
shared base + overlay |
ci_matrix_regression.json (+ ENT ci_matrix_regression.ent.json) |
playwright_regression.yml |
shared base + overlay |
ci_matrix_cloud.json (ENT repo) |
playwright_alpha1.yml |
ENT-only standalone |
ci_matrix_env.json (ENT repo) |
playwright_env.yml |
ENT-only standalone |
ci_matrix_env_scheduled.json (ENT repo) |
playwright_env_scheduled.yml |
ENT-only standalone |
ci_matrix_firefox.json (ENT repo) |
playwright-firefox-ondemand.yml |
ENT-only standalone |
Base manifests + build-ci-matrix.js live in OSS; overlays and ENT-only manifests live
in o2-enterprise/tests/ui-testing/ci-matrix/. The merge script is shared (ENT reuses it
from its OSS checkout).
The shared PR-gate matrix (below refers to ci_matrix.json)
ci_matrix.json (this directory) is the only place the Playwright UI shard list
lives. Both the OSS and Enterprise playwright.yml workflows build their test matrix
from it at run time via .github/scripts/build-ci-matrix.js, so a spec added here runs
in both repos automatically — no more hand-syncing two workflow files.
Adding / moving a spec
- A spec both OSS and ENT run: edit
ci_matrix.jsononly. Add the filename to therun_filesof the right shard (testfolder). Done — ENT picks it up on its next run. - An enterprise-only spec: edit
o2-enterprise/tests/ui-testing/ci-matrix/ci_matrix.ent.json(the overlay), never this file. Two shapes:- add it to an existing shared shard →
"append": { "<testfolder>": ["my.spec.js"] } - a whole new ENT-only shard → add an object to
"shards": [ … ].
- add it to an existing shared shard →
- A new shard: add a new object to
ci_matrix.jsonwithtestfolder,actual_folder,browser,run_files.
Fields
| field | meaning |
|---|---|
testfolder |
shard label — becomes the job name e2e / <testfolder> (must be unique) |
actual_folder |
real directory under playwright-tests/ (e.g. Logs-Core → Logs) |
browser |
chrome |
run_files |
spec filenames run by this shard |
disabled |
(optional) specs intentionally turned off — see below |
quick_mode_enabled |
(optional) true starts this shard's server with ZO_QUICK_MODE_ENABLED |
ingest_allowed_upto |
(optional) hours of backdated ingestion this shard's server accepts |
workers |
(optional) pin --workers=N for this shard |
The last three exist because a shard is the smallest unit that can change them.
ingest_allowed_upto— the workflow default is 5 hours, and older rows are dropped while ingestion still answers200. A suite that seeds history needs a wider window;SLO-Measurementsets240because SLOs measure a rolling 7-day window. Shards that omit it keep the workflow default.workers—fullyParallelruns separate spec files concurrently, andtest.describe.configure({ mode: 'serial' })only orders tests within one file. A shard whose specs contend for a shared server-side resource must pinworkers: 1; the specs cannot express that themselves.SLO-Measurementdoes, because both of its specs wait on the SLO backfill job, which runs withZO_SCHEDULER_SLO_BACKFILL_CONCURRENCY=1.slo_backfill_chunk_secs— the server default is 86400 (1 day), so a test SLO with a 7-day window backfills in 7 sequential chunks under the concurrency-1 rule above.SLO-Measurementsets604800so each of its 7-day-window SLOs backfills in a single chunk instead. Safe only because no spec in that shard asserts on per-chunk timing or partial coverage — only on the final measured state. If a future spec in this shard needs to see backfill mid-flight, give that spec its own shard rather than lowering this back down.
Disabling a spec (JSON has no // comments)
Every shard ships with a "disabled": [] placeholder, so turning a spec off is a
fill-in-the-blank — don't delete the spec you want to remember, move it into that
shard's disabled array with a reason. build-ci-matrix.js never emits disabled, so
those specs don't run, but the record survives and is git-diffable:
{
"testfolder": "Alerts",
"run_files": ["alerts-ui-operations.spec.js"],
"disabled": [
{ "file": "alerts-e2e-flow.spec.js", "reason": "flaky; pending rewrite" }
]
}
A spec cannot be in both run_files and disabled — the build fails if it is. Any
_comment (or _-prefixed) key is also ignored, for free-form notes.
Enterprise-only disabled specs go in the overlay's disabled map, keyed by shard:
"disabled": { "Alerts": [ { "file": "…", "reason": "…" } ] }.
The ENT overlay only ever carries the delta from OSS. It must not re-list any spec
already in ci_matrix.json; build-ci-matrix.js fails the run if it does.