Skip to content

Parallelize tenant database initialization in EnsureAllTenantDatabasesCreatedAsync#2482

Merged
jeremydmiller merged 3 commits intomainfrom
fix/2477-parallelize-tenant-db-init
Apr 9, 2026
Merged

Parallelize tenant database initialization in EnsureAllTenantDatabasesCreatedAsync#2482
jeremydmiller merged 3 commits intomainfrom
fix/2477-parallelize-tenant-db-init

Conversation

@jeremydmiller
Copy link
Copy Markdown
Member

Fixes #2477

Summary

  • Replaced sequential foreach loops in EnsureAllTenantDatabasesCreatedAsync() with Parallel.ForEachAsync (MaxDegreeOfParallelism=10) in both TenantedDbContextBuilderByConnectionString and TenantedDbContextBuilderByDbDataSource
  • Tenant assignments are enumerated once into a list before parallel execution to avoid multiple enumeration
  • Added unit tests verifying all tenants are initialized, that concurrent execution is faster than sequential, and that failures surface as exceptions

Details

The previous implementation processed tenants one at a time, causing significant startup delays for deployments with 100+ tenants. With Parallel.ForEachAsync, up to 10 databases can be initialized concurrently, dramatically reducing startup time.

Error handling is preserved: if any tenant initialization fails, the exception propagates via the returned Task, consistent with the previous behavior of stopping on the first error.

Test plan

  • Builds without errors on net9.0
  • Unit tests in parallel_tenant_initialization_tests.cs verify all tenants processed, concurrent execution is faster than sequential, and failures surface as exceptions
  • Existing multi-tenancy integration tests in EfCoreTests.MultiTenancy cover end-to-end tenant initialization

🤖 Generated with Claude Code

jeremydmiller and others added 3 commits April 9, 2026 15:50
Replace culture-sensitive ToLower() with ToLowerInvariant() in all SQL identifier
normalization paths. In Turkish locale, 'I'.ToLower() produces dotless 'ı' instead
of 'i', corrupting SQL names such as queue/table identifiers used in envelope storage.

Affects SqlServerTransport, PostgresqlTransport, MySqlTransport, SqliteTransport,
and SagaTableDefinition. Adds a regression test that verifies SanitizeIdentifier
behaves correctly under tr-TR culture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
`PullSagaIdFromEnvelopeFrame` used `NameInCode()` (bare type name) when
emitting `TryParse` calls for non-Guid saga ID types. For strong-typed
identifiers in a custom namespace this produced unresolvable type references
(CS0103/CS0246) in the generated handler code.

Changed to `FullNameInCode()` so the fully qualified type name is always
emitted, matching the approach already used in `PullSagaIdFromMessageFrame`
for its strong-typed-ID path.

Added a regression test (`Bug_2465_strong_typed_id_saga_codegen`) that uses a
`record struct StrongSagaId(Guid)` with a `TryParse` method and verifies that
the host starts (code generation succeeds) and that messages routed through the
envelope path are handled correctly.

Also added a "Strong-Typed Identifiers" documentation section in sagas.md.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…sCreatedAsync (#2477)

Replace sequential foreach loops in TenantedDbContextBuilderByConnectionString and
TenantedDbContextBuilderByDbDataSource with Parallel.ForEachAsync (MaxDegreeOfParallelism=10),
reducing startup time for deployments with many tenants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@jeremydmiller jeremydmiller merged commit f87bdd0 into main Apr 9, 2026
17 of 19 checks passed
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.

Parallelize tenant database initialization in BuildAllAsync

1 participant