fix(cron): gate startup on schema readiness before opening the pooled context#329
Merged
Conversation
… context The Cron host does not own migrations (the API is the sole migrator), but its OpenShockContext binds Postgres enum types (email_status, email_type, ...) to CLR enums by name via MapEnum<T>(). Npgsql resolves those names against the type catalog it loads on the pooled data source's first connection and caches for the life of the process. If that first connection happens before the migrator has created a newly-added enum, every query using it fails permanently with 'data type name ... could not be found in the types that were loaded by Npgsql' until the process restarts - which is exactly what hit the EmailOutboxDeliveryJob claim query during the outbox rollout. Add WaitForOpenShockSchemaReady (polls GetPendingMigrationsAsync with capped backoff and a timeout) and call it in the Cron host right after the common middleware, before Hangfire or any job opens the pooled context. Cron performs no schema writes; the API stays the sole migrator.
|
Ready to review this PR? Stage has broken it down into 2 individual chapters for you:
Chapters generated by Stage for commit 9b2472c on Jul 1, 2026 10:32am UTC. |
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a startup gate to the Cron host so it won’t open an enum-mapped OpenShockContext pool until the database schema (migrations) is fully applied by the API host, preventing Npgsql from permanently caching “missing” Postgres enum types during deploy races.
Changes:
- Introduces
WaitForOpenShockSchemaReady(DatabaseOptions, TimeSpan?)which pollsGetPendingMigrationsAsync()with capped exponential backoff and a timeout. - Calls the new gate in
Cron/Program.csimmediately afterUseCommonOpenShockMiddleware()and before Hangfire/job execution can touch the pooled context.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
Cron/Program.cs |
Blocks Cron startup until schema is migration-complete before any job execution can open/use the pooled EF Core context. |
Common/OpenShockMiddlewareHelper.cs |
Adds a reusable schema-readiness wait helper for non-migrator hosts to avoid enum type-catalog caching races. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
The Cron host was failing every
EmailOutboxDeliveryJobclaim query in production with:Root cause
OpenShockContextbinds the Postgres enum types (email_status,email_type, …) to CLR enums by name viaMapEnum<T>(). Npgsql resolves those names against the type catalog it loads on the pooled data source's first connection, then caches it for the life of the process.The Cron host does not own migrations — the API is the sole migrator (
API/Program.cscallsApplyPendingOpenShockMigrations; Cron never did). So during the outbox rollout, Cron's data source loaded its type catalog before the API had applied the migration that creates those enums. The missing type was cached as absent, and every query using it failed permanently until the process was restarted.This is a deploy-time race that recurs whenever a new enum consumed by Cron is added.
Fix
WaitForOpenShockSchemaReady(DatabaseOptions)inCommon/OpenShockMiddlewareHelper.cs— pollsGetPendingMigrationsAsync()with capped backoff and a timeout.Cron/Program.csright afterUseCommonOpenShockMiddleware(), before Hangfire or any job opens the pooled context.Cron performs no schema writes; the API stays the sole migrator. This closes the race window without reintroducing a cross-host migration write-race.
Notes
AddPooledDbContextFactorybareUseNpgsqlinAddOpenShockDBis dead config (TryAddmeans the enum-mapped pool options win), so it was not the cause — left untouched here.