Skip to content

fix(core): capture streaming queries in query instrumentation#1580

Merged
ascorbic merged 1 commit into
mainfrom
fix/query-counts-full-capture
Jun 22, 2026
Merged

fix(core): capture streaming queries in query instrumentation#1580
ascorbic merged 1 commit into
mainfrom
fix/query-counts-full-capture

Conversation

@ascorbic

Copy link
Copy Markdown
Collaborator

What does this PR do?

The query instrumentation (EMDASH_QUERY_LOG=1) flushed its per-query recorder when middleware returned, i.e. when the response headers were ready but before the body streamed. Queries issued by components during streaming were appended to the recorder but never emitted, so the query-counts harness only ever saw the pre-header queries.

Astro 7 makes queued rendering the default, which shifts more queries into the streaming phase and made the gap obvious: the post-detail fixture route reported 9 queries in the snapshot while the request actually ran 24 (the [emdash-stream-end] summary already knew the real number).

Changes:

  • Flush the recorder at stream end, in the same transform that already snapshots the stream-end metrics, so queries issued while the body renders are captured. A fallback flush remains in the middleware finally for bodyless responses (redirects, 304s), and flushRecorder is now idempotent so the two paths can't double-emit.
  • Add a companion query-text snapshot (query-counts.queries.{target}.json): a per route+phase map of the actual SQL to its occurrence count. A count change now shows which query appeared or vanished, not just that the number moved. Whitespace is normalized and variable-arity IN (?, ?, ...) lists fold to in (...) so the text stays stable.
  • Regenerated both snapshots for sqlite and d1. Counts now reflect full per-request query work (e.g. post detail: sqlite 16->24; d1 cold 26->35, warm 15->24). The new totals match the db.count the stream-end line always reported.

Why now / context

