fix: honour Spanner emulator detection in outbox/inbox connections (#4162)#4225
fix: honour Spanner emulator detection in outbox/inbox connections (#4162)#4225iancooper wants to merge 2 commits into
Conversation
Root cause: SpannerConnectionProvider and SpannerUnitOfWork built a SpannerConnection from the raw connection string without opting into EmulatorDetection, so against the emulator the client fell back to Application Default Credentials and threw "Your default credentials were not found". Matching the existing SpannerConnectionHelper, they now set EmulatorDetection.EmulatorOrProduction (a no-op in real production, active only when SPANNER_EMULATOR_HOST is set). Also embeds EmulatorDetection in the Gcp test connection string (Const) so the pre-existing Spanner outbox/inbox tests reach the emulator. Fixes #4162 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Code Review — PR #4225: Honour Spanner emulator detection in outbox/inbox connectionsThorough, well-diagnosed fix. The Design / behaviour
Tests
Scope / housekeeping
SummaryCorrect, minimal, well-tested fix that resolves #4162. The only point I'd ask the author to consciously sign off on is #1 — the production providers now unconditionally impose |
| // Enable emulator detection so the connection routes to SPANNER_EMULATOR_HOST when it is set, | ||
| // and behaves as normal production otherwise. The keyword serialises into the connection string. | ||
| private readonly string _connectionString = | ||
| new SpannerConnectionStringBuilder(configuration.ConnectionString) | ||
| { | ||
| EmulatorDetection = EmulatorDetection.EmulatorOrProduction | ||
| }.ConnectionString; | ||
|
|
There was a problem hiding this comment.
Should it be done in the IAmARelationalDatabaseConfiguration?
Like
new RelationalDatabaseConfiguration(new SpannerConnectionStringBuilder("MyConnection")
{
EmulatorDetection = EmulatorDetection.EmulatorOrProduction
}.ConnectionString)
Symptom
Run against the Spanner emulator, 60 Spanner Outbox/Inbox tests failed at fixture setup with:
The gRPC client tried to load Application Default Credentials instead of routing to
SPANNER_EMULATOR_HOST. The 33Spanner/BoxProvisioning/tests passed because they already opt into emulator detection.Confirmed root cause
SpannerConnectionProviderandSpannerUnitOfWorkbuiltnew SpannerConnection(configuration.ConnectionString)without opting intoEmulatorDetection. BecauseEmulatorDetectionis opt-in and absent, the client falls back to ADC and throws. The passingBoxProvisioningtests work precisely because their productionSpannerConnectionHelpersetsEmulatorDetection.EmulatorOrProduction.Evidence (code-trace, verified against the emulator):
SpannerConnectionProvider.cs:22/30,SpannerUnitOfWork.cs:28/41), rebuilding fromconfiguration.ConnectionString— so a test-object-only fix is insufficient; the fix must reach the connection string that round-trips throughRelationalDatabaseConfiguration.SpannerConnectionStringBuilder.EmulatorDetectionserialises into.ConnectionString(Google.Cloud.Spanner.Data 5.12.0), so embedding it once propagates to every derived connection.EmulatorDetection.EmulatorOrProductionis a no-op in real production — it only routes to the emulator whenSPANNER_EMULATOR_HOSTis set.Fix
SpannerConnectionProviderandSpannerUnitOfWorknow build their connection string viaSpannerConnectionStringBuilder { EmulatorDetection = EmulatorDetection.EmulatorOrProduction }, matching the existingSpannerConnectionHelper.Const.ConnectionString) embedsEmulatorDetectionso the pre-existing Spanner outbox/inbox tests reach the emulator.SpannerConnectionProviderEmulatorTestsbuilds the production provider from a bare connection string and asserts it routes to the emulator (was red with the ADC error, now green).Verification (emulator)
--filter "FullyQualifiedName~Spanner"with collection parallelisation disabled: 94 passed / 0 failed (33 BoxProvisioning kept green — no regression; all 60 ADC failures resolved).Out of scope — tracked separately (#4224)
Under a parallel
dotnet testrun, ~28 Spanner Outbox/Inbox tests still fail withFailedPrecondition: Schema change operation rejected because a concurrent schema change operation ... is already in progress. That is a different root cause — the emulator serialises DDL while those test classes run in parallel (unlikeBoxProvisioning, which uses a shared[Collection]). It was previously masked by the ADC failure and only surfaced once emulator detection let the tests reach the DDL stage. Filed as #4224; each test passes in isolation / when serialised.Fixes #4162