ci+docs(perf-gate): enrich memory regression gate + OOM-fix evidence … #42
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: perf-gate | |
| # Performance regression gate. Runs `codeiq index` against fixture-multi-lang | |
| # and asserts wall-clock + node-count budgets. Catches regressions like: | |
| # - Regex pathology re-introduced (e.g. the CertificateAuthDetector | |
| # pre-screen miss that pushed indexing from 0.1s → 42s on PSA). | |
| # - Detector over-emission past the dedup budget. | |
| # | |
| # Trigger: push to main + PRs that touch go/**. Manual via workflow_dispatch. | |
| # Failure is informational on PRs (`continue-on-error`) until the threshold | |
| # is curated against real-world load; once stable, set strict gate. | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| # No `paths:` filter — same reason as go-ci.yml. If branch protection | |
| # ever marks this required, a path filter would deadlock "Waiting for | |
| # status to be reported" on non-Go PRs. Wall-clock is ~1 minute; the | |
| # signal is worth the cost. | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| bench: | |
| name: index perf gate (fixture-multi-lang) | |
| runs-on: ubuntu-latest | |
| env: | |
| CGO_ENABLED: '1' | |
| # Per-target budgets. Tune as the fixture grows. Current | |
| # fixture-multi-lang sits at ~50 files; an 8 s ceiling leaves | |
| # headroom over the observed ~0.3 s without hiding obvious | |
| # regressions (10x cushion catches the kinds of regex pathology | |
| # that pushed PSA from 0.1 s → 42 s mid-port). | |
| MAX_INDEX_SECONDS: '8' | |
| MIN_NODES: '40' | |
| MAX_PHANTOM_DROP_RATIO: '50' | |
| # Memory ceiling for `codeiq enrich` on fixture-multi-lang. Local | |
| # baseline post-Phase-A+B+C of the 2026-05-13 OOM-fix plan: ~108 MB | |
| # peak RSS. 300 MB ceiling gives ~2.7x headroom — tight enough to | |
| # surface real regressions, loose enough to absorb GC / scheduler | |
| # variance on CI runners. Bump only if a deliberate enrich-mem | |
| # regression is documented in a PR. | |
| MAX_ENRICH_RSS_KB: '307200' | |
| steps: | |
| - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 | |
| - uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c # v6.4.0 | |
| with: | |
| go-version: '1.25.10' | |
| cache: true | |
| cache-dependency-path: go/go.sum | |
| - name: Install C toolchain | |
| run: sudo apt-get update -y && sudo apt-get install -y build-essential | |
| - name: Build codeiq | |
| working-directory: go | |
| run: go build -o /tmp/codeiq ./cmd/codeiq | |
| - name: Stage fixture (separate copy so cache writes don't dirty git) | |
| run: cp -r go/testdata/fixture-multi-lang /tmp/fm-perf | |
| - name: Run + measure | |
| id: bench | |
| run: | | |
| set -euo pipefail | |
| START=$(date +%s.%N) | |
| /tmp/codeiq index /tmp/fm-perf > /tmp/perf.log 2>&1 | |
| END=$(date +%s.%N) | |
| ELAPSED=$(awk "BEGIN{printf \"%.3f\", $END - $START}") | |
| # Parse the "Files: F Nodes: N Edges: E ..." summary line. | |
| NODES=$(awk -F'[ ]+' '/^Files:/ {print $4}' /tmp/perf.log) | |
| EDGES=$(awk -F'[ ]+' '/^Files:/ {print $6}' /tmp/perf.log) | |
| # Optional "Deduped: D nodes, ... Dropped: P phantom edges" | |
| # line; absence is fine, defaults to 0. | |
| DEDUP_NODES=$(awk -F'[ ,]+' '/^Deduped:/ {print $2}' /tmp/perf.log) | |
| DEDUP_NODES=${DEDUP_NODES:-0} | |
| DROPPED=$(awk -F'[ ]+' '/^Deduped:/ {for(i=1;i<=NF;i++) if($i=="Dropped:") print $(i+1)}' /tmp/perf.log) | |
| DROPPED=${DROPPED:-0} | |
| echo "elapsed=$ELAPSED" >> "$GITHUB_OUTPUT" | |
| echo "nodes=$NODES" >> "$GITHUB_OUTPUT" | |
| echo "edges=$EDGES" >> "$GITHUB_OUTPUT" | |
| echo "dropped=$DROPPED" >> "$GITHUB_OUTPUT" | |
| { | |
| echo "## codeiq perf gate" | |
| echo "" | |
| echo "| metric | value | budget |" | |
| echo "|---|---:|---:|" | |
| echo "| wall-clock (s) | $ELAPSED | $MAX_INDEX_SECONDS |" | |
| echo "| nodes | $NODES | >= $MIN_NODES |" | |
| echo "| edges | $EDGES | — |" | |
| echo "| deduped nodes | $DEDUP_NODES | — |" | |
| echo "| dropped phantom edges | $DROPPED | ratio gated |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| cat /tmp/perf.log >> "$GITHUB_STEP_SUMMARY" | |
| # --- Hard gates --- | |
| fail=0 | |
| if awk "BEGIN{exit !($ELAPSED > $MAX_INDEX_SECONDS)}"; then | |
| echo "::error::wall-clock $ELAPSED s exceeds budget $MAX_INDEX_SECONDS s" | |
| fail=1 | |
| fi | |
| if [ "${NODES:-0}" -lt "$MIN_NODES" ]; then | |
| echo "::error::node count $NODES below minimum $MIN_NODES" | |
| fail=1 | |
| fi | |
| if [ "${EDGES:-0}" -gt 0 ] && [ "${DROPPED:-0}" -gt 0 ]; then | |
| RATIO=$(( DROPPED * 100 / (EDGES + DROPPED) )) | |
| if [ "$RATIO" -gt "$MAX_PHANTOM_DROP_RATIO" ]; then | |
| echo "::error::phantom-edge drop ratio ${RATIO}% exceeds ${MAX_PHANTOM_DROP_RATIO}%" | |
| fail=1 | |
| fi | |
| fi | |
| exit $fail | |
| # Enrich memory regression gate. Locks in the gains from the | |
| # 2026-05-13 OOM-fix plan (Phases A-C). Pre-Phase-A on the same | |
| # fixture peaked at ~600 MB; current main lives at ~108 MB peak. | |
| # If a refactor pushes peak past MAX_ENRICH_RSS_KB, fail the PR. | |
| - name: Enrich memory gate | |
| run: | | |
| set -euo pipefail | |
| # Run enrich against the already-indexed fixture; /usr/bin/time | |
| # -v reports peak RSS. | |
| /usr/bin/time -v /tmp/codeiq enrich /tmp/fm-perf \ | |
| > /tmp/perf-enrich.log 2> /tmp/perf-enrich.time | |
| RSS=$(awk -F': ' '/Maximum resident set size/ {print $2}' /tmp/perf-enrich.time) | |
| RSS=${RSS:-0} | |
| ELAPSED=$(awk -F': ' '/Elapsed \(wall clock\)/ {print $2}' /tmp/perf-enrich.time) | |
| { | |
| echo "" | |
| echo "## codeiq enrich memory gate" | |
| echo "" | |
| echo "| metric | value | budget |" | |
| echo "|---|---:|---:|" | |
| echo "| peak RSS (KB) | $RSS | <= $MAX_ENRICH_RSS_KB |" | |
| echo "| wall-clock | $ELAPSED | — |" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| cat /tmp/perf-enrich.log >> "$GITHUB_STEP_SUMMARY" | |
| if [ "$RSS" -gt "$MAX_ENRICH_RSS_KB" ]; then | |
| echo "::error::enrich peak RSS ${RSS} KB exceeds budget ${MAX_ENRICH_RSS_KB} KB" | |
| exit 1 | |
| fi |