docs(TST-30): new finding — single shared Gitea runner is a CI throughput/availability bottleneck
ci / java (push) Successful in 1m58s
ci / windows-x86 (push) Successful in 1m6s
ci / nightly-windev (push) Has been skipped
ci / portable (push) Successful in 8m7s

Surfaced during TST-25 acceptance verification: all CI runs on one shared
gitea-runner (maxParallel=1, co-located 10.100.0.35) interleaved with
dohertj2/lmxopcua, and Gitea 1.26 has no run cancel/delete API, so queue
latency is unbounded under cross-repo contention and the runner is a single
point of failure. Design: add a second/labelled runner; document the no-cancel
reality and the run-windev-ci.sh queue-bypass. Low/P2. Roll-ups updated.
This commit is contained in:
Joseph Doherty
2026-07-13 12:37:10 -04:00
parent 19cbf7be72
commit 4b8ba7a5bd
2 changed files with 31 additions and 5 deletions
@@ -2,7 +2,7 @@
Source review: [60-testing-docs-gaps.md](../60-testing-docs-gaps.md) · Baseline: `main` @ `4f5371f` · Generated: 2026-07-13
This cycle's new findings (TST-25..29) are all fallout or residue of the prior remediation: the Windows CI tier was removed after act_runner proved broken (TST-25), the docs describing that tier were not updated in the same commit (TST-26), a flag SEC-25 made live is still documented as dead (TST-27), a handshake field is asserted only on the side CI cannot run (TST-28), and the root-artifact triage from TST-14 needs its retirement decision refreshed (TST-29). Every citation below was re-verified against the working tree on 2026-07-13.
This cycle's new findings (TST-25..29) are all fallout or residue of the prior remediation: the Windows CI tier was removed after act_runner proved broken (TST-25), the docs describing that tier were not updated in the same commit (TST-26), a flag SEC-25 made live is still documented as dead (TST-27), a handshake field is asserted only on the side CI cannot run (TST-28), and the root-artifact triage from TST-14 needs its retirement decision refreshed (TST-29). Every citation below was re-verified against the working tree on 2026-07-13. **TST-30** was added later on 2026-07-13, surfaced while running TST-25's acceptance checks — the CI infrastructure itself (a single shared runner) is a throughput/availability bottleneck.
Prior-cycle open findings (TST-05..24 where still open) are tracked in the prior design doc, `archreview/remediation/60-testing-docs-gaps.md`; this doc covers only the new findings. Note TST-25 is the unlock for two of those: a working Windows tier is where the scheduled live-MXAccess smoke (old TST-05) and CI-run client wire tests (old TST-24) land.
@@ -15,6 +15,7 @@ Prior-cycle open findings (TST-05..24 where still open) are tracked in the prior
| TST-27 | Medium | P1 (doc batch) | S | — | Not started | `ShowTagValues` config row still says "Reserved" after SEC-25 made the flag live |
| TST-28 | Low | P2 | S | relates IPC-02 | Not started | Gateway-side `max_frame_bytes` handshake field untested in the CI-run suite |
| TST-29 | Low | P2 | S | — | Not started | Retire `oldtasks.md` after folding the Phase-5 governance record into DesignDecisions.md; delete root docs-review artifacts |
| TST-30 | Low | P2 | M | — | Not started | Single shared Gitea runner is a CI throughput/availability bottleneck (cross-repo contention, no run cancel/delete) |
---
@@ -145,6 +146,29 @@ Design details for (b):
---
## TST-30 — Single shared Gitea runner is a CI throughput/availability bottleneck `Low` · `P2` · Effort M
**Finding.** All CI for this repo runs on **one** co-located `gitea-runner` container on docker host `10.100.0.35` with `maxParallel=1` (observed in the runner logs during TST-25 acceptance: `Running job with maxParallel=1`). That runner is **shared across repos** — it interleaves `dohertj2/mxaccessgw` and `dohertj2/lmxopcua` jobs on the single slot — and Gitea's per-repo runner list is empty (`GET /repos/dohertj2/mxaccessgw/actions/runners` → `total_count: 0`), i.e. it is an instance/shared runner, not repo-scoped. Every job in every run executes serially: a single `mxaccessgw` push fans out to `portable` (~5 min), `java`, `windows-x86`, so the wall time for the queue behind it is additive, and an active `lmxopcua` run blocks `mxaccessgw` entirely. Compounding it, this Gitea version (1.26.4) exposes **no run cancel or delete via the API** (`POST .../actions/runs/{id}/cancel` → 404; `DELETE .../actions/runs/{id}` → 400), so superseded or stale runs cannot be cleared and consume the slot in full before newer runs start.
**Impact.** Low severity, real drag. CI feedback latency is unbounded under cross-repo contention (observed ~2030 min queue depth during TST-25 verification, forcing direct local `run-windev-ci.sh` verification to bypass the queue). Because the runner is the *only* one, it is also a single point of failure — if it wedges or its host is down, **all** CI (both repos) stops, with no failover; and there is no way to abort a run that is hung or was obsoleted by a newer push, so a stuck job can hold the slot until its internal timeout. This does not threaten correctness (jobs still run and report accurately) but it undercuts the fast-feedback purpose of the TST-25 tier and makes the CI system fragile in exactly the way act_runner history (TST-25) warns about.
**Design.** Add a **second runner** so the two repos (and the fan-out jobs) stop contending on one slot, and prefer repo/label scoping so `mxaccessgw`'s Windows-adjacent jobs aren't starved by an unrelated repo. Options, cheapest first:
- **(a) Raise `maxParallel` / register a second runner instance on `10.100.0.35` — recommended.** The host already runs `gitea-runner`; add a second `act_runner` instance (or bump the existing runner's capacity where the docker-in-docker/resource budget allows) so at least two jobs run concurrently. Cheapest change, keeps the co-located-with-`gitea:3000` network property that TST-03 depended on (`container.network: traefik`).
- **(b) Dedicate a runner to `mxaccessgw`** with a distinct label and gate the CI jobs on it, so `lmxopcua` load never blocks this repo. Cleaner isolation than (a) but needs label wiring in `ci.yml` (`runs-on: [ubuntu-latest, mxgw]` or similar) and a labelled runner registration.
- **(c) Put the runner on windev / a second host** — rejected as the primary fix: windev is the Windows build target, not a CI host, and co-locating the Linux runner there loses the `gitea:3000` resolution TST-03 relies on. Only consider if `10.100.0.35` runs out of capacity.
Independent of the runner count, document the **no-cancel** reality (Gitea 1.26 API) and the escape hatch: a specific SHA can be verified out-of-band via `scripts/ci/run-windev-ci.sh` (Linux) or the manual windev worktree flow, bypassing the queue — this is already the degraded-mode path in `scripts/ci/README.md`; TST-30 generalizes it from "windev tier down" to "runner contended".
**Implementation.**
1. Register a second `act_runner` on `10.100.0.35` (option a) with the same `container.network: traefik` config (repo memory `project_gitea_ci`), or a labelled dedicated runner (option b) and add the label to `ci.yml`'s `runs-on`. Confirm both runners appear and pick up jobs concurrently (two pushes should no longer serialize end-to-end).
2. `docs/GatewayTesting.md` (Continuous Integration section) — note that the runner is shared/finite, that queue latency under cross-repo contention is expected, that this Gitea version has no run cancel/delete, and that `scripts/ci/run-windev-ci.sh <sha>` / the manual worktree flow verify a commit without waiting for the queue.
3. Optional: add a workflow-level `concurrency` group to `ci.yml` so a newer push to the same branch supersedes an in-flight run *if* the deployed Gitea honors it (belt-and-suspenders against the missing cancel API; verify it actually cancels before relying on it).
**Verification.** Push two branches back-to-back and confirm their runs execute concurrently (not serially) once a second runner exists; `GET /repos/dohertj2/mxaccessgw/actions/runners` (or the instance runner list) shows ≥2 runners online; `docs/GatewayTesting.md` describes the shared-runner/no-cancel reality and the bypass. Re-run the TST-25 acceptance push and confirm queue depth is materially lower under a concurrent `lmxopcua` run.
---
## Cross-domain dependencies
- **TST-25 → old TST-05 / old TST-24:** the SSH-driven nightly is where the scheduled live-MXAccess smoke lands (closes old TST-05), and a working CI Windows tier unblocks wiring client wire-behavior tests into CI (old TST-24).