Skip to content

Fix token-usage report aggregation at production scale - #301

Merged
abhi1693 merged 9 commits into
masterfrom
fix/295-token-usage-aggregation
Oct 24, 2026
Merged

abhi1693 merged 9 commits into
masterfrom
fix/295-token-usage-aggregation

Conversation

@abhi1693

@abhi1693 abhi1693 commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

What changes and why?

The token-usage report materialized every matching job and grouped a date expression derived from near-unique completion timestamps. PostgreSQL 17 overestimated daily groups and spilled long windows to temporary storage.

Stream two narrow projections into daily aggregation: extract the UTC date and bounded token text once, then validate/cast tokens once. Literal OFFSET 0 fences preserve those projections under custom and generic plans. Counts, durations, UTC boundaries and cumulative token accounting stay exact, with no schema or additional writer maintenance.

The token benchmark and reader API comparison now update the oldest existing performance comment. A shared publisher serializes updates, preserves both sections and head/run provenance, and removes duplicate performance comments after saving their results. Token tables show before, after and signed improvement; resource/volume details expand below the validation totals and writer caveats.

Related issues

Closes #295.

Validation

  • 550 admin reporting and operations tests passed against disposable PostgreSQL 17.11 and Redis. Coverage includes UTC/inclusive boundaries, terminal statuses, malformed/missing usage, unreported durations and sums above bigint. Planner guards reject the original and intermediate materialized query shapes.
  • 1,171 relevant tooling tests passed, including 134 profiler, summary and publisher cases. Local profiler coverage is 99.31% and summary-renderer coverage is 100%. Cases cover disposable DB isolation, realistic fixtures, regression gates, writer failures, safe artifact I/O, signed deltas, missing evidence, comment migration and stale-run handling.
  • Python lint, formatting, types, versions, Actionlint and commit hooks passed. Artifact files are owner-only, fixed filenames under reports/token-usage/; unsafe links/files are rejected. The disposable PostgreSQL port binds only to loopback with no embedded password, and runtime commands use the locked environment without package builds.
  • A browser test from the updated base measured tab-resume position after fixed hydration sleeps. Its helper now waits for hydrated controls, placeholder removal, fonts and a stable initial layout. It keeps the 24 px position limit and original-DOM assertions, and saves failing-scenario evidence before asserting. 205 extension unit tests (including eight layout/geometry cases), 25 fixture/dispatch checks and 84 relevant reader component tests passed. Both focused browser tests passed locally in Chrome and Edge. Fresh hosted Web (47/47 feed tests), Chrome (53/53) and Edge (53/53) suites passed from the same workflow attempt: every runtime retained 25 rows across all 12 resume scenarios with zero reading-position change.

Exact read-only production counts at 2026-10-24 10:05 UTC, schema 0034, calibrated every fixture population and its qualifying 1/7/30-day windows:

Population Production 100k-article fixture 300k-article fixture
Articles 92,598 100,000 300,000
Article job history 156,021 200,000 600,000
Topic job history 159,575 200,000 600,000
Research job history 67,014 73,000 219,000

The completed native ARM64 PostgreSQL 17.11 run on 5f9dcbebee28810860c93d5e4068bb291d0e0890 passed 12 exact accounting comparisons, 84 performance/plan gates and 26 production-volume checks. It uses 180 days of history, incompressible 4 KiB provider metadata, 4 MiB work_mem, JIT, parameterized custom/generic plans and concurrent ingestion requested at 20 writes/second. No parallel workers actually launched. Complete JSON evidence.

Measured 30-day query medians:

Articles Plan Before After Improvement (+/−)
100,000 Custom 834.60 ms 630.73 ms +24.4%
100,000 Generic 879.76 ms 666.93 ms +24.2%
300,000 Custom 2,661.04 ms 1,916.34 ms +28.0%
300,000 Generic 2,677.34 ms 2,024.46 ms +24.4%

