Skip to content

perf(metrics): count pool stats only while metrics are enabled [W1-07] - #939

Open
ajitpratap0 wants to merge 6 commits into
mainfrom
perf/w1-07-pool-stats-gated
Open

ajitpratap0 wants to merge 6 commits into
mainfrom
perf/w1-07-pool-stats-gated

Conversation

@ajitpratap0

@ajitpratap0 ajitpratap0 commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Decision D1(b): pool Get/Put statistics are counted only while metrics.Enable() is in effect. A new internal/metricsgate holds the single enabled flag (own cache line); metrics.Enable/Disable/IsEnabled and every check in metrics.go read it.
  • The gate lives inside poolstats.Counter.Get/Put, which still inline at all 6 call sites, so no tokenizer/parser/ast pool file is touched.
  • RecordNamedPool* return 0 while disabled and create no custom entries. While disabled, GetPoolStats returns exactly the three standard pools with zero values; counts made while enabled are kept and reappear after Enable.
  • Tests: TestPoolStats_* and pool hygiene tests enable metrics (restoring the prior state); new tests for the gate, disabled behaviour, toggling and an end-to-end Parse/Release count pin. Mutations M1-M8 each fail a test. The transpiler hygiene test also needed the helper (test-only edit in a wave-1b-owned dir, Q1 default).
  • Evidence note: the earlier "11.9% of COMMIT" figure came from a profile taken before W1-01; what remains is 6 atomic adds per Parse. The amd64 gate measured about 2.9 ns of PoolGetPut/serial (56.89 -> 54.03 ns/op, EPYC 9V74, -cpu 1); the larger win is under contention (parallel Get/Put), which the gate does not measure. instructions/op may rise slightly (locked add becomes a gate load plus branch).

