Skip to content

e2e: the Chainsaw catch budget counts only the global legs, not the per-suite collectors #3754

Description

hack/e2e-chainsaw/.chainsaw.yaml:57-59 records the worst-case diagnostics cost of a failed test as ~19m, and derives it from the two global error.catch legs: the previous-instance log read at timeout: 6m (:82) and the crust-gather snapshot at timeout: 13m (:133). The figure is exact for those two, and the block says outright that it exists so the arithmetic does not have to be rediscovered.

It does not count the catch collectors that individual suites add. Those run on the same failed test, in addition to the global legs. Counting them is easy to get wrong in the direction of undercounting, for two reasons that are worth stating before the table: a suite's catch can sit at test level (spec.catch) or on a single step (spec.steps[].catch), and a script op that omits timeout: does not run unbudgeted. It inherits timeouts.exec, which is 5m.

The enumeration below is (.spec.catch[]?, .spec.steps[]?.catch[]?) | select(has("script")) | .script.timeout over every hack/e2e-chainsaw/*/chainsaw-test.yaml, so it can be reproduced or refuted rather than taken:

suite catch script ops budget added
harbor 3, all test-level: one omitting timeout: plus two at 2m 9m
qdrant 2: a 2m at test level and a 5m on the verify-pvc-reclaim-on-delete step 7m
bucket 1 test-level, omitting timeout: 5m
openbao 1 at 2m 2m
kubernetes-oidc-system 1 at 2m 2m
kubernetes-oidc-customconfig 1 at 2m 2m

So a failed harbor test can spend ~28m in diagnostics against the ~19m the block states, and a failed qdrant test ~26m.

The gap is not the 2m collectors. It is that harbor and bucket each carry one op with no timeout: at all, and an omitted budget is the largest single contribution while being the only entry a reader cannot see a number for. qdrant's second op is the same shape of surprise from the other direction: it is explicit at 5m, but it lives on a step rather than in spec.catch, so a count that walks only one of the two levels misses it entirely.

This matters because of what the same block says happens at the job ceiling: the E2E job is capped at 180 timeout-minutes, a cap ends the job cancelled, and the ~4m48s grace window is consumed by the earlier Collect report step, so both cozyreport.tgz and the job log are lost. That was measured, on runs 29557003429 and 29568562337. A cascade reddening many suites is the case the block already calls out as spending a large share of the job in diagnostics, and the per-suite collectors make each of those tests cost more than the recorded figure.

Three things worth deciding separately:

  1. Whether the recorded number should include per-suite collectors, or whether the block should state that it excludes them and where to look instead. The block's own closing advice, "bound the number of snapshotted tests rather than shrinking these timeouts", points at the first.
  2. Whether a catch script may omit timeout: here at all. Inheriting timeouts.exec: 5m is what makes harbor and bucket dominant while reading like the cheapest rows in the table.
  3. Whether per-suite catch should be allowed at both nesting levels, given that the two have to be enumerated separately and a partial walk silently undercounts.

Pre-existing: harbor, qdrant, bucket and openbao all carry these at cfaf2e086. The two OIDC rows are the smallest entries in the table and are new; they change the total by 4m and nothing else about the shape of the problem. This is not a regression from a specific change. It is an accounting gap in a comment that is otherwise precise, and worth closing while the comment is still trusted.

Activity

  1. lexfrei commented on Sep 7, 2026

    @lexfrei
    ContributorAuthor

    What that undercount costs at job level, measured on the OIDC pair while raising one of their budgets.

    Once the BYO Ready wait goes to 20m (5m sat below the floor, which is the bootstrap hook's own 600s deadline), one failing suite costs 49m: 20m assert, 5m own catch, 6m and 13m for the two global legs, 5m cleanup that fails anyway because the uninstall cannot start while the install is still running.

    Both suites pay that in the same run. execution.parallel: 1 makes them serial, the runner passes no --fail-fast, and both legs sit on the Configuration's spec.error.catch, so they are charged per failing test rather than once per run. The pair is 98m, and the trigger is ordinary: the release retries forever, so a single platform breakage reds both at full price.

    Composing that against the worst job observed in a 19-run window (138m) puts the total near 226m against the 215m cap. That is a stack of ceilings which have never fired together rather than a bound, so it is not a claim that a run has failed this way.

    Correcting an earlier version of this comment, which softened the attribution in the wrong direction. Computed identically before that change, the same composition is 206m, because the three un-timed collectors it replaces already carried a 15m inherited ceiling, so a failing suite was 39m rather than free. Two things follow and neither replaces the other. 206 against the 215m cap is a fit, with nine minutes to spare, so the change is what crosses it: 226 against 215 is eleven minutes over. And 206 of the 226 is 91%, so almost all of the total was accumulated before, by the collectors this issue is about.

    So the accumulated cost is where the work is, and that is this issue, but the crossing belongs to the change and should not be smoothed over. One 13m leg alone takes 26m off the both-fail path, which is more than the 20m the budget correction adds, so bounding the legs restores the fit on its own.

  2. added
    triage/acceptedIndicates an issue is ready to be actively worked on
    priority/backlogGeneral backlog priority. Lower than priority/important-longterm
    and removed
    triage/needs-triageIndicates an issue needs triage by a maintainer
    on Sep 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/testingIssues or PRs related to testing (e2e, bats, unit tests)kind/bugCategorizes issue or PR as related to a bugpriority/backlogGeneral backlog priority. Lower than priority/important-longtermtriage/acceptedIndicates an issue is ready to be actively worked on

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions