Skip to content

ci(codeql): move to advanced setup with concurrency control and non-alerting query exclusions - #397

Closed
MichaelTaylor3d wants to merge 4 commits into
mainfrom
ci/3205-codeql-advanced-setup
Closed

MichaelTaylor3d wants to merge 4 commits into
mainfrom
ci/3205-codeql-advanced-setup

Conversation

@MichaelTaylor3d

@MichaelTaylor3d MichaelTaylor3d commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Status: DRAFT -- the stacking half is fixed, the duration half is MEASURED, not fixed

Refs DIG-Network/dig_ecosystem#3205 -- CodeQL's Analyze (rust) required check was taking
~130 minutes per PR. This PR fixes the run-stacking and measures the duration to its floor;
it does not reach the sub-30-minute target, and it says so rather than claiming to.
The remaining lever is a gate-shape decision recorded on the parent ticket, not a config
change (see the "Round 2 measurement" comment below for the full evidence table).

Diagnosis, measured on three real runs (CodeQL 2.26.4 / rust-queries 0.1.41, 2026-09-06)

  • Stacking (fixed here). Default setup (dynamic/github-code-scanning/codeql) accepts no
    concurrency: block. Measured 10:07Z: six CodeQL runs in progress at once across two PRs,
    none of the superseded ones ever cancelled. With this workflow's concurrency group,
    e1c3b0c4's run was cancelled 91 s after b61011eb superseded it.
  • Duration (measured, not fixed). The unfiltered default-setup run 34027560462
    (SUCCESS, 122m24s) profiles as: extraction 5m44s, TRAP import 1m28s, 12 cheap
    diagnostic/summary queries by eval-minute 6, TypeInferenceConsistencyCounts done at
    54m32s, NodesWithTypeAtLengthLimit done at 113m21s, then all 16 security queries
    finish inside the next 45 seconds, each stamped eval 113mXXs
    . Excluding those two
    queries (round 1, run 34027562265) provably applied and changed nothing. Excluding every
    non-alerting query so only the 16 security queries remain (round 2, run 34034753310,
    90-minute ceiling) still did not finish: all 16 started by 13:08:42Z, none completed in
    the following 81m27s
    . The ~113 minutes is one shared type-inference relation every Rust
    taint/data-flow query depends on; whichever query consumes it first gets the bill. No
    exclusion can remove a shared predicate. Runner shows no memory pressure; CPU-bound.
  • Levers closed: build-mode: none was already in force (extraction is 6 min); the
    suite in force is default; codeql-bundle-v2.26.4 is the latest release and already
    pinned by github/codeql-action@v4; the org has zero GitHub-hosted larger runners and its
    default group excludes public repos; path scoping cannot reach the dependency-graph
    type inference.