Monthly temporary writes fell 549 → 0 blocks at 100k and 2,868 → 0 at 300k. The largest reported aggregate-node peak fell 793 → 40 KiB; this is not whole-process memory. Database-container CPU includes concurrent writer/background work. Writer latency is observational: short samples include startup effects and do not prove steady-state ingestion latency. The current run exposes one writer p95 regression: 22.54 → 39.58 ms (−75.6%), from only 56/41 samples; it does not establish a sustained-load regression or improvement.

The existing results comment was updated in place by both workflows on the current head; exactly one performance comment remains. The reader API comparison completed all six runs without request failures; latency was inconclusive, with no confirmed regression. The native run and in-place comment update passed on 5f9dcbebee28810860c93d5e4068bb291d0e0890. Full CI also passed on that head in attempt 1: all six reader browser suites, all six database integration shards, all three CodeQL languages, SonarQube, all 12 native ARM64 image builds/scans/smoke tests, and CI required. Sonar reports 99.7% new-code coverage, no open issues and no security hotspots awaiting review. All automated review conversations are resolved.

Rollout notes

No schema/API contract change, extra index, trigger or writer maintenance. Rollback is an application rollback. The query still reads matching histories once. Synthetic timings do not establish production latency; after deployment compare fresh statement statistics under matched traffic, report freshness, database CPU and ingestion latency. This PR does not deploy production.

Ready for review

  • The description explains the change, evidence and remaining validation gaps.
  • Relevant documentation and regression tests are updated.
  • Native ARM64 fixtures cover each job kind above the measured production populations.

@abhi1693
abhi1693 marked this pull request as ready for review October 24, 2026 10:26
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Backend unit coverage

PASS · minimum combined coverage 98.00%.

Tests: 10580 passed, 0 failed, 0 skipped. Source files: 367.

Workspace Lines Branches Combined
All backend code 98.68% 95.66% 98.01%
apps/admin-api 96.65% 89.65% 95.33%
apps/aggregator 97.99% 94.45% 97.18%
apps/api 99.49% 98.88% 99.38%
apps/article-enrichment-worker 80.00% 50.00% 71.43%
apps/cli 99.43% 90.76% 98.13%
apps/images-worker 80.00% 50.00% 71.43%
apps/mcp 99.86% 98.70% 99.65%
apps/notifications 100.00% 100.00% 100.00%
apps/partner-api 99.47% 95.51% 98.83%
apps/search-indexer 85.48% 85.71% 85.53%
apps/source-discovery-worker 80.00% 50.00% 71.43%
apps/user-api 99.88% 98.96% 99.72%
packages/core 99.10% 96.86% 98.55%
packages/http 99.47% 97.49% 99.03%

Unit coverage includes every backend workspace. PostgreSQL/Redis integration tests run separately.

Commit: 1f23bc6 · HTML, JSON, XML reports and job logs · attempt 1

@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

PR performance results

Current PR head: 1f23bc6149fcc6176605ee392478695171e22dc5

Token usage benchmark

Source head: 1f23bc6149fcc6176605ee392478695171e22dc5 · run 40119863259 · attempt 1

PASS — Token usage benchmark: all 12 comparisons, accounting checks and production-volume gates passed.

Compared head: 1f23bc6149fcc6176605ee392478695171e22dc5 · Benchmark run · Architecture: aarch64.

Before = frozen pre-fix SQL; after = application SQL at the compared head. Improvement = (before − after) / before × 100: + means better, − means worse; n/a means missing evidence or a zero baseline. Lower is better for every performance metric.

Query runtime — median milliseconds

Articles Window Plan Before (ms) After (ms) Improvement (+/−)
100,000 1d custom 43.36 35.21 +18.8%
100,000 1d generic 42.56 34.95 +17.9%
100,000 7d custom 260.85 210.25 +19.4%
100,000 7d generic 263.12 214.88 +18.3%
100,000 30d custom 867.73 657.18 +24.3%
100,000 30d generic 895.08 691.82 +22.7%
300,000 1d custom 193.50 171.48 +11.4%
300,000 1d generic 199.59 176.86 +11.4%
300,000 7d custom 779.42 640.32 +17.8%
300,000 7d generic 810.52 665.24 +17.9%
300,000 30d custom 2,735.86 1,989.03 +27.3%
300,000 30d generic 2,785.50 2,129.12 +23.6%

