Skip to content

fix(widgets): avoid duplicate queries and repeated archive date formatting - #3068

Open
logelog wants to merge 2 commits into
emdash-cms:mainfrom
logelog:fix/public-render-query-work
Open

fix(widgets): avoid duplicate queries and repeated archive date formatting#3068
logelog wants to merge 2 commits into
emdash-cms:mainfrom
logelog:fix/public-render-query-work

Conversation

@logelog

@logelog logelog commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Reduces duplicate database reads and unnecessary content processing during public page renders that use widget areas or the Archives widget.

  • Share widget reads while prefetch is running. Named widget areas await the request's pending bulk load instead of issuing duplicate queries. Failed prefetch falls back to a scoped lookup. Four concurrent named lookups use two widget queries instead of six.
  • Read only the dates Archives needs. Building a list of archive months previously loaded every matching post's full content and prepared its bylines and taxonomy terms. Archives now reads publication and modification dates through the request's database connection. Reads preserve the collection's locale filtering, exclude unpublished and deleted entries, deduplicate within each request, and use the existing content cache invalidation. Preview and isolated databases bypass the shared cache.
  • Format only displayed archive labels. Entries are grouped and counted before month labels are formatted. Archive links, ordering, and counts are preserved, including when the display limit hides older groups.
  • Measure the HTML request path. Page requests in the query counter and performance probe send Accept: text/html, so they exercise the same layout prefetch as browser requests. RSS uses its own content type.

The snapshot change includes a measurement correction: the fixture's warm homepage count changes from 6 to 10 because the harness includes prefetch work that already runs for browser requests. Old and corrected snapshot totals are not a like-for-like performance comparison.

Type of change

  • Bug fix
  • Feature
  • 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.
  • Targeted tests pass against real SQLite and PostgreSQL databases.
  • Changed files have been formatted.
  • Regression tests reproduce the original failures and pass with the fixes.
  • Admin string translation: not applicable; no admin strings change.
  • User-facing changesets are included and reviewed.
  • New feature approval: not applicable; this fixes existing behavior.
  • UI screenshots: not applicable; no UI change.

AI-generated code disclosure

  • This PR includes AI-generated code. GPT-5.6.

Screenshots / test output

Screenshots: not applicable; the rendered Archives HTML is unchanged.

The same local D1/workerd benchmark was run against the reference implementation, the previous PR head, and this update. Each route used five warmup requests followed by five batches of 50 requests. Values below are the median of batch mean active CPU per response, including garbage collection and excluding idle time.

Fixture and route Reference implementation Previous PR head This update
8 posts, post page with Archives 16.4 ms 15.0 ms 14.4 ms
500 posts, post page with Archives 46.7 ms 32.9 ms 15.5 ms
500 posts, homepage 10.6 ms 10.0 ms 10.9 ms

The reference uses the benchmark branch runtime; the previous PR head is db122778. All three runs use the same seeded D1 data within each dataset. The larger fixture has 500 synthetic posts across 24 months. In the 500-post fixture, all three implementations render byte-identical Archives HTML with the same 12 displayed groups, links, labels, and counts. The extra date projection reduces the large-fixture post cost by about 53% against the previous PR head; the small-fixture differences are modest. These are local profiler measurements, separate from live Workers invocation CPU.

Validation also covers publication visibility, locale filtering, request deduplication, content cache invalidation, preview isolation, failure recovery, SQL parameter binding, and widget prefetch regressions. The standard fixture's query counts stay unchanged with the date projection on both SQLite and D1; the SQL snapshots now show the narrower Archives query.

@changeset-bot

changeset-bot Bot commented Sep 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: bdafeee

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

This PR includes changesets to release 17 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
create-emdash Patch
@emdash-cms/gutenberg-to-portable-text Patch
@emdash-cms/x402 Patch
@emdash-cms/auth-atproto Patch
@emdash-cms/release-service 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

@emdashbot emdashbot Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR's three changes are well-scoped and fit EmDash's patterns: a real duplicate-query bug during concurrent widget-area reads, wasted date-formatting work in the Archives widget, and a perf-harness bug that failed to exercise the HTML prefetch path. I traced each change against the relevant request-cache, middleware, and test code.

What I checked:

  • Read the full diff and the changed source files (packages/core/src/widgets/index.ts, archives.ts, astro/prefetch.ts, infra/perf-monitor/probe/src/measure.ts, scripts/query-counts.mjs) plus packages/core/src/request-cache.ts and the middleware.
  • Verified prefetchLayoutData is called only for HTML navigations and is wrapped via after(), so the new requestCached("widget-areas", getWidgetAreas) sharing plus peekRequestCache in getWidgetArea(name) correctly collapses concurrent named reads onto the in-flight bulk load.
  • Confirmed groupEntriesByPublishedAt still groups/counts over the full input but only calls toLocaleDateString after slicing to the display limit.
  • Checked the D1 query-count snapshot increase against the harness code: the old harness sent no Accept header, so EmDash skipped its HTML prefetch; the new text/html header makes the snapshot reflect what browser requests already do. The SQLite snapshot is unchanged, which is consistent with prefetch running only on remote adapters.
  • Reviewed the two changesets; both are present-tense, name the user-facing surface, and explain the observable behavior.
  • Inspected the new tests. The widget-request-cache and archive formatting tests reproduce the claimed regressions; the perf-monitor test verifies the probe now requests HTML.

I found no logic bugs, security issues, convention violations, or missing tests. The D1 snapshot change is a measurement correction, not new production queries, and the code reduces real logged-out work on remote adapters.

One minor note: the perf-monitor probe test lives under packages/core/tests/unit/ because the infra/perf-monitor package has no test script. That is pragmatic, but if the infra package later grows its own vitest config this test should move there. Not a blocker.

@pkg-pr-new

pkg-pr-new Bot commented Sep 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/auth-atproto

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

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

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

emdash

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

create-emdash

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

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

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

@emdash-cms/plugin-cli

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

@emdash-cms/plugin-types

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

@emdash-cms/registry-client

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

@emdash-cms/registry-lexicons

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

@emdash-cms/registry-moderation

npm i https://pkg.pr.new/@emdash-cms/registry-moderation@3068

@emdash-cms/registry-verification

npm i https://pkg.pr.new/@emdash-cms/registry-verification@3068

@emdash-cms/sandbox-workerd

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-field-kit

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: bdafeee

@github-actions github-actions Bot added the query-count changed PR diff modifies query-count snapshot files label Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Query-count snapshot changes

17 routes changed, total Δ +67 queries.

D1

Route Before After Δ
GET / (cold) 17 22 +5
GET / (warm) 6 10 +4
GET /category/development (cold) 23 27 +4
GET /category/development (warm) 11 14 +3
GET /contributors (cold) 17 22 +5
GET /contributors (warm) 6 10 +4
GET /contributors-naive (cold) 24 29 +5
GET /contributors-naive (warm) 13 17 +4
GET /pages/about (cold) 17 22 +5
GET /pages/about (warm) 6 10 +4
GET /posts (cold) 17 22 +5
GET /posts (warm) 6 10 +4
GET /posts/building-for-the-long-term (warm) 17 16 -1
GET /search (cold) 22 27 +5
GET /search (warm) 11 15 +4
GET /tag/webdev (cold) 23 27 +4
GET /tag/webdev (warm) 11 14 +3

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

@github-actions github-actions Bot added the review/approved Approved; no new commits since label Sep 11, 2026
@github-actions github-actions Bot added review/needs-rereview Author pushed changes since the last review size/XL overlap and removed review/approved Approved; no new commits since size/L labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

Overlapping PRs

This PR modifies files that are also changed by other open PRs:

This may cause merge conflicts or duplicated work. A maintainer will coordinate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/core overlap query-count changed PR diff modifies query-count snapshot files review/needs-rereview Author pushed changes since the last review size/XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant