SnapshotEvaluator.spikes loaded RuleContext.window("query_snapshot", 7)
as QRecords on every snapshot evaluation (every collection interval on
the worker). kof22 writes ~33,000 query snapshots a day, so by day 7 that
is ~230,000 records per evaluation.
history() now reads the window in BATCH_SIZE keyset pages (only OK and
PARTIAL snapshots, as before) and keeps per statement only the current
interval time, the latest capture time and earlier interval times in a
primitive double array. Median, twelve-sample baseline and the one-hour
absence rule are unchanged (SnapshotEvaluatorTest still passes).
SpikeHistoryBoundedTest counts every query snapshot QQQ materializes:
2,500 samples never exceed one 1,000-row page, failed snapshots and
samples outside the window are excluded, the current interval is found.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
dbPerfAnalyze (hourly, on the worker) loaded the full day of
db_perf_attribution through RuleContext.window and cached it for the
rules: ~570,000 rows. kof22 worker working set jumped from ~660 MiB to
~1.84 GiB at every :07-:08 run on 2026-10-16 (2 Gi limit, 1.5 GB heap).
RuleContext.window("attribution", days) now returns AggregateAction sums
(units, statements, rowsCount, errors, totalMs; max maxMs) grouped by
unitKind, unitName, tableName, operation: the only fields the rules
(OVER_AUDITED, HOT_JOB) read. AttributionBoundedReadTest proves no detail
rows are loaded and the totals are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Attribution home widget queried every db_perf_attribution row of the
last 24 hours into QRecords and summed them in Java. kof22 writes about
30,000 attribution rows an hour, so one render loaded ~570,000 rows
(540 MB in PostgreSQL). On 2026-10-16 a single admin request logged
"Large number of records in QueryOutputList" at 50k, 100k and 250k rows
and the web pod died with OutOfMemoryError: Java heap space about 50 s
later, after slowing every other request with GC thrash.
- DbPerfWidgetRenderer.attribution() now uses AggregateAction: SUM of
statements and totalMs grouped by unitKind, unitName, tableName,
ordered by time and capped at 5,000 groups (prod has ~1,000).
- RollupService reads the day's attribution detail in 1,000-row keyset
pages through the new DbPerfStore.forEachPage, so the nightly rollup
on the worker is bounded by distinct keys instead of the day's rows.
- AttributionBoundedReadTest counts every detail record QQQ materializes
(post-query customizer): the widget loads none, the rollup never holds
more than BATCH_SIZE at once, and sums stay correct across pages.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The normalizer folded IN and VALUES lists with nested-repetition regular
expressions, which threw StackOverflowError on kof22's 60 KB statements.
That Error escaped catch(Exception), so runs stayed RUNNING, nothing was
logged and no alert fired. List folding is now a linear token scan, the
collector catches Throwable (rethrowing OutOfMemoryError), logs class and
frame at ERROR, marks FAILED and alerts, and stale RUNNING runs are closed.
Verified against kof22's 4,462 real statement texts (under 1 s).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
QRun-IO/.github#2 adds a tag-and-release job that signs vX with the
QRUN_TAG_SIGNING_KEY secret after Maven Central confirms the artifact.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Statement listeners are JVM-wide. Starting attribution on another
instance now stops the previous runtime's listener and flusher, so
repeated server starts (tests, hot swaps) never pin old instances.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every db_perf_ table used the speed icon, which repeats in the nav and
clashes with web performance (kof22's nav icon uniqueness check).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Top queries, live activity, attribution, repeated statements and tables
fold secondary facts into a muted second line of the first cell, so the
home stays readable at every width (kof22's widget readability guard).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Seeded suggestion rules get plain descriptions of what each finds and
what to do, and threshold labels without stray quotes. The spec now
names the alert types dbperf.* (core rejects underscores).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Query plan record action and POST explain: plain or generic EXPLAIN,
and EXPLAIN (ANALYZE, BUFFERS) for single SELECTs in a rolled-back
READ ONLY transaction with statement_timeout, behind explain_analyze,
always audited, ANALYZE plans and parameters never stored. UI API
extension /qqq/v1/qbit/db-performance (overview, activity with live
text only for view_live_text, explain, statements/lookup) with a
committed OpenAPI fragment and contract test. Eight full-width typed
home widgets. Data-driven help bundle with a coverage test. README,
CHANGELOG and UPGRADE.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fourteen rules read their db_perf_rule row and produce deduplicated,
ranked findings with evidence and ready-to-commit Liquibase snippets
(never applied). Acknowledge, Mark resolved, Ignore and Reopen are
standard record actions. Collection failures, query spikes, connection
saturation and blocked chains raise and resolve keyed QAlerts; the
collector, stats access and connections register core health checks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Registers a core StatementExecutionListener (QQQ 4.1.0-RC.13) that
aggregates statements per unit of work (route, scheduled job, process),
table, operation and fingerprint in bounded memory, detects repeated
statement shapes per unit, and flushes each window with one batched
insert per table from a daemon thread that never measures itself.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>