Validation

Check Passed / expected
Runtime comparisons 12 / 12
Identical token accounting 12 / 12
Runtime, plan and spill gates 84 / 84
Production-volume gates 26 / 26
Measurement step success

Runtime gate allows up to 20% or 20 ms noise; PASS does not mean every case got faster. Writer latency is observational and has no pass/fail gate. Its samples include writer startup and reports may finish before a subsequent write completes.

Writer latency caveats — p95 regressions over 20%

Fixture / window / plan Before p95 (ms) After p95 (ms) Improvement (+/−) Samples before / after
100,000 / 30d / generic 4.32 50.04 -1057.7% 57 / 44
300,000 / 1d / generic 4.94 13.11 -165.6% 15 / 15

p95 uses the nearest-rank sample. Small sample counts and startup outliers prevent a sustained-load latency conclusion.

30-day CPU, temporary writes and hash memory
Articles Plan Metric Before After Improvement (+/−)
100,000 custom DB container CPU (ms) 920.37 705.03 +23.4%
100,000 custom App CPU (ms) 0.83 0.87 -4.9%
100,000 custom Temp writes (blocks) 549.00 0.00 +100.0%
100,000 custom Largest hash aggregate (KiB) 793.00 40.00 +95.0%
100,000 generic DB container CPU (ms) 944.68 730.97 +22.6%
100,000 generic App CPU (ms) 0.73 0.74 -0.6%
100,000 generic Temp writes (blocks) 549.00 0.00 +100.0%
100,000 generic Largest hash aggregate (KiB) 73.00 40.00 +45.2%
300,000 custom DB container CPU (ms) 2,951.74 2,134.61 +27.7%
300,000 custom App CPU (ms) 1.28 1.10 +14.2%
300,000 custom Temp writes (blocks) 2,868.00 0.00 +100.0%
300,000 custom Largest hash aggregate (KiB) 793.00 40.00 +95.0%
300,000 generic DB container CPU (ms) 3,200.43 2,406.86 +24.8%
300,000 generic App CPU (ms) 1.35 1.25 +7.1%
300,000 generic Temp writes (blocks) 2,868.00 0.00 +100.0%
300,000 generic Largest hash aggregate (KiB) 121.00 40.00 +66.9%

DB CPU is the container CPU median, including concurrent ingestion; elapsed time is measured separately. Hash memory is the largest reported aggregate node, not total process memory. Temporary writes are PostgreSQL blocks from EXPLAIN.

Production-volume coverage: retained history and 1/7/30-day counts
Fixture Production snapshot 100,000 articles 300,000 articles
Articles 92,598 100,000 (PASS) 300,000 (PASS)
article analysis / total 156,021 200,000 (PASS) 600,000 (PASS)
article analysis / one day 3,034 6,901 (PASS) 20,701 (PASS)
article analysis / seven day 16,692 17,695 (PASS) 53,085 (PASS)
article analysis / thirty day 50,402 59,098 (PASS) 177,296 (PASS)
topic analysis / total 159,575 200,000 (PASS) 600,000 (PASS)
topic analysis / one day 2,381 2,666 (PASS) 8,010 (PASS)
topic analysis / seven day 30,257 34,663 (PASS) 104,013 (PASS)
topic analysis / thirty day 128,657 157,331 (PASS) 472,000 (PASS)
research verification / total 67,014 73,000 (PASS) 219,000 (PASS)
research verification / one day 190 974 (PASS) 2,925 (PASS)
research verification / seven day 3,142 12,655 (PASS) 37,968 (PASS)
research verification / thirty day 45,316 57,426 (PASS) 172,277 (PASS)

Production snapshot observed at: 2026-10-24T10:05:40Z.