Test Plan

  • task test:cbinding, go test -count=1 ./pkg/compatibility/..., staticcheck on changed packages: pass.
  • task check: lint and vet pass; under machine load (~85) several race packages hit the default 2m timeout or timing-ratio tests. Re-ran individually: formatter, gosqlx, transpiler, mcp, parser, security, tokenizer all ok with -race. The pre-commit internal/scaletest timing test flaked once and passed on re-run.
  • Inlining: can inline (*Counter).Get/Put, Enabled, IsEnabled, RecordPoolGet/Put, and all 6 call sites inline.
  • Local indicative (shared M4, scripts/perf/ab.sh -r 8 -k 8, ./pkg/gosqlx=PoolGetPut|ParseParallel|E2EMetrics, merged tree vs origin/main): allocs/op identical on all rows; B/op +0.00%; geomean -1.42% (p=0.065); PoolGetPut/serial +3.34% (p=0.44) and parallel +2.03% (p=0.65), both within noise, no -cpu 4 isolation in this run; ParseParallel and the metrics-ON rows (ParseE2EMetrics, ParseParallelMetrics) show no significant regression (spread 2-7%). Separate reviewer run (n=6, load 8-11): PoolGetPut/parallel-4 91.7 -> 8.2 ns (-91%, p=0.002), serial-4 -3.1% (p=0.009).
  • CI perf-gate verdict (amd64, layout-randomized), PoolGetPut/serial, runner-dependent: Zen4 EPYC 9V74 -5.41% (p<0.001, earlier head); Zen3 EPYC 7763 +3.99% and +3.93% (p<0.001, REGRESSION, head 3843b87, two attempts); Intel Xeon 6973P-C -37.06% (p<0.001, A/A' +0.43%, 0/52 moved, final head 4046552, PASS, target listed low-power so allocs/op, B/op unchanged are the backstop). Planned threshold <= -20%: met only on the Xeon run. Hypothesis of data-placement effect probed locally (per-counter flag copy, padding): no clear improvement, no production change. Maintainer decision needed on D1(b) given CPU-dependent result.
  • PLAN threshold misses (reported, not hidden): target <= -20% on PoolGetPut/serial vs -5.4% measured (projected -30% to -45%); ParseE2E/simple_select <= -1% vs -0.32% (p=0.543, not significant). The gain materializes in contended use, which the gate does not measure.

Breaking Changes

Pool Get/Put counts and metrics.GetPoolStats are zero unless metrics are enabled. Migration: call metrics.Enable() before the workload to measure; counts are not retroactive. See CHANGELOG.

🤖 Generated with Claude Code

https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2

Pool statistics are gated on a shared internal/metricsgate flag inside
poolstats.Counter.Get/Put (still inlined at all call sites). GetPoolStats
returns zeros for the three standard pools while disabled. Breaking:
callers must metrics.Enable() before measuring.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
@vercel

vercel Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
gosqlx-website Ready Ready Preview Nov 3, 2026 9:03pm UTC

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 25s —— View job


Review of PR #939: gate pool stats on metrics.Enable()

I read the core diff: metricsgate, poolstats, metrics.go and pool_stats.go. I did not run the tests or benchmarks, and I only skimmed the test files.

Overall: The change looks correct and clean. I found no blocking issues.

What looks good

  • Single flag: metricsgate is the one source of truth. Enable, Disable, IsEnabled and all Record* checks read it, so the old enabled int32 field is gone and the two flags can't drift apart.
  • Cache-line padding: The gate has 64 bytes of padding on each side. Hot-path reads therefore won't false-share with neighbouring globals.
  • Inlining: Counter.Get/Put stay small, and the PR reports that all 6 call sites still inline. The disabled path is now a load, a test and a branch, with no locked add. The extra cost when enabled is one predictable load.
  • Disabled GetPoolStats: It returns a fresh map with exactly the three standard pools at zero. Callers can't mutate shared state, and the key set stays stable.
  • Documented skew: The Active() doc comment explains the negative-value case when metrics are toggled mid-borrow. That is the right trade-off for decision D1(b).
  • Tests: Mutations M1-M8 each fail a test, and the end-to-end Parse/Release pin is a good guard.

Minor observations and suggestions

  1. Enable/Disable ordering (pkg/metrics/metrics.go, Enable): metricsgate.Set(true) runs before startTime.Store(...). A concurrent GetStats() can therefore see the gate on while startTime is still unset or stale, which can make uptime or rate figures briefly wrong. Calling startTime.Store first and then Set(true) fixes this. The old code had the same ordering, so this is not a regression.
  2. Toggle skew (pkg/metrics/pool_stats.go): Counters keep their values while disabled and reappear on Enable. Combined with Active() = Gets - Puts, a Get made while enabled and its Put made while disabled leave a permanently inflated Active() until ResetPoolStats. The new docs mention this, so it's acceptable. If users hit it, consider also suggesting ResetPoolStats() on Enable.
  3. Named-pool race (RecordNamedPoolGet/Put): These check the gate first and then do ByName, a map lookup and an atomic add. A disable between the check and the add records one extra count. That is harmless for statistics.
  4. Breaking behaviour: GetPoolStats is now zero unless metrics are enabled. The CHANGELOG and the PR body both flag this. Also check docs/ and any CLI or LSP code that reports pool stats without enabling metrics, since they will now show zeros. A quick grep -rn GetPoolStats outside tests would confirm this.
  5. Test hygiene: Several tests now enable metrics and restore the prior state. Make sure that restore always runs via t.Cleanup, and that no test using the global gate calls t.Parallel(). Otherwise parallel tests can race on the flag.
  6. Perf evidence: The PR admits the local bench:ab run for PoolGetPut didn't complete. Please fill in the CI perf-gate verdict before merging, as the PR body promises. I'd especially check for any regression on the enabled path.

Security

None. The change adds only an internal atomic flag and no external input.

Verdict

Looks good to merge once the CI perf-gate result is confirmed. Item 1 is a small optional fix.
· branch perf/w1-07-pool-stats-gated

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Perf gate: PASS (shadow mode: report only, this check never fails)

Base 6d29d6802dc6, head e767ef8cf16c. 20 interleaved rounds, 20 randomized code layouts per arm (-randlayout seeds 417181730852..417181730871; A/A' 417181730872..417181730891), benchtime 100ms, -cpu 1, GOMEMLIMIT 64MiB, pinned to CPU 3 (SMT siblings 2-3) with taskset.

Runner CPU: AMD EPYC 9V74 80-Core Processor (x86_64). Base and head ran interleaved in this job, so the tables show their ratios only; absolute timings (raw files in the artifact) hold for this CPU and this job alone and are never compared across jobs.

  • no gated metric moved past its threshold

Rules: each arm runs 20 randomized code layouts (-ldflags=-randlayout); time regresses at >= +3% when significant at a false discovery rate of 0.01 over the suite (Benjamini-Hochberg on exact Mann-Whitney U p-values of the per-layout medians, so a regression must hold across layouts, not within one binary), or a suite geomean >= +1.5% (per-layout suite geomeans, same test); allocs/op must not increase; B/op must not rise more than 2%; instructions/op must not rise more than 1.5%. The A/A' control runs the base revision under a disjoint set of layout seeds; when more than 10% of benchmarks move over 3% in it, the run is noisy: re-run once, then inconclusive (neutral).

Gated suite (GOGC=off, GOMAXPROCS=1)

benchmark time p A/A' spread B/op allocs/op status
ParseE2E/simple_select +0.61% 0.756 -0.15% 6.93% +0.00% 5 → 5 ok
ParseE2E/select_where +0.46% 0.745 -0.76% 6.46% +0.00% 18 → 18 ok
ParseE2E/join -0.30% 0.787 -0.70% 2.87% +0.00% 17 → 17 ok
ParseE2E/aggregate -1.17% 0.473 -1.69% 4.12% +0.00% 18 → 18 ok
ParseE2E/subquery -0.10% 0.968 -1.81% 5.27% +0.00% 14 → 14 ok
ParseE2E/cte -0.73% 0.718 -2.15% 7.08% +0.00% 21 → 21 ok
ParseE2E/window +1.14% 0.525 -1.16% 4.71% +0.00% 11 → 11 ok
ParseE2E/complex -0.97% 0.636 -0.72% 8.47% +0.00% 49 → 49 ok
ParseE2E/insert +1.27% 0.152 -0.11% 4.73% +0.00% 17 → 17 ok
ParseE2E/update +0.89% 0.117 +0.54% 2.06% +0.00% 13 → 13 ok
ParseE2E/delete +1.72% 0.155 +0.00% 3.39% +0.00% 5 → 5 ok
ParseRW/postgresql/medium +1.89% 0.355 +0.32% 10.61% +0.00% 646 → 646 ok
ParseRW/mysql/medium -0.14% 0.947 -0.32% 7.75% +0.00% 368 → 368 ok
ParseRW/mariadb/medium +0.65% 0.738 -0.20% 7.42% +0.00% 528 → 528 ok
ParseRW/sqlserver/medium +0.09% 0.925 +0.27% 2.05% +0.00% 927 → 927 ok
ParseRW/oracle/medium -0.36% 0.698 -1.01% 3.13% +0.00% 1090 → 1090 ok
ParseRW/sqlite/medium -2.13% 0.314 -2.45% 7.84% +0.00% 347 → 347 ok
ParseRW/snowflake/medium -1.62% 0.341 -1.71% 3.96% +0.00% 776 → 776 ok
ParseRW/clickhouse/medium -0.78% 0.583 -3.47% 7.90% +0.00% 383 → 383 ok
ParseFresh/simple_select -1.65% 0.344 -3.15% 4.85% +0.00% 6 → 6 ok
ParseFresh/join -1.79% 0.429 -1.04% 4.15% +0.00% 18 → 18 ok
ParseFresh/complex -1.68% 0.429 -1.34% 4.55% +0.00% 50 → 50 ok
PoolGetPut/serial -6.51% 0.002 -1.84% 6.35% +0.00% 0 → 0 ok
Tokenize/scratch/join -1.04% 0.482 -2.70% 5.23% +0.00% 0 → 0 ok
Tokenize/scratch/complex -1.89% 0.355 -2.45% 6.79% +0.00% 0 → 0 ok
Tokenize/scratch/insert -0.90% 0.507 -1.54% 7.19% +0.00% 0 → 0 ok
Normalize/simple_select +0.17% 0.946 -0.45% 3.65% +0.00% 7 → 7 ok
Normalize/join -0.06% 0.957 -1.33% 4.85% +0.00% 23 → 23 ok
Normalize/complex -1.76% 0.114 -0.97% 3.86% +0.00% 68 → 68 ok
TransformParse/simple_select +0.50% 0.607 +0.16% 3.67% +0.00% 5 → 5 ok
TransformParse/join +0.26% 0.903 -0.73% 8.16% +0.00% 17 → 17 ok
TransformParse/complex -0.20% 0.799 -0.87% 2.67% +0.00% 49 → 49 ok
LintParallel/rules +0.12% 0.883 -0.55% 3.67% -0.00% 158312 → 158312 ok
LintParallel/lint +0.59% 0.678 +0.35% 3.78% -0.00% 236887.5 → 236887 ok
LintRW/postgresql -0.26% 0.883 -0.76% 5.03% +0.00% 26500 → 26499.5 ok
LintRW/mysql -0.95% 0.327 -0.61% 2.96% +0.00% 32413 → 32413 ok
LintRW/mariadb +0.05% 0.968 -0.48% 4.59% +0.00% 29610 → 29610 ok
LintRW/sqlserver -0.51% 0.659 -1.55% 4.77% +0.00% 25622 → 25622 ok
LintRW/oracle -0.07% 0.968 -0.17% 5.62% -0.00% 31614 → 31614 ok
LintRW/sqlite +0.02% 0.989 -0.06% 5.39% +0.00% 29427 → 29427 ok
LintRW/snowflake +0.70% 0.620 +0.60% 4.27% -0.00% 28805 → 28805 ok
LintRW/clickhouse -0.59% 0.659 +0.18% 5.69% +0.00% 32891 → 32891 ok
LintRule/L007 +0.86% 0.718 -0.33% 4.66% +0.00% 1194 → 1194 ok
LintRule/L010 +0.37% 0.904 +0.93% 9.13% +0.00% 0 → 0 ok
ParseError/syntax_mid/parse +1.18% 0.552 +0.17% 6.75% +0.00% 12 → 12 ok
ParseError/syntax_mid/validate +1.07% 0.543 -0.11% 6.52% +0.00% 12 → 12 ok
ParseError/syntax_end/parse +0.54% 0.745 -0.67% 7.21% +0.00% 16 → 16 ok
ParseError/syntax_end/validate -1.10% 0.482 -0.88% 6.30% +0.00% 16 → 16 ok
ParseError/tokenize/parse +2.06% 0.174 +0.49% 5.11% +0.00% 4 → 4 ok
ParseError/tokenize/validate +0.56% 0.787 -1.23% 6.87% +0.00% 21 → 21 ok
ParseError/long_prefix/parse -1.78% 0.192 -0.35% 4.90% +0.00% 219 → 219 ok
ParseError/long_prefix/validate -0.50% 0.495 -0.05% 3.49% +0.00% 219 → 219 ok
geomean -0.27% 0.678 -0.79%

time and A/A' are Hodges-Lehmann shifts over the per-layout medians. spread: the base arm's robust standard deviation across its layouts (1.4826 x MAD, relative), i.e. how much this benchmark moves between builds that differ only in function placement, plus run-to-run noise; a change much smaller than it cannot be resolved in one run.

A/A' control (base revision, disjoint layout seeds): 2 of 52 benchmarks moved more than 3%; 0 would have been flagged by the time rule; suite geomean -0.79%, p 0.529.

Low power

46 benchmark(s) have under 80% estimated power to be flagged on their own for a +5% slowdown (their spread across layouts is too wide for 20 layouts at the Benjamini-Hochberg level alpha/m = 0.000192). A slowdown below the last column usually goes unflagged there; instructions/op, allocs/op and the suite geomean still guard them.

benchmark spread power at +5% slowdown caught 80% of the time
ParseRW/postgresql/medium 10.61% 1% +17.0%
LintRule/L010 9.13% 2% +14.5%
ParseE2E/complex 8.47% 3% +13.3%
TransformParse/join 8.16% 3% +12.8%
ParseRW/clickhouse/medium 7.90% 3% +12.4%
ParseRW/sqlite/medium 7.84% 4% +12.3%
ParseRW/mysql/medium 7.75% 4% +12.1%
ParseRW/mariadb/medium 7.42% 4% +11.6%
ParseError/syntax_end/parse 7.21% 5% +11.2%
Tokenize/scratch/insert 7.19% 5% +11.2%
ParseE2E/cte 7.08% 5% +11.0%
ParseE2E/simple_select 6.93% 6% +10.8%
ParseError/tokenize/validate 6.87% 6% +10.7%
Tokenize/scratch/complex 6.79% 7% +10.6%
ParseError/syntax_mid/parse 6.75% 7% +10.5%
ParseError/syntax_mid/validate 6.52% 8% +10.1%
ParseE2E/select_where 6.46% 8% +10.0%
PoolGetPut/serial 6.35% 9% +9.9%
ParseError/syntax_end/validate 6.30% 9% +9.8%
LintRW/clickhouse 5.69% 14% +8.8%
LintRW/oracle 5.62% 15% +8.7%
LintRW/sqlite 5.39% 18% +8.3%
ParseE2E/subquery 5.27% 19% +8.1%
Tokenize/scratch/join 5.23% 20% +8.0%
ParseError/tokenize/parse 5.11% 22% +7.9%
LintRW/postgresql 5.03% 23% +7.7%
ParseError/long_prefix/parse 4.90% 26% +7.5%
ParseFresh/simple_select 4.85% 27% +7.4%
Normalize/join 4.85% 27% +7.4%
LintRW/sqlserver 4.77% 28% +7.3%
... 16 more

Power is estimated with the normal approximation for the U test (Noether): power = Phi(ln(1+shift)/s * sqrt(K/2) * sqrt(3/pi) - z(1 - alpha/(2m))), with s the spread and K the layouts per arm.

Instructions per operation (cmd/perfdriver)

case instructions/op status
e2e/aggregate +0.11% ok
e2e/complex +0.03% ok
e2e/cte +0.11% ok
e2e/delete +0.17% ok
e2e/insert +0.10% ok
e2e/join +0.07% ok
e2e/select_where +0.09% ok
e2e/simple_select +0.24% ok
e2e/subquery +0.16% ok
e2e/update +0.15% ok
e2e/window +0.14% ok
rw/postgresql/medium +0.04% ok
Secondary metric: default GC, GOMAXPROCS=1 (informational)
benchmark time p
ParseE2E/simple_select +1.18% 0.427
ParseE2E/select_where -0.62% 0.739
ParseE2E/join -1.63% 0.481
ParseE2E/aggregate -1.96% 0.436
ParseE2E/subquery -2.21% 0.315
ParseE2E/cte -2.18% 0.520
ParseE2E/window -1.07% 0.739
ParseE2E/complex -1.03% 0.529
ParseE2E/insert -1.28% 0.631
ParseE2E/update -2.22% 0.315
ParseE2E/delete -1.23% 0.545
ParseRW/postgresql/medium -1.42% 0.631
ParseRW/mysql/medium -0.28% 0.912
ParseRW/mariadb/medium +1.28% 0.481
ParseRW/sqlserver/medium +1.86% 0.315
ParseRW/oracle/medium +0.91% 0.684
ParseRW/sqlite/medium -0.87% 0.796
ParseRW/snowflake/medium -0.24% 0.912
ParseRW/clickhouse/medium +0.75% 0.579
ParseFresh/simple_select -1.14% 0.436
ParseFresh/join +0.04% 0.971
ParseFresh/complex +0.23% 0.971
PoolGetPut/serial -3.57% 0.143
Tokenize/scratch/join -0.04% 1.000
Tokenize/scratch/complex -0.15% 1.000
Tokenize/scratch/insert -0.63% 0.473
Normalize/simple_select +1.97% 0.280
Normalize/join -0.39% 0.853
Normalize/complex +2.05% 0.280
TransformParse/simple_select +1.42% 0.393
TransformParse/join +1.64% 0.684
TransformParse/complex -0.02% 1.000
LintParallel/rules +1.52% 0.684
LintParallel/lint -2.10% 0.481
LintRW/postgresql +0.92% 0.739
LintRW/mysql +2.45% 0.315
LintRW/mariadb +0.72% 0.739
LintRW/sqlserver +0.86% 0.631
LintRW/oracle +0.44% 0.912
LintRW/sqlite +1.50% 0.529
LintRW/snowflake -0.31% 0.912
LintRW/clickhouse +2.61% 0.631
LintRule/L007 -0.82% 0.684
LintRule/L010 -1.09% 0.912
ParseError/syntax_mid/parse -0.62% 0.596
ParseError/syntax_mid/validate -1.55% 0.496
ParseError/syntax_end/parse -2.77% 0.140
ParseError/syntax_end/validate -2.16% 0.290
ParseError/tokenize/parse -0.18% 0.880
ParseError/tokenize/validate -3.93% 0.190
ParseError/long_prefix/parse -3.13% 0.123
ParseError/long_prefix/validate -0.92% 0.853
geomean -0.38%
Environment
date:        2026-11-03T21:06:50Z
base:        6d29d6802dc6c910264e0d0ee4ff820a323fd2a9
head:        working tree of e767ef8cf16cbfffb390e05f00b5f020429ed3d2
cpu model:   AMD EPYC 9V74 80-Core Processor (x86_64)
go:          go version go1.26.9 linux/amd64
kernel:      Linux 7.0.0-1017-azure x86_64
layouts:     20 per arm, -ldflags=-randlayout seeds 417181730852..417181730871 (base, head), 417181730872..417181730891 (A/A')
pinning:     pinned to CPU 3 (SMT siblings 2-3) with taskset
gcoff env:   GOGC=off GOMEMLIMIT=64MiB
clocksource: tsc
load:        8.32, 2.82, 1.01
instr count: cachegrind
perf probe:  Error: No supported events found. The instructions:u event is not supported. 

Architecture:                            x86_64
CPU(s):                                  4
Model name:                              AMD EPYC 9V74 80-Core Processor
Thread(s) per core:                      2
Core(s) per socket:                      2
Socket(s):                               1
Hypervisor vendor:                       Microsoft
L1d cache:                               64 KiB (2 instances)
L1i cache:                               64 KiB (2 instances)
L2 cache:                                2 MiB (2 instances)
L3 cache:                                32 MiB (1 instance)
benchstat (primary metric, samples pooled across layouts: informational, the verdict uses the per-layout test)
goos: linux
goarch: amd64
pkg: github.com/ajitpratap0/GoSQLX/pkg/fingerprint
cpu: AMD EPYC 9V74 80-Core Processor                
                        │    base     │                head                │
                        │   sec/op    │   sec/op     vs base               │
Normalize/simple_select   2.010µ ± 6%   2.048µ ± 4%       ~ (p=0.941 n=20)
Normalize/join            5.967µ ± 3%   5.918µ ± 4%       ~ (p=0.952 n=20)
Normalize/complex         15.78µ ± 3%   15.33µ ± 3%       ~ (p=0.114 n=20)
geomean                   5.742µ        5.706µ       -0.63%

                        │     base     │                 head                  │
                        │     B/op     │     B/op      vs base                 │
Normalize/simple_select     896.0 ± 0%     896.0 ± 0%       ~ (p=1.000 n=20) ¹
Normalize/join            2.211Ki ± 0%   2.211Ki ± 0%       ~ (p=1.000 n=20) ¹
Normalize/complex         6.117Ki ± 0%   6.117Ki ± 0%       ~ (p=1.000 n=20) ¹
geomean                   2.279Ki        2.279Ki       +0.00%
¹ all samples are equal

                        │    base    │                head                 │
                        │ allocs/op  │ allocs/op   vs base                 │
Normalize/simple_select   7.000 ± 0%   7.000 ± 0%       ~ (p=1.000 n=20) ¹
Normalize/join            23.00 ± 0%   23.00 ± 0%       ~ (p=1.000 n=20) ¹
Normalize/complex         68.00 ± 0%   68.00 ± 0%       ~ (p=1.000 n=20) ¹
geomean                   22.20        22.20       +0.00%
¹ all samples are equal

pkg: github.com/ajitpratap0/GoSQLX/pkg/gosqlx
                                │     base     │                head                │
                                │    sec/op    │   sec/op     vs base               │
ParseE2E/simple_select            1.359µ ±  4%   1.372µ ± 2%       ~ (p=0.753 n=20)
ParseE2E/select_where             2.928µ ±  5%   2.900µ ± 3%       ~ (p=0.743 n=20)
ParseE2E/join                     4.131µ ±  3%   4.088µ ± 3%       ~ (p=0.784 n=20)
ParseE2E/aggregate                3.169µ ±  7%   3.091µ ± 4%       ~ (p=0.474 n=20)
ParseE2E/subquery                 2.661µ ±  4%   2.647µ ± 2%       ~ (p=0.968 n=20)
ParseE2E/cte                      3.906µ ± 10%   3.829µ ± 7%       ~ (p=0.718 n=20)
ParseE2E/window                   2.426µ ±  5%   2.422µ ± 3%       ~ (p=0.525 n=20)
ParseE2E/complex                  10.83µ ±  6%   10.51µ ± 7%       ~ (p=0.634 n=20)
ParseE2E/insert                   1.929µ ±  4%   1.945µ ± 3%       ~ (p=0.151 n=20)
ParseE2E/update                   1.727µ ±  3%   1.738µ ± 1%       ~ (p=0.116 n=20)
ParseE2E/delete                   1.050µ ±  2%   1.062µ ± 3%       ~ (p=0.155 n=20)
ParseRW/postgresql/medium         124.6µ ±  7%   127.7µ ± 5%       ~ (p=0.355 n=20)
ParseRW/mysql/medium              74.82µ ±  5%   74.44µ ± 2%       ~ (p=0.947 n=20)
ParseRW/mariadb/medium            104.8µ ±  6%   106.2µ ± 5%       ~ (p=0.738 n=20)
ParseRW/sqlserver/medium          200.9µ ±  1%   201.4µ ± 2%       ~ (p=0.925 n=20)
ParseRW/oracle/medium             251.8µ ±  2%   250.4µ ± 2%       ~ (p=0.698 n=20)
ParseRW/sqlite/medium             60.30µ ±  5%   58.33µ ± 6%       ~ (p=0.314 n=20)
ParseRW/snowflake/medium          148.0µ ±  3%   146.0µ ± 2%       ~ (p=0.341 n=20)
ParseRW/clickhouse/medium         73.33µ ±  6%   70.22µ ± 8%       ~ (p=0.583 n=20)
ParseFresh/simple_select          6.073µ ±  3%   5.956µ ± 4%       ~ (p=0.344 n=20)
ParseFresh/join                   8.902µ ±  3%   8.742µ ± 4%       ~ (p=0.429 n=20)
ParseFresh/complex                17.14µ ±  3%   16.76µ ± 6%       ~ (p=0.429 n=20)
PoolGetPut/serial                 63.23n ±  4%   58.90n ± 5%  -6.85% (p=0.001 n=20)
ParseError/syntax_mid/parse       2.043µ ±  5%   2.070µ ± 5%       ~ (p=0.551 n=20)
ParseError/syntax_mid/validate    1.820µ ±  5%   1.832µ ± 5%       ~ (p=0.542 n=20)
ParseError/syntax_end/parse       2.294µ ±  5%   2.287µ ± 5%       ~ (p=0.743 n=20)
ParseError/syntax_end/validate    2.285µ ±  4%   2.232µ ± 3%       ~ (p=0.482 n=20)
ParseError/tokenize/parse         735.5n ±  4%   758.9n ± 3%       ~ (p=0.174 n=20)
ParseError/tokenize/validate      2.277µ ±  6%   2.277µ ± 2%       ~ (p=0.784 n=20)
ParseError/long_prefix/parse      54.40µ ±  3%   53.19µ ± 3%       ~ (p=0.192 n=20)
ParseError/long_prefix/validate   52.62µ ±  3%   51.91µ ± 2%       ~ (p=0.495 n=20)
geomean                           7.970µ         7.900µ       -0.87%

                                │     base      │                head                 │
                                │      B/s      │     B/s       vs base               │
ParseE2E/simple_select            18.24Mi ±  4%   18.07Mi ± 2%       ~ (p=0.774 n=20)
ParseE2E/select_where             33.55Mi ±  5%   33.87Mi ± 3%       ~ (p=0.758 n=20)
ParseE2E/join                     29.09Mi ±  3%   29.39Mi ± 3%       ~ (p=0.784 n=20)
ParseE2E/aggregate                25.28Mi ±  7%   25.92Mi ± 5%       ~ (p=0.465 n=20)
ParseE2E/subquery                 29.39Mi ±  4%   29.55Mi ± 2%       ~ (p=0.968 n=20)
ParseE2E/cte                      30.28Mi ± 11%   30.89Mi ± 7%       ~ (p=0.718 n=20)
ParseE2E/window                   36.95Mi ±  5%   37.02Mi ± 3%       ~ (p=0.525 n=20)
ParseE2E/complex                  33.28Mi ±  7%   34.31Mi ± 7%       ~ (p=0.634 n=20)
ParseE2E/insert                   37.56Mi ±  4%   37.26Mi ± 3%       ~ (p=0.159 n=20)
ParseE2E/update                   30.37Mi ±  3%   30.18Mi ± 1%       ~ (p=0.136 n=20)
ParseE2E/delete                   47.20Mi ±  2%   46.72Mi ± 3%       ~ (p=0.159 n=20)
ParseRW/postgresql/medium         28.80Mi ±  8%   28.10Mi ± 5%       ~ (p=0.355 n=20)
ParseRW/mysql/medium              36.17Mi ±  5%   36.36Mi ± 2%       ~ (p=0.941 n=20)
ParseRW/mariadb/medium            33.83Mi ±  6%   33.38Mi ± 6%       ~ (p=0.738 n=20)
ParseRW/sqlserver/medium          33.12Mi ±  1%   33.04Mi ± 2%       ~ (p=0.920 n=20)
ParseRW/oracle/medium             33.30Mi ±  2%   33.48Mi ± 2%       ~ (p=0.693 n=20)
ParseRW/sqlite/medium             34.25Mi ±  6%   35.41Mi ± 6%       ~ (p=0.311 n=20)
ParseRW/snowflake/medium          34.35Mi ±  3%   34.82Mi ± 2%       ~ (p=0.337 n=20)
ParseRW/clickhouse/medium         30.73Mi ±  6%   32.10Mi ± 8%       ~ (p=0.597 n=20)
ParseError/syntax_mid/parse       22.42Mi ±  5%   22.12Mi ± 5%       ~ (p=0.551 n=20)
ParseError/syntax_mid/validate    25.16Mi ±  4%   24.98Mi ± 4%       ~ (p=0.534 n=20)
ParseError/syntax_end/parse       16.21Mi ±  5%   16.26Mi ± 5%       ~ (p=0.743 n=20)
ParseError/syntax_end/validate    16.28Mi ±  5%   16.67Mi ± 3%       ~ (p=0.465 n=20)
ParseError/tokenize/parse         59.65Mi ±  4%   57.81Mi ± 3%       ~ (p=0.174 n=20)
ParseError/tokenize/validate      19.27Mi ±  6%   19.27Mi ± 2%       ~ (p=0.794 n=20)
ParseError/long_prefix/parse      42.46Mi ±  3%   43.43Mi ± 3%       ~ (p=0.189 n=20)
ParseError/long_prefix/validate   43.90Mi ±  3%   44.50Mi ± 2%       ~ (p=0.490 n=20)
geomean                           30.50Mi         30.66Mi       +0.52%

                                │      base      │                 head                  │
                                │      B/op      │     B/op      vs base                 │
ParseE2E/simple_select              816.0 ± 0%       816.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/select_where             1.523Ki ± 0%     1.523Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/join                     1.836Ki ± 0%     1.836Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/aggregate                1.812Ki ± 0%     1.812Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/subquery                 1.828Ki ± 0%     1.828Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/cte                      2.336Ki ± 0%     2.336Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/window                   1.281Ki ± 0%     1.281Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/complex                  5.025Ki ± 0%     5.025Ki ± 0%       ~ (p=0.716 n=20)
ParseE2E/insert                     824.0 ± 0%       824.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/update                     736.0 ± 0%       736.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/delete                     400.0 ± 0%       400.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/postgresql/medium         65.89Ki ± 0%     65.90Ki ± 0%       ~ (p=0.469 n=20)
ParseRW/mysql/medium              42.37Ki ± 0%     42.37Ki ± 0%       ~ (p=0.523 n=20)
ParseRW/mariadb/medium            60.59Ki ± 0%     60.59Ki ± 0%       ~ (p=0.660 n=20)
ParseRW/sqlserver/medium          83.67Ki ± 0%     83.67Ki ± 0%       ~ (p=0.951 n=20)
ParseRW/oracle/medium             104.8Ki ± 0%     104.8Ki ± 0%       ~ (p=0.369 n=20)
ParseRW/sqlite/medium             38.12Ki ± 0%     38.12Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/snowflake/medium          77.63Ki ± 0%     77.63Ki ± 0%       ~ (p=0.501 n=20)
ParseRW/clickhouse/medium         34.82Ki ± 0%     34.82Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/simple_select            896.0 ± 0%       896.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/join                   1.914Ki ± 0%     1.914Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/complex                5.102Ki ± 0%     5.102Ki ± 0%       ~ (p=1.000 n=20) ¹
PoolGetPut/serial                   0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_mid/parse         680.0 ± 0%       680.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_mid/validate      680.0 ± 0%       680.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_end/parse       1.430Ki ± 0%     1.430Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_end/validate    1.430Ki ± 0%     1.430Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseError/tokenize/parse           272.0 ± 0%       272.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/tokenize/validate      1.758Ki ± 0%     1.758Ki ± 0%       ~ (p=1.000 n=20) ¹
ParseError/long_prefix/parse      22.74Ki ± 0%     22.74Ki ± 0%       ~ (p=0.633 n=20)
ParseError/long_prefix/validate   22.68Ki ± 0%     22.68Ki ± 0%       ~ (p=1.000 n=20) ¹
geomean                                        ²                 +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                                │     base      │                 head                 │
                                │   allocs/op   │  allocs/op   vs base                 │
ParseE2E/simple_select             5.000 ± 0%      5.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/select_where              18.00 ± 0%      18.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/join                      17.00 ± 0%      17.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/aggregate                 18.00 ± 0%      18.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/subquery                  14.00 ± 0%      14.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/cte                       21.00 ± 0%      21.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/window                    11.00 ± 0%      11.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/complex                   49.00 ± 0%      49.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/insert                    17.00 ± 0%      17.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/update                    13.00 ± 0%      13.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseE2E/delete                    5.000 ± 0%      5.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/postgresql/medium          646.0 ± 0%      646.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/mysql/medium               368.0 ± 0%      368.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/mariadb/medium             528.0 ± 0%      528.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/sqlserver/medium           927.0 ± 0%      927.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/oracle/medium             1.090k ± 0%     1.090k ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/sqlite/medium              347.0 ± 0%      347.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/snowflake/medium           776.0 ± 0%      776.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseRW/clickhouse/medium          383.0 ± 0%      383.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/simple_select           6.000 ± 0%      6.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/join                    18.00 ± 0%      18.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseFresh/complex                 50.00 ± 0%      50.00 ± 0%       ~ (p=1.000 n=20) ¹
PoolGetPut/serial                  0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_mid/parse        12.00 ± 0%      12.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_mid/validate     12.00 ± 0%      12.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_end/parse        16.00 ± 0%      16.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/syntax_end/validate     16.00 ± 0%      16.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/tokenize/parse          4.000 ± 0%      4.000 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/tokenize/validate       21.00 ± 0%      21.00 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/long_prefix/parse       219.0 ± 0%      219.0 ± 0%       ~ (p=1.000 n=20) ¹
ParseError/long_prefix/validate    219.0 ± 0%      219.0 ± 0%       ~ (p=1.000 n=20) ¹
geomean                                       ²                +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                          │     base     │                head                │
                          │   sec/stmt   │  sec/stmt    vs base               │
ParseRW/postgresql/medium    10.38µ ± 7%   10.64µ ± 5%       ~ (p=0.355 n=20)
ParseRW/mysql/medium         9.352µ ± 5%   9.305µ ± 2%       ~ (p=0.952 n=20)
ParseRW/mariadb/medium       11.65µ ± 6%   11.80µ ± 5%       ~ (p=0.738 n=20)
ParseRW/sqlserver/medium     9.567µ ± 1%   9.591µ ± 2%       ~ (p=0.920 n=20)
ParseRW/oracle/medium        10.49µ ± 2%   10.43µ ± 2%       ~ (p=0.698 n=20)
ParseRW/sqlite/medium       10.051µ ± 5%   9.721µ ± 6%       ~ (p=0.311 n=20)
ParseRW/snowflake/medium     11.39µ ± 3%   11.23µ ± 2%       ~ (p=0.331 n=20)
ParseRW/clickhouse/medium    10.48µ ± 6%   10.03µ ± 8%       ~ (p=0.583 n=20)
geomean                      10.39µ        10.31µ       -0.75%

pkg: github.com/ajitpratap0/GoSQLX/pkg/linter
                   │    base     │                head                 │
                   │   sec/op    │    sec/op     vs base               │
LintParallel/rules   26.41m ± 2%   26.33m ±  2%       ~ (p=0.883 n=20)
LintParallel/lint    42.18m ± 3%   42.30m ±  2%       ~ (p=0.678 n=20)
LintRW/postgresql    5.586m ± 4%   5.555m ±  3%       ~ (p=0.883 n=20)
LintRW/mysql         5.931m ± 6%   5.829m ±  2%       ~ (p=0.327 n=20)
LintRW/mariadb       5.360m ± 4%   5.336m ±  3%       ~ (p=0.968 n=20)
LintRW/sqlserver     4.910m ± 4%   4.884m ±  3%       ~ (p=0.659 n=20)
LintRW/oracle        5.878m ± 4%   5.906m ±  3%       ~ (p=0.968 n=20)
LintRW/sqlite        5.009m ± 4%   5.036m ±  2%       ~ (p=0.989 n=20)
LintRW/snowflake     5.134m ± 6%   5.146m ±  2%       ~ (p=0.620 n=20)
LintRW/clickhouse    5.779m ± 5%   5.737m ±  2%       ~ (p=0.659 n=20)
LintRule/L007        2.380m ± 3%   2.396m ±  3%       ~ (p=0.718 n=20)
LintRule/L010        754.6µ ± 7%   749.9µ ± 12%       ~ (p=0.904 n=20)
geomean              5.824m        5.811m        -0.22%

                  │     base     │                head                 │
                  │     B/s      │     B/s       vs base               │
LintRW/postgresql   5.074Mi ± 4%   5.097Mi ± 3%       ~ (p=0.867 n=20)
LintRW/mysql        5.517Mi ± 7%   5.612Mi ± 2%       ~ (p=0.344 n=20)
LintRW/mariadb      5.689Mi ± 4%   5.717Mi ± 3%       ~ (p=0.952 n=20)
LintRW/sqlserver    6.309Mi ± 4%   6.342Mi ± 3%       ~ (p=0.664 n=20)
LintRW/oracle       6.757Mi ± 4%   6.728Mi ± 3%       ~ (p=0.984 n=20)
LintRW/sqlite       5.727Mi ± 4%   5.703Mi ± 2%       ~ (p=0.995 n=20)
LintRW/snowflake    6.075Mi ± 6%   6.061Mi ± 2%       ~ (p=0.551 n=20)
LintRW/clickhouse   5.617Mi ± 5%   5.660Mi ± 2%       ~ (p=0.664 n=20)
geomean             5.825Mi        5.847Mi       +0.36%

                   │      base      │                 head                  │
                   │      B/op      │     B/op      vs base                 │
LintParallel/rules   8.111Mi ± 0%     8.111Mi ± 0%       ~ (p=0.723 n=20)
LintParallel/lint    28.08Mi ± 0%     28.08Mi ± 0%       ~ (p=0.140 n=20)
LintRW/postgresql    3.089Mi ± 0%     3.089Mi ± 0%       ~ (p=0.635 n=20)
LintRW/mysql         3.880Mi ± 0%     3.880Mi ± 0%       ~ (p=0.569 n=20)
LintRW/mariadb       3.570Mi ± 0%     3.570Mi ± 0%       ~ (p=0.654 n=20)
LintRW/sqlserver     3.124Mi ± 0%     3.124Mi ± 0%       ~ (p=0.920 n=20)
LintRW/oracle        3.818Mi ± 0%     3.818Mi ± 0%       ~ (p=0.033 n=20)
LintRW/sqlite        3.385Mi ± 0%     3.385Mi ± 0%       ~ (p=0.126 n=20)
LintRW/snowflake     3.384Mi ± 0%     3.384Mi ± 0%       ~ (p=0.857 n=20)
LintRW/clickhouse    3.832Mi ± 0%     3.832Mi ± 0%       ~ (p=0.867 n=20)
LintRule/L007        92.98Ki ± 0%     92.98Ki ± 0%       ~ (p=1.000 n=20) ¹
LintRule/L010          0.000 ± 0%       0.000 ± 0%       ~ (p=1.000 n=20) ¹
geomean                           ²                 +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                   │     base      │                 head                 │
                   │   allocs/op   │  allocs/op   vs base                 │
LintParallel/rules   158.3k ± 0%     158.3k ± 0%       ~ (p=0.830 n=20)
LintParallel/lint    236.9k ± 0%     236.9k ± 0%       ~ (p=0.572 n=20)
LintRW/postgresql    26.50k ± 0%     26.50k ± 0%       ~ (p=0.191 n=20)
LintRW/mysql         32.41k ± 0%     32.41k ± 0%       ~ (p=1.000 n=20)
LintRW/mariadb       29.61k ± 0%     29.61k ± 0%       ~ (p=1.000 n=20)
LintRW/sqlserver     25.62k ± 0%     25.62k ± 0%       ~ (p=1.000 n=20)
LintRW/oracle        31.61k ± 0%     31.61k ± 0%       ~ (p=1.000 n=20) ¹
LintRW/sqlite        29.43k ± 0%     29.43k ± 0%       ~ (p=0.231 n=20)
LintRW/snowflake     28.80k ± 0%     28.80k ± 0%       ~ (p=0.605 n=20)
LintRW/clickhouse    32.89k ± 0%     32.89k ± 0%       ~ (p=1.000 n=20)
LintRule/L007        1.194k ± 0%     1.194k ± 0%       ~ (p=1.000 n=20) ¹
LintRule/L010         0.000 ± 0%      0.000 ± 0%       ~ (p=1.000 n=20) ¹
geomean                          ²                -0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                  │    base     │                head                 │
                  │  sec/stmt   │   sec/stmt    vs base               │
LintRW/postgresql   18.31µ ± 4%   18.21µ ±  3%       ~ (p=0.883 n=20)
LintRW/mysql        14.33µ ± 6%   14.08µ ±  2%       ~ (p=0.327 n=20)
LintRW/mariadb      14.10µ ± 4%   14.04µ ±  3%       ~ (p=0.968 n=20)
LintRW/sqlserver    17.72µ ± 4%   17.63µ ±  3%       ~ (p=0.659 n=20)
LintRW/oracle       15.97µ ± 4%   16.05µ ±  3%       ~ (p=0.968 n=20)
LintRW/sqlite       14.39µ ± 4%   14.47µ ±  2%       ~ (p=0.995 n=20)
LintRW/snowflake    15.70µ ± 6%   15.74µ ±  2%       ~ (p=0.620 n=20)
LintRW/clickhouse   14.67µ ± 5%   14.56µ ±  2%       ~ (p=0.664 n=20)
LintRule/L007       845.9n ± 3%   851.6n ±  3%       ~ (p=0.713 n=20)
LintRule/L010       268.2n ± 7%   266.6n ± 12%       ~ (p=0.889 n=20)
geomean             7.756µ        7.735µ        -0.27%

pkg: github.com/ajitpratap0/GoSQLX/pkg/sql/tokenizer
                         │    base     │                head                │
                         │   sec/op    │   sec/op     vs base               │
Tokenize/scratch/join      1.176µ ± 4%   1.161µ ± 4%       ~ (p=0.482 n=20)
Tokenize/scratch/complex   3.135µ ± 6%   3.038µ ± 6%       ~ (p=0.355 n=20)
Tokenize/scratch/insert    578.1n ± 5%   567.9n ± 5%       ~ (p=0.507 n=20)
geomean                    1.287µ        1.260µ       -2.06%

                         │     base     │                head                 │
                         │     B/s      │     B/s       vs base               │
Tokenize/scratch/join      102.2Mi ± 4%   103.6Mi ± 4%       ~ (p=0.495 n=20)
Tokenize/scratch/complex   115.0Mi ± 7%   118.7Mi ± 6%       ~ (p=0.355 n=20)
Tokenize/scratch/insert    125.4Mi ± 5%   127.6Mi ± 5%       ~ (p=0.507 n=20)
geomean                    113.8Mi        116.2Mi       +2.11%

                         │     base     │                head                 │
                         │     B/op     │    B/op     vs base                 │
Tokenize/scratch/join      0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
Tokenize/scratch/complex   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
Tokenize/scratch/insert    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
geomean                               ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

                         │     base     │                head                 │
                         │  allocs/op   │ allocs/op   vs base                 │
Tokenize/scratch/join      0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
Tokenize/scratch/complex   0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
Tokenize/scratch/insert    0.000 ± 0%     0.000 ± 0%       ~ (p=1.000 n=20) ¹
geomean                               ²               +0.00%                ²
¹ all samples are equal
² summaries must be >0 to compute geomean

pkg: github.com/ajitpratap0/GoSQLX/pkg/transform
                             │    base     │                head                │
                             │   sec/op    │   sec/op     vs base               │
TransformParse/simple_select   1.314µ ± 5%   1.332µ ± 3%       ~ (p=0.606 n=20)
TransformParse/join            4.399µ ± 5%   4.377µ ± 5%       ~ (p=0.899 n=20)
TransformParse/complex         11.53µ ± 3%   11.54µ ± 2%       ~ (p=0.799 n=20)
geomean                        4.054µ        4.067µ       +0.32%

                             │     base     │                 head                  │
                             │     B/op     │     B/op      vs base                 │
TransformParse/simple_select     816.0 ± 0%     816.0 ± 0%       ~ (p=1.000 n=20) ¹
TransformParse/join            1.836Ki ± 0%   1.836Ki ± 0%       ~ (p=1.000 n=20) ¹
TransformParse/complex         5.023Ki ± 0%   5.023Ki ± 0%       ~ (p=1.000 n=20) ¹
geomean                        1.944Ki        1.944Ki       +0.00%
¹ all samples are equal

                             │    base    │                head                 │
                             │ allocs/op  │ allocs/op   vs base                 │
TransformParse/simple_select   5.000 ± 0%   5.000 ± 0%       ~ (p=1.000 n=20) ¹
TransformParse/join            17.00 ± 0%   17.00 ± 0%       ~ (p=1.000 n=20) ¹
TransformParse/complex         49.00 ± 0%   49.00 ± 0%       ~ (p=1.000 n=20) ¹
geomean                        16.09        16.09       +0.00%
¹ all samples are equal

Run, raw data and benchstat

Test and others added 2 commits November 3, 2026 23:45
Fill gate numbers in CHANGELOG, store startTime before opening the gate,
note gosqlx stats behaviour, tidy test imports.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 35s —— View job


Review of PR #939: gate pool stats on metrics.Enable()

I read the core diff (metricsgate, poolstats, metrics.go, pool_stats.go) and the CI perf-gate result. I did not run the tests, and I only skimmed the test files.

Verdict: The change is correct and clean. The perf gate passes with no regressions, and I have one real finding.

Perf gate (from the sticky comment)

  • PoolGetPut/serial is -5.4% (p<0.001), which is the intended win.
  • The suite geomean is -0.30%. No benchmark regressed, and allocs/op and B/op are unchanged.
  • instructions/op is +0.04% to +0.35%, well under the 1.5% limit.
  • The A/A' control is clean: 0 of 52 benchmarks moved more than 3%.
  • The enabled-path cost isn't measured by this suite, since the gate runs with metrics disabled. It is only one predictable load, so I'm not worried about it.

Findings

  1. gosqlx stats will always print zeros (cmd/gosqlx/cmd/stats.go:41). It calls metrics.GetPoolStats() without ever calling metrics.Enable(). The CLI process starts with metrics off, so the command now shows zero for every pool. That was already close to useless in a fresh process, but it is now explicitly zero. Consider one of these:

    • Document in the command's Long help that counts are only collected while metrics are enabled.
    • Print a note when !metrics.IsEnabled().
    • Remove the command, if it has no real use.

    I'd mention this in the CHANGELOG breaking-change entry as well.

  2. Ordering in Enable (pkg/metrics/metrics.go): you already fixed this by storing startTime before metricsgate.Set(true). This is correct.

  3. Toggle skew is documented on Active(). A Get made while enabled and its Put made while disabled leave Active() inflated until ResetPoolStats. This is acceptable under D1(b). A short note in doc.go pointing users to ResetPoolStats() before a measurement window would help.

  4. Named-pool race (RecordNamedPoolGet/Put): the gate check and the add are not atomic. A disable in between records one extra count, which is harmless.

  5. Test hygiene: the only t.Parallel() hit outside the metrics package is in a doc comment. Please confirm the gate-toggling tests restore state through t.Cleanup and don't use t.Parallel().

  6. Gate layout (internal/metricsgate): the padding on both sides plus atomic.Bool is fine. It is a read-mostly flag, so the padding only matters for neighbouring writers.

Security

None. The change adds only an internal atomic flag.

Summary

The change is safe to merge. The perf-gate verdict requested in the PR body can now be filled in: PASS, with PoolGetPut/serial -5.4% and no regressions. Please handle finding 1, either in this PR or as a follow-up.
· branch perf/w1-07-pool-stats-gated

Test and others added 2 commits November 4, 2026 02:01
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
… both runners [W1-07]

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DnbRnTFErhoMJdAjVEeCg2
@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 33s —— View job


Review of PR #939: gate pool stats on metrics.Enable()

I read the full diff of the core files (metricsgate, poolstats, metrics.go, pool_stats.go, CHANGELOG). I did not run tests or benchmarks. I only checked the test files for existence, not line by line.

Verdict: The change is correct and the design is sound. I have no blocking issues. Three points need your decision, and the first one matters most.

Needs a decision

  1. The perf evidence is mixed, and the PR body disagrees with the latest CI run.

    • The PR body says the gate passed, with PoolGetPut/serial at -5.41% on an EPYC 9V74.
    • The latest gate run on head 510eb5f used an EPYC 7763. It reports PoolGetPut/serial +3.93% (p<0.001) and a REGRESSION verdict. The default-GC secondary metric shows +3.34%.
    • The A/A' control was clean on that run (1/52 moved), so the result is real for that runner. The cause is plausible. On Zen3 the old uncontended LOCK XADD may be cheap enough that the extra load and branch costs more than it saves.
    • The CHANGELOG already says no consistent improvement is claimed, which is honest. The PR body's Test Plan still leads with "PASS" and -5.41%. Please update it to report both runners. Commit 9a7a5fe says the changelog was changed to "report both runners", so the body is the only thing left to fix.
    • The benefit is only measured under contention, in a reviewer run (parallel-4 -91%). The gate does not cover that. Consider adding a parallel Get/Put benchmark to the gated suite so the claimed win is checked in CI.
    • This conflicts with the "measure, don't assume" rule in CLAUDE.md. The serial path is where almost all users spend their time, and it is a net loss on one of the two CPUs tested. You should decide whether that is acceptable, and say so explicitly in the PR.
  2. gosqlx stats (cmd/gosqlx/cmd/stats.go:41) always prints zeros.

    • It calls metrics.GetPoolStats() without calling metrics.Enable(), and nothing else enables metrics in a fresh CLI process.
    • Before this change it also showed little in a fresh process. The CHANGELOG says "as it effectively did before". That is not quite accurate. Before, it showed the counts of whatever the process had done, such as the pool Get/Put calls made while loading and parsing its own input. Now it is always zero.
    • Either call metrics.Enable() at the start of the command, add a note to its help text, or print a message when !metrics.IsEnabled(). Do not leave it as a silent zero.
  3. Toggle skew.

    • A Get made while enabled and its Put made while disabled leave Active() inflated until ResetPoolStats(). A Get made while disabled and its Put made after Enable() can drive Active() negative.
    • The Active() doc comment covers this. The pkg/metrics/doc.go addition could also recommend calling ResetPoolStats() right after Enable() before a measurement window. That is optional.

Looks good

  • Single flag: metricsgate is the one source of truth. The old enabled int32 field is removed, so there is no way for two flags to drift.
  • Enable ordering: Enable now stores startTime before metricsgate.Set(true). A concurrent GetStats() can no longer see the gate on with startTime unset.
  • Inlining: Counter.Get/Put remain small enough to inline, per the PR notes. The disabled path is a load and a branch, with no locked add.
  • GetPoolStats while disabled: it returns a fresh map with exactly the three standard pools. Callers cannot mutate shared state, and the key set is stable.
  • Named pools: RecordNamedPool* check the gate first and create no entries while disabled. A disable between the check and the add can record one extra count. That is harmless for statistics.
  • Allocations: allocs/op and B/op are unchanged on every row. Instructions/op rise by at most +0.35%, under the 1.5% limit.
  • CHANGELOG: there is a Breaking Changes section with a migration note, as the repo's rules require.

Minor

  • metricsgate pads 64 bytes on each side of an atomic.Bool. That is fine for a read-mostly flag. The only benefit is to stop false sharing with neighbouring globals that are written often.
  • In the tests, check that every test that enables metrics restores the prior state through t.Cleanup, and that none of them call t.Parallel(). This holds for the files I could check. There is one t.Parallel() hit in a doc comment only.

Security

No concerns. The change adds only an internal atomic flag and takes no external input.

Summary

Safe to merge on correctness. Before merging, fix the PR body so it reports the Zen3 regression, and handle gosqlx stats.
· branch perf/w1-07-pool-stats-gated

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Claude finished @ajitpratap0's task in 16s —— View job


Review of PR #939: gate pool stats on metrics.Enable()

I checked the CLI call sites with a grep and did not re-read the full diff. I did not run tests or benchmarks. The notes below rely on the diff as described in the PR and on the earlier reviews and CI comments.

Verdict: The design is sound and I found no correctness bugs. Two items need fixing or deciding before merge.

Needs action

  1. gosqlx stats always prints zeros.
    • cmd/gosqlx/cmd/stats.go:41 is the only non-test caller of metrics.GetPoolStats() in cmd/.
    • A grep of cmd/ finds no metrics.Enable call, so a fresh CLI process now reports zero for every pool.
    • Fix it by calling metrics.Enable() at the start of the command, or by printing a note when !metrics.IsEnabled().
    • The CHANGELOG says the command "effectively" showed nothing before. That overstates it. Before this change it reported the pool traffic from the process's own parse.
  2. The PR body's perf claim doesn't match the latest runs.
    • The perf-gate comment shows PoolGetPut/serial at -37% on a Xeon 6973P. The body reports -5.4% on the EPYC 9V74.
    • An earlier EPYC 7763 run reportedly showed +3.9%. Your own commit says the changelog now reports all runners. Update the PR body Test Plan to say the same.
    • The gain depends on the CPU, so say plainly that serial results vary by runner. The solid win is under contention (parallel-4 -91%).
    • Consider adding a parallel Get/Put benchmark to the gated suite. The gate does not measure the contended case, which is the main benefit.

Minor

  • Toggle skew: a Get made while enabled and its Put made while disabled leave Active() inflated until ResetPoolStats(). The Active() doc covers this. A line in pkg/metrics/doc.go suggesting ResetPoolStats() right after Enable() before a measurement window would help.
  • Named-pool race: a disable between the gate check and the add can record one extra count. That is harmless for statistics.
  • Tests: confirm that every gate-toggling test restores state via t.Cleanup and none uses t.Parallel(), since they share the global flag.

Looks good

  • metricsgate is the single source of truth, and the old enabled int32 field is gone.
  • Enable stores startTime before setting the gate.
  • Counter.Get/Put still inline at all 6 call sites.
  • With metrics off, GetPoolStats returns a fresh map with the three standard pools at zero.
  • allocs/op and B/op are unchanged. instructions/op rise by at most +0.35%, against a 1.5% limit.
  • The CHANGELOG has the Breaking Changes and migration entry.

Security

No concerns. The change adds only an internal atomic flag.
· branch perf/w1-07-pool-stats-gated

This branch was successfully deployed

1 active deployment
Preview — d0169d98 Deployed Nov 3, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation performance transform

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant