Commit 4bed8fd
authored
fix(datastore): Create a plaintext gRPC transport channel when using the Emulator (#12721)
## Problem Statement
When using the `java-datastore` SDK with `GrpcTransportOptions` and
connecting to a local Datastore Emulator (e.g., via the
`DATASTORE_EMULATOR_HOST` environment variable), the client consistently
throws an `UNAUTHENTICATED: Request is missing required authentication
credential` error.
### The Root Cause
In `GrpcDatastoreRpc.java`, the SDK constructor detects the emulator
correctly and creates an unauthenticated, plaintext gRPC channel
specifically tailored for local development via
`getClientContextForEmulator(datastoreOptions)`.
However, right after generating this specialized context, the SDK uses a
builder (`DatastoreStubSettings.newBuilder`) that **unconditionally
overwrites** the transport channel provider with a default
production-oriented channel provider in order to set channel pooling
limits.
Because the emulator's custom local channel gets overwritten by this
production channel configuration, the SDK ultimately discards the
emulator settings and attempts to connect securely to the actual
production endpoint (`datastore.googleapis.com`), but without any
credentials (since it knows it's an emulator). Production immediately
rejects the call.
## The Fix
The fix converts the monolithic `DatastoreStubSettings` builder chain
into a sequential one, and conditionally applies the custom channel
pooling provider only if the environment is **not** an emulator:
```java
DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
DatastoreStubSettings.newBuilder(clientContext)
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));
if (!isEmulator(datastoreOptions)) {
datastoreStubSettingsBuilder.setTransportChannelProvider(
// ... Production connection pool settings
);
}
```
## Why this is a safe fix without side effects
1. **Zero Impact on Production Traffic:** The fix relies exclusively on
the existing, battle-tested `isEmulator()` method. If the SDK is pointed
at production, `!isEmulator` returns true, and the code path executes
the exact same transport channel override as before. Production
throughput, connection pooling, and auth behavior remain 100% untouched.
2. **Restores Original Intent:** For emulator traffic, the channel
provider override is gracefully skipped. This allows the custom,
unauthenticated local gRPC channel created specifically for the emulator
in `getClientContextForEmulator()` to survive and be used by the
`GrpcDatastoreStub`.
3. **Architecturally Safe:** It introduces no new dependencies, requires
no new variables or complex state management, and makes no changes to
the public API surface. It simply resolves an unintentional clobbering
of configuration variables at object instantiation time.1 parent 463c68e commit 4bed8fd
1 file changed
Lines changed: 15 additions & 13 deletions
File tree
- java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/spi/v1
Lines changed: 15 additions & 13 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
75 | 75 | | |
76 | 76 | | |
77 | 77 | | |
78 | | - | |
79 | | - | |
| 78 | + | |
| 79 | + | |
80 | 80 | | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
92 | 94 | | |
93 | 95 | | |
94 | 96 | | |
| |||
0 commit comments