Pin the NServiceBus SQL Server interop transport to a dedicated database under multi-tenanted storage#3271
Merged
Conversation
… database (multi-tenanted storage) When Wolverine's SQL Server message storage is multi-tenanted with a database per tenant, the NServiceBus-compliant SQL Server interop queues must stay on the one shared database NServiceBus reads from — they must not be replicated per tenant. The interop transport already only ever targeted a single database (Storage.Main), but that database was hardcoded and could not be decoupled from the tenanted storage. Add an opt-in: UseNServiceBusSqlServerInterop(connectionString: ...) pins the transport's queue tables, send, and listen paths to one explicit database, independent of storage tenancy. When omitted, behavior is unchanged (binds to the SQL Server Main message store). - NServiceBusSqlServerTransport.ConnectionString (explicit, optional) - ConnectAsync builds Settings from the explicit string when set, and no longer requires SQL Server-backed storage in that case (interop queues are buffered and never touch Wolverine's durable inbox/outbox) - ResolveConnectionString prefers the explicit string (resolvable before ConnectAsync for the Weasel CLI / resource model, so the IDatabase resource also targets the dedicated DB) Tests (vs real SQL Server): dedicated-DB opt-in provisions the queue table ONLY in the dedicated database (never main/tenant DBs) and a send under a tenant lands there; default (no connection string) binds to Main and is never replicated into a tenant database. Plus docs. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jun 29, 2026
Open
Merged
This was referenced Jul 9, 2026
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.
Scenario
From the wolverine.spike sample: Wolverine's SQL Server message storage is multi-tenanted with a database per tenant (
UseMasterTableTenancy), while the NServiceBus-compliant SQL Server interop queues live on one shared database NServiceBus reads from — and must not be replicated into each tenant database.What I found (reproduced vs. real SQL Server)
The interop transport already only ever provisioned/sent/listened against a single database (
Storage.Main) — it never iterated tenant databases (unlike the native SQL Server transport, whoseSqlServerQueuedeliberately fans out overParent.Databases.ActiveDatabases()). So the reported "creates a queue per database" symptom does not occur onmain.The real gap: that single database was hardcoded to
Storage.Main, with no way to point the interop transport at a dedicated shared database decoupled from the tenanted storage.Change — opt-in dedicated database
NServiceBusSqlServerTransport.ConnectionString(optional explicit DB).ConnectAsyncbuildsSettingsfrom the explicit string when set, and no longer requires SQL-Server-backed storage in that case — the interop queues are buffered and never touch Wolverine's durable inbox/outbox.ResolveConnectionStringprefers the explicit string, so it resolves beforeConnectAsync(Weasel CLI / resource model) and theIDatabaseresource also targets the dedicated DB.connectionStringis a trailing optional parameter; when omitted, behavior is unchanged (binds to theMainstore).Tests (vs. real SQL Server)
tenant_onelands a row in the dedicated DB only.Mainonly, never replicated into a tenant database.Plus a docs subsection in
docs/guide/durability/sqlserver.md.🤖 Generated with Claude Code