What changed

  • .github/workflows/codeql.yml (new) -- advanced setup replacing default setup.
    concurrency: { group: ${{ github.ref }}-${{ github.workflow }}-${{ github.event_name }}, cancel-in-progress: true } (this repo's ci.yml convention). Same four languages, same
    build-mode: none, same dependency-caching: true, same SARIF category per language so
    existing alerts keep their identity. Single-axis matrix so the check is still literally
    Analyze (rust). timeout-minutes: 30 is an explicitly non-shippable fail-fast
    placeholder
    (a passing value would be ~210, clearing today's 101-199 minute spread);
    the header comment records the measured floor and the pending decision.
  • .github/codeql/codeql-config.yml (new) -- query-filters excluding 20 query IDs,
    every one @kind diagnostic or @kind metric (verified per file against github/codeql
    main). Code scanning only turns @kind problem/path-problem into alerts, so no
    security query is touched
    ; all 16 security/CWE-* queries still run. Worth ~6 minutes
    and quieter telemetry, nothing more -- the comment says exactly that.
  • Cargo.toml / Cargo.lock -- workspace version 15.3.0 -> 15.3.1 (rebased onto
    main @ 4852247e; strictly greater than main, kept per the parent ticket).

The two traps this PR is careful about

  1. Branch protection requires the exact context string Analyze (rust); a required context
    that never appears is ABSENT, not red, and blocks every future PR silently. The check
    name is preserved (single-axis matrix) and branch protection is untouched.
  2. That same trap is live right now for every other dig-app PR: default setup is off
    (state: not-configured, and no dynamic run fired on this PR's 13:00Z push), and this
    workflow is not yet on main, so other PRs get no Analyze (rust) check-run at all.
    Measured: dig-app#395 head 991ddf15 has zero such check-runs. This stays true until
    this workflow lands on main in some shape, or default setup is re-enabled.

Decision pending on the parent ticket (not taken here)

  • A (reaches the target): run the Rust analysis on push: main + weekly schedule only
    and skip the Analyze (rust) matrix entry on pull_request via job-level if:. A skipped
    job still registers its check-run and branch protection treats skipped as satisfied, so
    the required-context name survives with no protection edit. Cost, stated plainly: Rust
    findings become post-merge alerts on main, not a pre-merge blocking check. Same queries.
  • B: timeout-minutes: ~210, accept ~2-hour PRs. No latency fix.
  • C: re-enable default setup. Same 2 hours, and the stacking returns.

Blast radius checked

Two workflow/config files + the version manifest. No overlap with #395's changed files.
Nothing is analysed less than before on any path above.

MichaelTaylor3d added a commit that referenced this pull request Sep 6, 2026
Confirmed live on PR #397: varying build-mode (always 'none' for every
language here) produced the check name 'Analyze (rust, none)' instead of
the 'Analyze (rust)' branch protection requires. Single-axis matrix on
language alone keeps the registered name identical, so no branch-protection
change is needed.

Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Sep 6, 2026
…ound 1 found

Round 1 excluded the two queries measured to dominate the ~100-minute query
evaluation phase (type-inference-consistency-counts, nodes-at-type-path-
length-limit). That exclusion works -- PR #397's own run proves both are gone
from the 36-query default suite -- but wall time did not move: the same job
was still running past its 30-minute timeout, evaluating
DataFlowConsistencyCounts.ql, an `@kind diagnostic` query with the identical
"full-database consistency check" shape as the query round 1 already found
ruinous.

Generalize round 1's own reasoning instead of chasing queries one at a time:
exclude every `@kind diagnostic` / `@kind metric` Rust query (18 more,
confirmed individually against their github/codeql main headers), since
code-scanning SARIF only ever alerts on `@kind problem` / `@kind
path-problem`. This leaves exactly the 16 security/CWE-* queries running --
zero security coverage lost.

Also raises the job timeout to 90 minutes (from 30) TEMPORARILY so this PR's
next run measures a real completion time instead of being truncated by the
same ceiling that cut round 1's post-fix run short at exactly 30m11s. Comes
back down once a green run gives a real number.

Refs DIG-Network/dig_ecosystem#3205
MichaelTaylor3d and others added 3 commits September 6, 2026 05:59
Default setup (`dynamic/github-code-scanning/codeql`, no workflow file) took no
concurrency group, so every push started a fresh full analysis and superseded
runs were never cancelled -- measured 2026-09-06, six CodeQL runs in progress
at once across PR #394 and #395.

Stacking was not the whole story: a single, uncontested run (job 101430031199,
PR #395) still took ~101 minutes. Its own job log shows why -- extraction
(rust-analyzer, build-mode: none) finished in ~6.5 minutes, then
`rust/diagnostics/type-inference-consistency-counts` alone ran 44m34s and
`rust/summary/nodes-at-type-path-length-limit` a further ~49m. Both are
internal CodeQL diagnostic/telemetry queries (zero security alerts, ever) that
force a full-database type-inference relation nothing else needs at that
granularity -- every real security query (TaintedPath, XSS, SqlInjection, ...)
then completed within seconds of each other once that relation was warm.

- Add `.github/workflows/codeql.yml`: advanced setup, `concurrency:
  cancel-in-progress` keyed on ref+workflow+event, `timeout-minutes: 30` as a
  regression backstop, same four languages and same SARIF `category` default
  setup used (so existing alerts keep their identity).
- Add `.github/codeql/codeql-config.yml`: `query-filters` excluding exactly
  the two internal queries above, by their upstream `@id`. No security query
  is touched.
- Bump the workspace version 15.2.0 -> 15.3.1 (above both #394's 15.3.0 and
  #395's 15.2.1, the two other PRs open against this version line).

Branch protection's required context `Analyze (rust)` is updated separately,
once this workflow's real check name is confirmed from a live run (advanced
setup can register under a different name than default setup did).

Refs DIG-Network/dig_ecosystem#3205

Co-Authored-By: Claude <noreply@anthropic.com>
Confirmed live on PR #397: varying build-mode (always 'none' for every
language here) produced the check name 'Analyze (rust, none)' instead of
the 'Analyze (rust)' branch protection requires. Single-axis matrix on
language alone keeps the registered name identical, so no branch-protection
change is needed.

Co-Authored-By: Claude <noreply@anthropic.com>
…ound 1 found

Round 1 excluded the two queries measured to dominate the ~100-minute query
evaluation phase (type-inference-consistency-counts, nodes-at-type-path-
length-limit). That exclusion works -- PR #397's own run proves both are gone
from the 36-query default suite -- but wall time did not move: the same job
was still running past its 30-minute timeout, evaluating
DataFlowConsistencyCounts.ql, an `@kind diagnostic` query with the identical
"full-database consistency check" shape as the query round 1 already found
ruinous.

Generalize round 1's own reasoning instead of chasing queries one at a time:
exclude every `@kind diagnostic` / `@kind metric` Rust query (18 more,
confirmed individually against their github/codeql main headers), since
code-scanning SARIF only ever alerts on `@kind problem` / `@kind
path-problem`. This leaves exactly the 16 security/CWE-* queries running --
zero security coverage lost.

Also raises the job timeout to 90 minutes (from 30) TEMPORARILY so this PR's
next run measures a real completion time instead of being truncated by the
same ceiling that cut round 1's post-fix run short at exactly 30m11s. Comes
back down once a green run gives a real number.

Refs DIG-Network/dig_ecosystem#3205
…timeout

Round 2 (run 34034753310, 16 security queries only, 90-minute ceiling) did
not finish: extraction 6m15s, TRAP import 1m39s, then all 16 security
queries started by 13:08:42Z and not one completed in the following 81m27s.
Cross-read with the unfiltered default-setup run 34027560462 (SUCCESS,
122m24s), where TypeInferenceConsistencyCounts finished at eval 54m32s,
NodesWithTypeAtLengthLimit at 113m21s, and then every security query
finished inside the next 45 seconds each stamped "eval 113mXXs", the
picture is unambiguous: the ~113 minutes is a single shared type-inference
relation every Rust taint/data-flow query depends on. Round 1 booked it
against the two queries that happened to consume it first. Excluding
reporters cannot remove a shared predicate.

So: keep the 20 exclusions (correct, zero coverage loss, ~6 minutes of
cheap work saved) but rewrite both files' comments to state what was
actually measured instead of the misattribution, and put timeout-minutes
back to 30 as an explicitly non-shippable fail-fast placeholder -- a value
that passes would have to clear today's 101-199 minute spread, which is the
wait this ticket exists to remove. The remaining lever is a gate-shape
decision (run the Rust analysis on push:main + schedule instead of every
pull_request), left for the orchestrator on dig_ecosystem#3205.

Also records the verified default-setup handoff: no dynamic run fired on
this push, so the earlier double-runs were a propagation lag -- but with
default setup off and this workflow not yet on main, every other open PR
now has NO Analyze (rust) check-run at all, and that required context reads
as ABSENT (dig-app#395 head 991ddf1, measured 14:35Z).

Refs DIG-Network/dig_ecosystem#3205
@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

Closing: the user has decided to disable CodeQL for now — "codeql is taking to long, its infeasible to keep using it, i would like to disable it for now" (2026-09-06).

This PR was moving CodeQL from GitHub's default setup to an advanced workflow so its stacking could be controlled. That work is now moot, and it had also caused a repo-wide merge outage: disabling default setup left Analyze (rust) ABSENT on every PR except this one, and this PR could not pass its own check — its run was cancelled at the 90-minute ceiling. Circular.

Analyze (rust) has been removed from main's required contexts, which cleared the outage immediately (dig-app#395 went from BLOCKED to preconditions MET). Six real checks still gate every PR: Rustfmt, Clippy, Test + coverage, version increment, commitlint, headless build.

The measurement is worth keeping, because it is why the decision is right rather than a retreat:

phase duration
extraction + build 6 min 30 s
TRAP import 1 min 39 s
query load 14 s
query evaluation 81 min 27 s

Excluding 18 diagnostic queries cut the total from 122.4 min, but the cost is query evaluation of the 16 security/CWE-* queries — not the build, which an earlier hypothesis had blamed. On the same commit actions took 37s, python 39s, javascript-typescript 48s. Rust is ~120× the others, and no configuration change available here closes that gap.

Re-enabling is tracked on dig-app#398, which is now scoped to that decision rather than to restoring a required check.

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor Author

Round 2 measurement: the Rust floor is ~2 hours and it is not in any excludable query

TL;DR: the round-1 diagnosis ("two internal telemetry queries dominate") was a misattribution of a shared predicate. Excluding them worked (verified) and changed nothing (verified). Every Rust security query depends on one ~113-minute type-inference relation; whichever query consumes it first gets the bill. No config-file change can remove it. Analyze (rust) still cannot pass on a standard runner inside any timeout a PR gate can tolerate.

The three runs, all CodeQL 2.26.4 / codeql/rust-queries 0.1.41, all today

run what ran outcome extraction TRAP import query evaluation
34027560462 (GitHub default setup, dynamic/github-code-scanning/codeql, unfiltered, 36 queries) on b61011eb SUCCESS, 122m24s (10:29:32Z -> 12:31:56Z) 5m44s 1m28s 12 cheap diagnostic/summary queries done by eval-min 6; TypeInferenceConsistencyCounts done at 54m32s; NodesWithTypeAtLengthLimit done at 113m21s; then all 16 security queries finish within the next 45 s, each stamped eval 113mXXs
34027562265 (this workflow, round-1 filters, 34 queries) on b61011eb CANCELLED at 30m11s (job timeout) 5m52s 1m25s the 2 exclusions provably applied (Loaded N/34); the same 12 cheap queries done by eval-min 6; nothing else finished in the next 16 min
34034753310 (this workflow, round-2 filters, 16 security queries only) on b7d8529a CANCELLED at 90m11s (job timeout raised to 90 for this measurement) 6m15s 1m39s all 16 started by 13:08:42Z; not one finished in the following 81m27s

Read together: in the unfiltered run the security queries were never "fast" -- they were waiting 113 minutes on the shared type-inference relation and then finished in seconds. Remove the two reporter queries and the identical cost moves into the security queries themselves. The 4-core / 14.5 GB runner shows no memory pressure in any log; this is CPU-bound relational evaluation.

Levers, each measured or checked

lever result
build-mode: none already in force (log: --build-mode=none, rust-analyzer-style extraction). Extraction is 6 min, not the problem.
exclude the 2 round-1 queries applied (34 not 36 loaded). Worth 0 minutes of wall time.
exclude every @kind diagnostic / @kind metric query (18 more, each verified against its github/codeql header) applied (16 loaded). Worth ~6 minutes (the cheap queries). Zero coverage change: only @kind problem/path-problem become alerts and all 16 of those still run. Kept.
query suite in force default (not security-extended), confirmed from the default-setup API and the loaded list.
newer CodeQL with faster Rust type inference none exists: codeql-bundle-v2.26.4 is the latest release and is what github/codeql-action@v4 already pins.
larger runner not available: the org has 0 GitHub-hosted larger runners and its default runner group excludes public repos. Would be a paid org-level setup, and even a 3x speedup lands ~40 min.
path scoping would trim our own 329 inputs only; the type-inference cost is over the dependency graph (ExtractLibrary phase), so expected payoff is small and it was not spent a 2-hour run on.
dependency-caching: true already on; "no caching configuration for rust" in the log -- the Rust extractor has nothing to cache in build-mode none.

What is on the branch now (89817bc7, rebased onto main @ 4852247e, version kept at 15.3.1 > main's 15.3.0)

  • the advanced workflow + concurrency cancellation (the stacking half, still real and still working: e1c3b0c4's run was cancelled 91 s after b61011eb superseded it);
  • the 20 exclusions, with both files' comments rewritten to say what was measured instead of the misattribution;
  • timeout-minutes: 30 restored as an explicitly non-shippable fail-fast placeholder. A passing value would have to clear today's observed default-setup spread of 101-199 minutes (i.e. ~210), which codifies the very wait this ticket exists to remove.
  • check name Analyze (rust) unchanged (single-axis matrix); branch protection untouched.

Two things the orchestrator has to decide -- I have not taken either

  1. Gate shape. The only lever left that reaches "under 30 minutes per PR" is to stop running the Rust analysis on every pull_request: run it on push: main + the weekly schedule, and let the Analyze (rust) matrix entry skip on PRs via a job-level if:. A job skipped by if: still registers a check-run (conclusion skipped), which branch protection treats as satisfied, so the required-context name is preserved and no protection edit is needed. Cost, stated plainly: Rust findings would surface as alerts on main after merge, not as a blocking check before it. Same queries, same coverage, different timing. Alternatively: raise the timeout to ~210 and accept ~2-hour PRs (no latency fix), or re-enable default setup (same 2 hours, plus the stacking comes back).
  2. A live hazard, independent of the above. Default setup is off and this workflow is not on main, so every OTHER open dig-app PR now gets no Analyze (rust) check-run at all -- and a required context that never appears reads as ABSENT and blocks the merge. Measured: dig-app#395 head 991ddf15 has zero such check-runs. That stays true until either this file lands on main or default setup is re-enabled.

Nothing is analysed less than before on any path above: the 16 alert-producing Rust queries run in every variant.

@MichaelTaylor3d MichaelTaylor3d changed the title ci(codeql): move to advanced setup, cut the 130-minute PR wait ci(codeql): move to advanced setup with concurrency control and non-alerting query exclusions Sep 6, 2026
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.

1 participant