Surfaced while reviewing the Astro 7 upgrade (#1579): its CI showed the post page "dropping" from 16 to 9 queries. Investigation found the total per-request query count was unchanged (24 on both Astro 6 and 7) — queued rendering had simply moved queries past the point where the recorder was flushed. This is a pre-existing instrumentation bug the upgrade merely exposed; fixing it here makes the metric measure the whole request regardless of when queries run. Recommend landing this first so #1579 rebases to a no-op query-count diff.

Type of change

  • Bug fix
  • Feature (requires maintainer-approved Discussion)
  • Refactor (no behavior change)
  • Translation
  • Documentation
  • Performance improvement
  • Tests
  • Chore (dependencies, CI, tooling)

Checklist

  • I have read CONTRIBUTING.md
  • pnpm typecheck passes
  • pnpm lint passes (0 diagnostics)
  • pnpm test passes (core: 4024, incl. 2 new stream-end tests)
  • pnpm format has been run
  • I have added/updated tests for my changes (deferred-flush capture + idempotency)
  • User-visible strings in the admin UI are wrapped for translation (n/a — no admin UI strings)
  • I have added a changeset
  • New features link to an approved Discussion (n/a — bug fix)

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Claude Opus 4.8

Screenshots / test output

Verified with the harness on both targets: every route's snapshot count now equals the stream-end db.count (e.g. GET / 6->11, GET /posts/building-for-the-long-term 16->24 on sqlite). The query-text snapshot for the post page shows the expected breakdown (widget-area join x2, byline join x4, taxonomy join x4, comments, menus, settings, etc.) summing to 24.

The per-query log (EMDASH_QUERY_LOG=1) flushed its recorder when middleware
returned, i.e. when the response headers were ready but before the body
streamed. Queries issued by components during streaming were appended to the
recorder but never emitted, so the query-count harness only saw pre-header
queries. Astro 7's queued rendering moves more queries into the streaming
phase, which made the gap obvious (the post-detail fixture route reported 9
queries while the request actually ran 24).

Flush the recorder when the body finishes streaming instead (in the same
stream-end transform that already snapshots the metrics), and keep a fallback
flush in the middleware finally for bodyless responses (redirects, 304s). The
flush is now idempotent so the two paths can't double-emit.

Also adds a companion query-text snapshot to the harness
(query-counts.queries.{target}.json): a per-route map of the actual SQL to its
occurrence count, so a count change shows which query appeared or vanished, not
just that the number moved. Snapshots regenerated for both targets now reflect
full per-request query counts.
@changeset-bot

changeset-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ae12c00

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
emdash Patch
@emdash-cms/cloudflare Patch
@emdash-cms/sandbox-workerd Patch
@emdash-cms/fixture-perf-site Patch
@emdash-cms/perf-demo-site Patch
@emdash-cms/cache-demo-site Patch
@emdash-cms/do-demo-site Patch
@emdash-cms/do-solo-demo-site Patch
@emdash-cms/admin Patch
@emdash-cms/auth Patch
@emdash-cms/blocks Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
create-emdash Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/plugin-embeds Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions github-actions Bot added query-count changed PR diff modifies query-count snapshot files review/needs-review No maintainer or bot review yet labels Jun 22, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Query-count snapshot changes

36 routes changed, total Δ +221 queries.

SQLite

Route Before After Δ
GET / (cold) 6 11 +5
GET / (warm) 6 11 +5
GET /category/development (cold) 10 15 +5
GET /category/development (warm) 9 14 +5
GET /contributors (cold) 6 11 +5
GET /contributors (warm) 6 11 +5
GET /contributors-naive (cold) 13 18 +5
GET /contributors-naive (warm) 13 18 +5
GET /pages/about (cold) 5 10 +5
GET /pages/about (warm) 5 10 +5
GET /posts (cold) 6 11 +5
GET /posts (warm) 6 11 +5
GET /posts/building-for-the-long-term (cold) 16 24 +8
GET /posts/building-for-the-long-term (warm) 16 24 +8
GET /search (cold) 6 12 +6
GET /search (warm) 6 12 +6
GET /tag/webdev (cold) 9 14 +5
GET /tag/webdev (warm) 9 14 +5

D1

Route Before After Δ
GET / (cold) 16 22 +6
GET / (warm) 5 11 +6
GET /category/development (cold) 19 26 +7
GET /category/development (warm) 8 14 +6
GET /contributors (cold) 15 22 +7
GET /contributors (warm) 5 11 +6
GET /contributors-naive (cold) 22 29 +7
GET /contributors-naive (warm) 12 18 +6
GET /pages/about (cold) 13 20 +7
GET /pages/about (warm) 4 10 +6
GET /posts (cold) 15 22 +7
GET /posts (warm) 5 11 +6
GET /posts/building-for-the-long-term (cold) 26 35 +9
GET /posts/building-for-the-long-term (warm) 15 24 +9
GET /search (cold) 14 22 +8
GET /search (warm) 5 12 +7
GET /tag/webdev (cold) 19 26 +7
GET /tag/webdev (warm) 8 14 +6

Comparing snapshot files between base and head. Updated automatically on each push.

@github-actions

Copy link
Copy Markdown
Contributor

Scope check

This PR changes 858 lines across 10 files. Large PRs are harder to review and more likely to be closed without review.

If this scope is intentional, no action needed. A maintainer will review it. If not, please consider splitting this into smaller PRs.

See CONTRIBUTING.md for contribution guidelines.

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
docs ae12c00 Jun 22 2026, 11:41 AM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
❌ Deployment failed
View logs
emdash-demo-cache ae12c00 Jun 22 2026, 11:41 AM

@cloudflare-workers-and-pages

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
emdash-playground ae12c00 Jun 22 2026, 11:41 AM

@pkg-pr-new

pkg-pr-new Bot commented Jun 22, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

npm i https://pkg.pr.new/@emdash-cms/admin@1580

@emdash-cms/auth

npm i https://pkg.pr.new/@emdash-cms/auth@1580

@emdash-cms/auth-atproto

npm i https://pkg.pr.new/@emdash-cms/auth-atproto@1580

@emdash-cms/blocks

npm i https://pkg.pr.new/@emdash-cms/blocks@1580

@emdash-cms/cloudflare

npm i https://pkg.pr.new/@emdash-cms/cloudflare@1580

@emdash-cms/contentful-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/contentful-to-portable-text@1580

emdash

npm i https://pkg.pr.new/emdash@1580

create-emdash

npm i https://pkg.pr.new/create-emdash@1580

@emdash-cms/gutenberg-to-portable-text

npm i https://pkg.pr.new/@emdash-cms/gutenberg-to-portable-text@1580

@emdash-cms/plugin-cli

npm i https://pkg.pr.new/@emdash-cms/plugin-cli@1580

@emdash-cms/plugin-types

npm i https://pkg.pr.new/@emdash-cms/plugin-types@1580

@emdash-cms/registry-client

npm i https://pkg.pr.new/@emdash-cms/registry-client@1580

@emdash-cms/registry-lexicons

npm i https://pkg.pr.new/@emdash-cms/registry-lexicons@1580

@emdash-cms/sandbox-workerd

npm i https://pkg.pr.new/@emdash-cms/sandbox-workerd@1580

@emdash-cms/x402

npm i https://pkg.pr.new/@emdash-cms/x402@1580

@emdash-cms/plugin-ai-moderation

npm i https://pkg.pr.new/@emdash-cms/plugin-ai-moderation@1580

@emdash-cms/plugin-atproto

npm i https://pkg.pr.new/@emdash-cms/plugin-atproto@1580

@emdash-cms/plugin-audit-log

npm i https://pkg.pr.new/@emdash-cms/plugin-audit-log@1580

@emdash-cms/plugin-color

npm i https://pkg.pr.new/@emdash-cms/plugin-color@1580

@emdash-cms/plugin-embeds

npm i https://pkg.pr.new/@emdash-cms/plugin-embeds@1580

@emdash-cms/plugin-field-kit

npm i https://pkg.pr.new/@emdash-cms/plugin-field-kit@1580

@emdash-cms/plugin-forms

npm i https://pkg.pr.new/@emdash-cms/plugin-forms@1580

@emdash-cms/plugin-webhook-notifier

npm i https://pkg.pr.new/@emdash-cms/plugin-webhook-notifier@1580

commit: ae12c00

@ascorbic ascorbic merged commit ca47da4 into main Jun 22, 2026
69 of 71 checks passed
@ascorbic ascorbic deleted the fix/query-counts-full-capture branch June 22, 2026 13:38
@emdashbot emdashbot Bot mentioned this pull request Jun 22, 2026
ascorbic added a commit that referenced this pull request Jun 22, 2026
…ixed harness

The pre-merge snapshots were measured with the old query-counts harness
that missed queries issued during streaming (fixed in #1580). Regenerated
against the fixed harness so the numbers reflect reality: the per-isolate
taxonomy-defs cache removes the repeated _emdash_taxonomy_defs read on
every public render (-1 query per route).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core query-count changed PR diff modifies query-count snapshot files review/needs-review No maintainer or bot review yet size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant