Skip to content

fix(taxonomies): never query a declared collection whose ec_* table is missing#2043

Open
swissky wants to merge 2 commits into
emdash-cms:mainfrom
swissky:fix/term-counts-skip-missing-tables
Open

fix(taxonomies): never query a declared collection whose ec_* table is missing#2043
swissky wants to merge 2 commits into
emdash-cms:mainfrom
swissky:fix/term-counts-skip-missing-tables

Conversation

@swissky

@swissky swissky commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Stops term counting from issuing queries against ec_* tables that don't exist, which currently floods D1 logs with phantom no such table: ec_posts errors on any site without a posts collection.

The bug: migration 006_taxonomy_defs seeds the default category/tag defs bound to ["posts"] whether or not that collection is ever created. fetchVisibleTermCounts then builds a UNION that joins ec_posts, catches the missing-table error, and retries per collection — the result is correct, but the failed statement itself is logged by the database. On D1 each one surfaces as a no such table: ec_posts: SQLITE_ERROR error span in Workers Observability, once per taxonomy per uncached render, drowning out real errors. (Observed in production on a Workers + D1 site with Logs/Traces enabled; the same drift also occurs when a declared collection is deleted.)

The fix: resolve the set of existing ec_* tables upfront (via the dialect-aware listTablesLike, same helper migration 051 uses) and scope the count to collections whose table actually exists — a failing statement is never sent. To keep the count path at one round-trip per taxonomy, the table list is cached per isolate in a new database/content-tables-cache.ts (same pattern as the loader's taxonomy-names cache, including the dbIsIsolated bypass) and reset by SchemaRegistry.createCollection/deleteCollection. A reader that still hits a dropped table (stale list from another isolate's delete) resets the cache and retries once, preserving the previous partial-but-correct behavior.

The getTerm query-budget test stays at 4 queries; it now primes the once-per-isolate table lookup so the budget reflects the steady state.

Tests:

  • never sends a query referencing a missing ec_* table — asserts on the SQL sent through the Kysely executor; fails on main, passes with the fix.
  • recovers when the isolate-cached table list goes stale — covers the refresh-and-retry fallback.

Possible follow-up (out of scope here): stop hardcoding ["posts"] in the migration-seeded defaults and let seeds declare the binding — happy to open a Discussion if that's of interest.

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
  • pnpm test passes (or targeted tests for my change)
  • pnpm format has been run
  • I have added/updated tests for my changes (if applicable)
  • User-visible strings in the admin UI are wrapped for translation (if applicable). Do not include messages.po changes except in translation PRs — a workflow extracts catalogs on merge to main.
  • I have added a changeset (if this PR changes a published package)
  • New features link to an approved Discussion: https://github.com/emdash-cms/emdash/discussions/...

AI-generated code disclosure

  • This PR includes AI-generated code — model/tool: Cursor + Claude Fable 5

Screenshots / test output

 Test Files  355 passed | 1 skipped (356)
      Tests  4723 passed | 3 skipped (4726)

Production log entry that motivated the fix (Workers Observability, one per taxonomy per uncached render):

error scl-lauerz d1_all ERROR
error.type: "no such table: ec_posts: SQLITE_ERROR"
db.query.text: "SELECT taxonomy_id, SUM(count) AS count FROM ( SELECT ct.taxonomy_id ... INNER JOIN \"ec_posts\" AS e ON e.id = ct.entry_id ...)"

… of probing missing ones

The migration-seeded category/tag defs declare ["posts"] whether or not
that collection exists, so on any site without a posts collection every
visible-term-count computation issued a UNION that joined ec_posts and
failed. The retry path made the result correct, but the failed statement
itself is logged by the database — on D1 it surfaces as a
"no such table: ec_posts" error span in Workers Observability, once per
taxonomy per uncached render, drowning out real errors.

Resolve the set of existing ec_* tables upfront (cached per isolate,
reset by SchemaRegistry create/delete and by readers that observe a
stale list) and scope the count to collections whose table exists, so a
query referencing a missing table is never sent.
@changeset-bot

changeset-bot Bot commented Jul 14, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f58d88b

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 review/needs-review No maintainer or bot review yet area/core size/M labels Jul 14, 2026
@pkg-pr-new

pkg-pr-new Bot commented Jul 14, 2026

Copy link
Copy Markdown

Open in StackBlitz

@emdash-cms/admin

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

@emdash-cms/auth

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

@emdash-cms/auth-atproto

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

@emdash-cms/blocks

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

@emdash-cms/cloudflare

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

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

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

emdash

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

create-emdash

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

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

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

@emdash-cms/plugin-cli

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

@emdash-cms/plugin-types

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

@emdash-cms/registry-client

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

@emdash-cms/registry-lexicons

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

@emdash-cms/sandbox-workerd

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

@emdash-cms/x402

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

@emdash-cms/plugin-ai-moderation

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

@emdash-cms/plugin-atproto

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

@emdash-cms/plugin-audit-log

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

@emdash-cms/plugin-color

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

@emdash-cms/plugin-embeds

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

@emdash-cms/plugin-field-kit

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

@emdash-cms/plugin-forms

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

@emdash-cms/plugin-webhook-notifier

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

commit: f58d88b

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

Copy link
Copy Markdown
Contributor

Query-count snapshot changes

4 routes changed, total Δ +4 queries.

SQLite

Route Before After Δ
GET /posts/building-for-the-long-term (cold) 18 19 +1

D1

Route Before After Δ
GET /category/development (cold) 23 24 +1
GET /posts/building-for-the-long-term (cold) 29 30 +1
GET /tag/webdev (cold) 23 24 +1

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

@github-actions github-actions Bot added size/L and removed size/M labels Jul 14, 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-review No maintainer or bot review yet size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant