Skip to content

Fix on-the-fly event store schema + InitialData seeding on startup (#219) - #233

Merged
jeremydmiller merged 1 commit into
mainfrom
fix/219-on-the-fly-event-store-and-initial-data
Jun 25, 2026
Merged

Fix on-the-fly event store schema + InitialData seeding on startup (#219)#233
jeremydmiller merged 1 commit into
mainfrom
fix/219-on-the-fly-event-store-and-initial-data

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #219.

Analysis: two gaps, both "Polecat doesn't evaluate schema on first use like Marten does"

The reporter found that IInitialData seeding only ran when ApplyAllDatabaseChangesOnStartup was called. Digging in (per the maintainer's framing — on-the-fly migration for the first usage of a document type or the IEvent) surfaced two distinct gaps:

1. The event store is not created on the fly ❌ → ✅

Document tables ARE ensured on first Store/query (DocumentTableEnsurer), and document queries ensure too — but the event store tables (pc_streams / pc_events / pc_event_progression, plus tag and natural-key tables) were only ever created by ApplyAllDatabaseChangesOnStartup. A first append or event query on a fresh DB failed:

Invalid object name 'pc_streams'.   -- on append
Invalid object name 'pc_events'.    -- on FetchStreamAsync

(Confirmed with a failing repro before the fix.)

Fix: added DocumentTableEnsurer.EnsureEventStoreSchemaAsync (idempotent, once per process, applies the EventStoreFeatureSchema) and call it from:

  • the append path — DocumentSessionBase.SaveChanges, before the data transaction, only when there are events to write (it opens its own connection, like the document ensure);
  • the event read path — QueryEventStore's leaf helpers (FetchStream/Load/FetchStreamState; the aggregate helpers delegate through these), via a new QuerySession.EnsureEventStoreSchemaAsync.

2. InitialData never ran without ApplyAllDatabaseChangesOnStartup ❌ → ✅

The PolecatActivator hosted service that runs IInitialData.Populate was only registered by ApplyAllDatabaseChangesOnStartup / AddAsyncDaemon / AddProjectionCoordinator. So the documented pattern —

builder.Services.AddPolecat(opts => opts.InitialData.Add(new SeedUsers()));

— silently did nothing (the reporter's exact symptom). Fix: AddPolecat now registers the activator unconditionally. Its StartAsync is a no-op when there's no InitialData and ShouldApplyChangesOnStartup is false, so it's safe for every app. The seeder writes through normal sessions, so the document tables it touches are still created on the fly.

Also: honor AutoCreate.None

The maintainer's principle was "on the fly unless AutoCreate.None." The document ensurer actually ignored AutoCreate.None and always created tables. Both the document and the new event on-the-fly paths now skip implicit creation under AutoCreate.None, so the manual-schema opt-out works as intended.

Tests

  • on_the_fly_event_store_tests — appending and querying events on a fresh DB now create the event store; AutoCreate.None correctly skips (append throws).
  • initial_data_host_startup_tests — builds a real service provider with AddPolecat(... InitialData.Add ...) and no ApplyAllDatabaseChangesOnStartup, starts the hosted services, and confirms the seeder ran — using marcominerva's query-then-seed shape (which also exercises on-the-fly document creation from inside the seeder).

Verified on SQL Server 2025 and 2022; broad Seeding/Events/Storage/Documents/Projections/MultiTenancy/Diagnostics suites green (~600 tests, including all existing AutoCreate.None tests).

🤖 Generated with Claude Code

…DatabaseChangesOnStartup (#219)

Two gaps the reporter hit, both about Polecat not evaluating schema on first use the way
Marten does:

1. Event store not created on the fly. Document tables are ensured on first Store/query
   (DocumentTableEnsurer), but the event store tables (pc_streams / pc_events /
   pc_event_progression, plus tag and natural-key tables) were only created by
   ApplyAllDatabaseChangesOnStartup. A first append or event query on a fresh database failed
   with "Invalid object name 'pc_streams'/'pc_events'". Added
   DocumentTableEnsurer.EnsureEventStoreSchemaAsync (idempotent, once per process) and call it
   from the append path (DocumentSessionBase.SaveChanges, before the data transaction, when there
   are events) and the event read path (QueryEventStore's leaf helpers via QuerySession).

2. InitialData never ran without ApplyAllDatabaseChangesOnStartup. The PolecatActivator that
   runs IInitialData seeders was only registered by ApplyAllDatabaseChangesOnStartup /
   AddAsyncDaemon / AddProjectionCoordinator, so the documented
   `options.InitialData.Add(...)` pattern silently did nothing. AddPolecat now registers the
   activator unconditionally (StartAsync is a no-op when there's no InitialData and
   ShouldApplyChangesOnStartup is false), so seeders run on host startup as documented.

Also honor AutoCreate.None: both the document and the new event on-the-fly paths now skip
implicit creation when the user opts out with AutoCreate.None (previously the document ensurer
ignored it and always created on the fly).

Tests: appending/querying events on a fresh DB creates the event store; AutoCreate.None skips it;
and IInitialData runs on host startup via AddPolecat alone (mirroring marcominerva's query-then-seed
seeder). Verified on SQL Server 2025 and 2022; broad Seeding/Events/Storage/Documents/Projections/
MultiTenancy/Diagnostics suites green (~600 tests).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@jeremydmiller
jeremydmiller merged commit 3ff8d0f into main Jun 25, 2026
7 checks passed
@jeremydmiller
jeremydmiller deleted the fix/219-on-the-fly-event-store-and-initial-data branch June 25, 2026 22:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Is ApplyAllDatabaseChangesOnStartup required for Initial Baseline Data?

1 participant