Refactor: Cache manifest and init data to reduce cold-start queries#378
Refactor: Cache manifest and init data to reduce cold-start queries#378gruntlord5 wants to merge 10 commits intoemdash-cms:mainfrom
Conversation
Cold starts currently run ~20 database queries before the first page can render (FTS verify, 32+ migration checks, 3 sequential site info queries, N+1 manifest rebuild). Most of this produces the same result every time and only needs to rerun when an admin changes something. This commit reduces cold-start queries from ~20 to ~2: 1. **Manifest caching**: getManifest() now checks an in-memory cache, then a DB-persisted cache (1 query), before falling back to the full N+1 rebuild. invalidateManifest() clears both caches and is already called from the MCP server on schema/plugin changes. 2. **Init data caching**: Plugin states + site info are cached together in a single options row. On cold start, 1 query replaces 2-4. Site info uses getMany() (batch WHERE IN) instead of 3 sequential get() calls even on cache miss. 3. **Lazy migration wrapper**: New wrapWithLazyMigrations() dialect wrapper defers migration checks until the first schema error. On established sites the schema is stable, so checking 30+ migrations on every cold start is pure overhead. Fresh installs and upgrades are handled automatically when the first query hits "no such table". Applied to all built-in adapters (SQLite, libSQL, PostgreSQL, D1). Third-party dialects that don't use the wrapper still get eager migrations as a fallback. 4. **Skip FTS verify on init**: FTS corruption is rare; checking every cold start adds latency without benefit on stable sites.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Cold starts currently run ~20 database queries before the first page can render (FTS verify, 32+ migration checks, 3 sequential site info queries, N+1 manifest rebuild). Most of this produces the same result every time and only needs to rerun when an admin changes something. This commit reduces cold-start queries from ~20 to ~2: 1. **Manifest caching**: getManifest() now checks an in-memory cache, then a DB-persisted cache (1 query), before falling back to the full N+1 rebuild. invalidateManifest() clears both caches and is already called from the MCP server on schema/plugin changes. 2. **Init data caching**: Plugin states + site info are cached together in a single options row. On cold start, 1 query replaces 2-4. Site info uses getMany() (batch WHERE IN) instead of 3 sequential get() calls even on cache miss. 3. **Lazy migration wrapper**: New wrapWithLazyMigrations() dialect wrapper defers migration checks until the first schema error. On established sites the schema is stable, so checking 30+ migrations on every cold start is pure overhead. Fresh installs and upgrades are handled automatically when the first query hits "no such table". Applied to all built-in adapters (SQLite, libSQL, PostgreSQL, D1). Third-party dialects that don't use the wrapper still get eager migrations as a fallback. 4. **Skip FTS verify on init**: FTS corruption is rare; checking every cold start adds latency without benefit on stable sites.
…d5/emdash into perf/cold-start-caching
🦋 Changeset detectedLatest commit: b19d1a0 The changes in this PR will be included in the next version bump. This PR includes changesets to release 9 packages
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 |
|
All contributors have signed the CLA ✍️ ✅ |
|
I have read the CLA Document and I hereby sign the CLA |
@emdash-cms/admin
@emdash-cms/auth
@emdash-cms/blocks
@emdash-cms/cloudflare
emdash
create-emdash
@emdash-cms/gutenberg-to-portable-text
@emdash-cms/x402
@emdash-cms/plugin-ai-moderation
@emdash-cms/plugin-atproto
@emdash-cms/plugin-audit-log
@emdash-cms/plugin-color
@emdash-cms/plugin-embeds
@emdash-cms/plugin-forms
@emdash-cms/plugin-webhook-notifier
commit: |
|
Hey! This would be great to get in. Can you check why the tests are failing? |
They all passed when I ran them now, which test is failing? |
|
Yes, they are probably failing because of conflicts with changes thatw ere merged after you opened this/ |
is there a different commit I should be testing against? |
|
No this is the right commit – I just mean that you need to ensure that the tests pass now that main has been merged into this. |
|
I merged the changes and all the tests pass locally. |
What does this PR do?
Reduces cold-start database queries from ~20 to ~2 on established sites by caching manifest and init data, deferring migrations to the dialect layer, and skipping FTS verification on init.
Closes #346
Type of change
Changes
Manifest caching
getManifest()checks an in-memory cache, then a DB-persisted cache (1 query), before falling back to the full N+1 schema registry rebuild.invalidateManifest()clears both caches -- it's already called from the MCP server on schema/plugin changes.Init data caching
Plugin states + site info are cached together in a single options row. On cold start, 1 query replaces 2-4. Site info also uses
getMany()(batchWHERE IN) instead of 3 sequentialget()calls on cache miss.Lazy migration dialect wrapper
New
wrapWithLazyMigrations()utility wraps any Kysely dialect to defer migration checks until the first schema error (no such table/does not exist). Applied to all built-in adapters (SQLite, libSQL, PostgreSQL, D1). Third-party dialects that don't use the wrapper still get eager migrations as a fallback.Skip FTS verify on init
FTS corruption is rare; checking every cold start adds latency without benefit on stable sites.
Checklist
pnpm typecheckpasses (pre-existing failures in admin/core unrelated to this PR -- same on main)pnpm --silent lint:json | jq '.diagnostics | length'returns 14 (down from 16 on main -- we removed 2 unused imports)pnpm testpasses (1829 passed, 18 pre-existing failures same on main)pnpm formathas been runAI-generated code disclosure
Screenshots / test output
Build passes for both
emdashand@emdash-cms/cloudflare. Test suite: 1829 tests passed, 18 pre-existing failures (identical to cleanmain). Lint diagnostics reduced from 16 to 14 (removed 2 unused imports from FTS verify removal).