Skip to content

GH-5435 Tenant databases with one name on one host are told apart by port - #5436

Merged
jeremydmiller merged 1 commit into
mainfrom
gh-5435-tenant-uri-port
Nov 7, 2026
Merged

jeremydmiller merged 1 commit into
mainfrom
gh-5435-tenant-uri-port

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

Closes #5435

What was wrong

A relational message store's agent Uri is wolverinedb://{engine}/{host}/{database}/{schema}. It leaves out the port: PostgreSQL and MySQL keep the port apart from the host, and SQL Server's host,port is split at the comma. Two tenant databases with the same name on the same host but different ports (two clusters or two containers on one box) therefore shared one Uri. MessageStoreCollection keys stores by Uri, so only the first database got a durability agent. The second database's scheduled messages were never released, and its dead nodes' work was never recovered.

Found by the durability chaos rig, leg 8 (#5414). In a 20 second run with one pause fault, all 820 of the second tenant's delayed messages stayed Scheduled with owner 0.

The fix

  • New internal IPortQualifiedAgentUri, implemented by MessageDatabase<T>. It exposes the port and can switch the Uri to wolverinedb://{engine}/{host}:{port}/{database}/{schema}. The qualified Uri is built only when it is asked for.
  • MessageStoreCollection.categorizeStore checks for the collision. If a non-main store's Uri is already taken by another database on a different port, each of the two that is not the main store is qualified. The colliding Uri is remembered, so a database discovered later with that Uri is qualified too. Nodes that discover tenant databases in different orders therefore end up with the same Uris.
  • The same database reached twice (same port) is not a collision. The main store never changes its Uri.
  • MySqlMessageStore.Describe() now sets Port.

No Uri changes for any configuration that works today. Qualification only happens for the collision, so agent assignments, node_assignments rows and CritterWatch's DatabaseUri stay as they are for everyone else. DurabilityProjectionAffinity already normalizes ports away, so projection affinity is unaffected.

Tests

  • CoreTests.Persistence.tenant_databases_told_apart_by_port_5435: collision, a later colliding database, collision with the main store, same database twice, and no collision. Without the fix, 3 of 5 fail.
  • PostgresqlTests.agent_uri_carries_the_port_when_asked_5435 and SqlServerTests.agent_uri_carries_the_port_when_asked_5435 run offline against the real stores.
  • Locally green: CoreTests Persistence + Runtime.Agents (760), PostgresqlTests MultiTenancy (29), MartenTests MultiTenancy (79), PersistenceTests ModularMonoliths (28).
  • Rig leg 8 smoke (seed 80): before, 820 delayed messages lost. After, CLEAN.

🤖 Generated with Claude Code

https://claude.ai/code/session_013nKJrqsJw3j23Xd46q37ob

…port

A relational store's agent Uri is wolverinedb://{engine}/{host}/{database}/{schema}, without the port.
Two tenant databases with the same name on the same host but different ports shared one Uri, so only
the first got a durability agent. The second database's scheduled messages were never released, and
its dead nodes' work was never recovered. Found by the durability chaos rig, leg 8 (#5414).

When MessageStoreCollection finds two such databases, each one that is not the main store switches
to a Uri that carries its port. A Uri that collided once is remembered, so a database discovered
later is qualified too, whatever order the nodes discover them in. Configurations without the
collision keep the Uris they have.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nKJrqsJw3j23Xd46q37ob
@jeremydmiller jeremydmiller added this to the 6.63 milestone Nov 7, 2026
@jeremydmiller
jeremydmiller merged commit 084c9eb into main Nov 7, 2026
This was referenced Nov 9, 2026
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.

Two tenant databases with one name on one host but different ports share a durability agent, so the second one's scheduled messages never run

1 participant