Production counts are the captured snapshot, not a live production benchmark.

Full JSON artifact and job logs · attempt 1

Reader API performance

Source head: 1f23bc6149fcc6176605ee392478695171e22dc5 · run 40119863858 · attempt 1

PR reader API performance

Result: INCONCLUSIVE

5 inconclusive · 3 within threshold. Zero request failures across all six runs.

Base ed219475c17f57c551cb4a70b5f5d453cf242531 → PR 1f23bc6149fcc6176605ee392478695171e22dc5

Three independent GitHub-hosted ARM64 runners, each measuring base and PR sequentially with alternating order and isolated resources for every sample. 16 users, completed bootstrap and 15 seconds of warmup before each full 60-second measurement, fixed journey seed 294, 1,000 synthetic articles, cache off, five PgBouncer slots.

Latency comparison

All latency values are in milliseconds; lower is better. Values are medians of three runs, with one-millisecond histogram bins.

Route Base p95 PR p95 Change Result
All requests 87 87 +0 (+0.0%) Within threshold
Latest feed 118 110 -8 (-6.8%) Within threshold
Feed pagination 89 84 -5 (-5.6%) Inconclusive
Article detail 68 55 -13 (-19.1%) Inconclusive
Source feed 93 93 +0 (+0.0%) Inconclusive
Topic feed 106 91 -15 (-14.2%) Within threshold
Feed filters 62 81 +19 (+30.6%) Inconclusive
Sources 40 37 -3 (-7.5%) Within threshold
Topics 52 43 -9 (-17.3%) Inconclusive
Additional latency and throughput metrics

Each cell shows base → PR. Latencies are in milliseconds.

Endpoint p50 p99 Requests/s
Aggregated 32 → 30 135 → 133 28.94 → 29.02
/v1/feed [latest] 44 → 43 170 → 146 8.69 → 8.68
/v1/feed [page] 29 → 29 132 → 136 4.08 → 4.10
/v1/articles/[id] 20 → 19 100 → 122 5.88 → 5.88
/v1/feed [source] 32 → 28 126 → 125 2.03 → 2.05
/v1/feed [topic] 31 → 29 155 → 148 1.60 → 1.62
/v1/feed/options 22 → 21 98 → 97 2.22 → 2.23
/v1/sources 13 → 13 84 → 79 2.22 → 2.23
/v1/topics 13 → 14 81 → 68 2.22 → 2.23
All paired p95 samples and changes

Each pair shares a validated runner. Cells show base → PR p95 (absolute and percentage change), in milliseconds. Slower/faster counts use the same strict 20% and 20 ms thresholds against that pair's own baseline.

Route Pair 1 Pair 2 Pair 3 Slower pairs Faster pairs
All requests 87 → 83 (-4, -4.6%) 86 → 87 (+1, +1.2%) 109 → 106 (-3, -2.8%) 0 0
Latest feed 118 → 110 (-8, -6.8%) 107 → 101 (-6, -5.6%) 140 → 131 (-9, -6.4%) 0 0
Feed pagination 72 → 84 (+12, +16.7%) 89 → 77 (-12, -13.5%) 116 → 103 (-13, -11.2%) 0 0
Article detail 68 → 52 (-16, -23.5%) 56 → 55 (-1, -1.8%) 76 → 86 (+10, +13.2%) 0 0
Source feed 93 → 93 (+0, +0.0%) 113 → 62 (-51, -45.1%) 92 → 99 (+7, +7.6%) 0 1
Topic feed 106 → 91 (-15, -14.2%) 75 → 88 (+13, +17.3%) 106 → 100 (-6, -5.7%) 0 0
Feed filters 53 → 74 (+21, +39.6%) 62 → 81 (+19, +30.6%) 70 → 113 (+43, +61.4%) 2 0
Sources 32 → 31 (-1, -3.1%) 40 → 45 (+5, +12.5%) 43 → 37 (-6, -14.0%) 0 0
Topics 65 → 38 (-27, -41.5%) 35 → 43 (+8, +22.9%) 52 → 48 (-4, -7.7%) 0 1
Individual runs
Run Requests Failures Exit code
Base 1 1736 0 0
Base 2 1744 0 0
Base 3 1722 0 0
PR 1 1742 0 0
PR 2 1746 0 0
PR 3 1719 0 0
How to interpret this report

