Skip to content

feat(actions): size arc-job-report runners from the job's 7-day history - #382

Merged
JesperTerkelsen merged 1 commit into
mainfrom
feat/arc-job-report-history-sizing
Oct 13, 2026
Merged

JesperTerkelsen merged 1 commit into
mainfrom
feat/arc-job-report-history-sizing

Conversation

@JesperTerkelsen

Copy link
Copy Markdown
Member

Summary

Follow-up to #381. The runner suggestion is now based on the job's last 7 days of runs, and memory and CPU are treated differently. That's because runners have a memory limit but only a CPU request: too little memory gets the job OOM-killed, while too little CPU only makes it slower.

Why one run isn't enough: cache hits change a job's peak memory a lot. Over 6 hours of monorepo-typescript runs:

Job Runs Peak memory p50 Max Runs whose own peak suggests a size the worst run would OOM on
Typecheck 417 1.5 GB 13.1 GB 84%
Build (staging-blind) 417 0.9 GB 4.8 GB 78%
E2E Tests (hub) 346 7.2 GB 10.7 GB 11%

The rule (suggest() in job_resources.py):

  • Memory is a hard floor. The size must hold 1.3 × the job's 7-day peak. If it doesn't, the suggestion is ⬆️ needs memory. An OOM kill in this run always moves up a size.
  • CPU is a labelled trade-off:
    • ⬆️ CPU-bound, faster when the job's median CPU is at least 85% of the runner's vCPU.
    • A smaller size must still cover the job's median CPU. It is fits when it also covers the peak, and trial: may be slower when it doesn't.
  • Smaller sizes need 5 runs in the window. This replaces the 2-minute cut-off, since the history covers short jobs. Bigger sizes don't wait.

The suggestion table now shows the reason, the 7-day peak memory, median and peak CPU, and the run count.

Where the history comes from: the arc:job_memory_working_set_bytes:max1m and arc:job_cpu_usage_cores:rate1m recording rules in https://github.com/monta-app/terraform/pull/261. The report runs four instant queries per run, all grouped by job_name, that read only the selected jobs' recorded series. It never joins raw cAdvisor series at read time; doing that over 7 days OOM-killed the mimir-internal query-frontends today.

Local path: job_resources.py now calls the same Grafana datasource-proxy URL as CI, through gcx api. gcx metrics query silently widened the step to fit about 100 points, so long jobs were coarser locally than in CI.

Testing

  • python3 -m unittest discover -s .github/actions/arc-job-report -p 'test_*.py': 12 tests. They cover every branch of suggest() (memory floor, OOM, CPU-bound, fits/trial, the 5-run gate, runners outside the catalog), the escaping in the history query, and the new table header and row.

  • job_resources.py against https://github.com/monta-app/monorepo-typescript/actions/runs/38407948093 with no recorded history yet: every job is keep and nothing errors.

  • The same run's arc_job_report.py --dry-run, with history stubbed from the 6-hour numbers above:

    • Build: 8cpu → 4cpu, fits
    • Lint: 8cpu → 4cpu, trial
    • Knip: 4cpu → 2cpu, fits
    • Format: 4cpu → 2cpu, trial
    • Test shards: 8cpu → 16cpu, CPU-bound, faster
    • Typecheck: stays on 8cpu, because its 13.1 GB peak needs 17 GB

    That run caught a name clash with the comment's existing HISTORY regex, which is fixed, and the render test now checks the header.

Gaps

  • Needs https://github.com/monta-app/terraform/pull/261 applied. Until then the history queries return nothing and the report suggests only memory and CPU-bound upsizes from the current run. It doesn't fail. There's no backfill, so downsizes start about a week after the rules are applied.
  • Jobs are matched by repository + job_name. Two workflows in one repo with the same job name share a history, which can only make the memory floor higher.
  • The Test gate job and the Test (shard N) jobs are separate names, so they have separate histories. The table row still shows the shard job as "Test", as before.

🤖 Generated with Claude Code

One run is not safe to size on: cache hits change a job's peak memory a
lot (monorepo Typecheck peaks at 1.5 GB in a typical run, 13.1 GB in the
worst), so a cached run suggested a runner the next uncached run would
be OOM-killed on.

The suggestion now reads the job's last 7 days from the
arc:job_memory_working_set_bytes:max1m and arc:job_cpu_usage_cores:rate1m
recording rules (monta-app/terraform) and treats the two resources
differently, since runners have a memory limit but only a CPU request:

- memory is a hard floor: 1.3 x the 7-day peak, and an OOM kill always
  moves up a size;
- CPU is a labelled trade-off: "CPU-bound, faster" when the median is at
  least 85% of the runner's vCPU, and a smaller size must cover the
  median, marked "trial: may be slower" when the peak would not fit.

Smaller sizes need 5 runs in the window, which replaces the 2-minute
cut-off. The local path now calls the datasource proxy through gcx api,
like CI, because gcx metrics query widens the step to about 100 points.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@JesperTerkelsen
JesperTerkelsen marked this pull request as ready for review October 13, 2026 15:17
@JesperTerkelsen
JesperTerkelsen requested a review from a team as a code owner October 13, 2026 15:17
@JesperTerkelsen
JesperTerkelsen requested review from prasad-manu and removed request for a team October 13, 2026 15:17
@JesperTerkelsen
JesperTerkelsen merged commit 79f05d3 into main Oct 13, 2026
1 check passed
@JesperTerkelsen
JesperTerkelsen deleted the feat/arc-job-report-history-sizing branch October 13, 2026 15:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants