Skip to content

Commit 9b87f57

Browse files
mw-ship-itNeenu1995
authored andcommitted
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 6518bba commit 9b87f57

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

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/spi/v1/GrpcDatastoreRpc.java

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,22 @@ public GrpcDatastoreRpc(DatastoreOptions datastoreOptions) throws IOException {
7575
? getClientContextForEmulator(datastoreOptions)
7676
: getClientContext(datastoreOptions);
7777

78-
/* For grpc transport options, configure default gRPC Connection pool with minChannelCount = 1 */
79-
DatastoreStubSettings.Builder builder =
78+
/* For non-emulator, configure default gRPC Connection pool with minChannelCount = 1 */
79+
DatastoreStubSettings.Builder datastoreStubSettingsBuilder =
8080
DatastoreStubSettings.newBuilder(clientContext)
81-
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions))
82-
.setTransportChannelProvider(
83-
DatastoreSettings.defaultGrpcTransportProviderBuilder()
84-
.setChannelPoolSettings(
85-
ChannelPoolSettings.builder()
86-
.setInitialChannelCount(DatastoreOptions.INIT_CHANNEL_COUNT)
87-
.setMinChannelCount(DatastoreOptions.MIN_CHANNEL_COUNT)
88-
.build())
89-
.build());
90-
91-
datastoreStub = GrpcDatastoreStub.create(builder.build());
81+
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions));
82+
if (!isEmulator(datastoreOptions)) {
83+
datastoreStubSettingsBuilder.setTransportChannelProvider(
84+
DatastoreSettings.defaultGrpcTransportProviderBuilder()
85+
.setChannelPoolSettings(
86+
ChannelPoolSettings.builder()
87+
.setInitialChannelCount(DatastoreOptions.INIT_CHANNEL_COUNT)
88+
.setMinChannelCount(DatastoreOptions.MIN_CHANNEL_COUNT)
89+
.build())
90+
.build());
91+
}
92+
93+
datastoreStub = GrpcDatastoreStub.create(datastoreStubSettingsBuilder.build());
9294
} catch (IOException e) {
9395
throw new IOException(e);
9496
}

0 commit comments

Comments
 (0)