Regressed / Improved: a p95 regression needs >20% and >20 ms increase in the medians and in at least two same-runner base/PR pairs. Improvements use the inverse threshold. Within threshold means the change did not meet both thresholds.

Inconclusive: a within-revision p95 range >35% of its median and >20 ms is too noisy for a confident latency verdict. A material median change without two confirming pairs is also inconclusive; this does not prove unchanged performance.

Inconclusive latency comparisons are non-blocking: the check passes without claiming an improvement. Confirmed regressions and test/report errors fail the PR gate. Throughput is descriptive: paced users do not measure maximum capacity. This uncached public API test does not establish production health, browser performance or worker capacity. Base and PR share a runner within each pair; three pairs use independent runners. Alternating order reduces host-assignment bias but cannot eliminate changes in host load over time.

Full HTML/CSV reports and logs · attempt 1

Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread scripts/ci/token-usage-summary.py Fixed
Comment thread scripts/ci/token-usage-summary.py Fixed
Comment thread scripts/ci/token-usage-summary.py Fixed
Comment thread scripts/profile_token_usage.py Fixed
Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread .github/workflows/token-usage-performance.yml Fixed
Comment thread scripts/ci/token-usage-summary.py Fixed
@github-actions

github-actions Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Lighthouse · ⚠️ Targets exceeded

⚠️ 12 target warnings · Bold values need attention.

Page FCP LCP CLS TBT JS Total
Latest (/latest) 1.21 s 4.22 s 0.001 283 ms 361 KiB 663 KiB
Article preview (/articles/…) 1.37 s 3.95 s 0.005 277 ms 364 KiB 685 KiB
Topics (/topics) 1.22 s 3.83 s 0.000 158 ms 319 KiB 471 KiB
Sources (/sources) 1.22 s 3.97 s 0.000 353 ms 332 KiB 488 KiB
Public profile (/users/…) 1.21 s 2.90 s 0.009 95 ms 280 KiB 428 KiB
Leaderboard (/leaderboard) 1.06 s 2.15 s 0.000 119 ms 276 KiB 383 KiB

Mobile reader · Three cold loads per page · Median timings · Largest transfer size.

Needs attention

Page Finding Measured Target / limit
Latest (/latest) ⚠️ Target · LCP 4.22 s 4.00 s
Latest (/latest) ⚠️ Target · JS transfer 361 KiB 330 KiB
Latest (/latest) ⚠️ Target · LCP 4.22 s 2.50 s
Latest (/latest) ⚠️ Target · TBT 283 ms 200 ms
Article preview (/articles/…) ⚠️ Target · JS transfer 364 KiB 330 KiB
Article preview (/articles/…) ⚠️ Target · LCP 3.95 s 2.50 s
Article preview (/articles/…) ⚠️ Target · TBT 277 ms 200 ms
Topics (/topics) ⚠️ Target · LCP 3.83 s 2.50 s
Sources (/sources) ⚠️ Target · JS transfer 332 KiB 330 KiB
Sources (/sources) ⚠️ Target · LCP 3.97 s 2.50 s
Sources (/sources) ⚠️ Target · TBT 353 ms 200 ms
Public profile (/users/…) ⚠️ Target · LCP 2.90 s 2.50 s

Full reports and logs · 1f23bc6 · Attempt 1

@sonarqubecloud

Copy link
Copy Markdown

@abhi1693
abhi1693 merged commit f9b9bfe into master Oct 24, 2026
91 checks passed
@abhi1693
abhi1693 deleted the fix/295-token-usage-aggregation branch October 24, 2026 19:10
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.

Improve token-usage report aggregation plans without changing accounting semantics

2 participants