-
Notifications
You must be signed in to change notification settings - Fork 0
135 lines (135 loc) · 6.14 KB
/
perf-gate.yml
File metadata and controls
135 lines (135 loc) · 6.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
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 any source. 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.sum
- name: Install C toolchain
run: sudo apt-get update -y && sudo apt-get install -y build-essential
- name: Build codeiq
run: go build -o /tmp/codeiq ./cmd/codeiq
- name: Stage fixture (separate copy so cache writes don't dirty git)
run: cp -r 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