diff --git a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java index 26e6101e02..b1e571510d 100644 --- a/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java +++ b/google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java @@ -42,10 +42,12 @@ import com.google.api.gax.rpc.StatusCode; import com.google.api.gax.rpc.StreamController; import com.google.api.gax.rpc.TransportChannelProvider; +import com.google.api.gax.rpc.UnavailableException; import com.google.api.gax.rpc.WatchdogProvider; import com.google.api.pathtemplate.PathTemplate; import com.google.cloud.RetryHelper; import com.google.cloud.grpc.GrpcTransportOptions; +import com.google.cloud.spanner.ErrorCode; import com.google.cloud.spanner.SpannerException; import com.google.cloud.spanner.SpannerExceptionFactory; import com.google.cloud.spanner.SpannerOptions; @@ -55,6 +57,7 @@ import com.google.cloud.spanner.admin.database.v1.stub.GrpcDatabaseAdminStub; import com.google.cloud.spanner.admin.instance.v1.stub.GrpcInstanceAdminStub; import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStub; +import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings; import com.google.cloud.spanner.spi.v1.SpannerRpc.Option; import com.google.cloud.spanner.v1.stub.GrpcSpannerStub; import com.google.cloud.spanner.v1.stub.SpannerStub; @@ -136,6 +139,7 @@ import com.google.spanner.v1.Transaction; import io.grpc.CallCredentials; import io.grpc.Context; +import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; @@ -401,11 +405,57 @@ public GapicSpannerRpc(final SpannerOptions options) { .setStreamWatchdogProvider(watchdogProvider) .build(); this.databaseAdminStub = GrpcDatabaseAdminStub.create(this.databaseAdminStubSettings); + + // Check whether the SPANNER_EMULATOR_HOST env var has been set, and if so, if the emulator is + // actually running. + checkEmulatorConnection(options, channelProvider, credentialsProvider); } catch (Exception e) { throw newSpannerException(e); } } + private static void checkEmulatorConnection( + SpannerOptions options, + TransportChannelProvider channelProvider, + CredentialsProvider credentialsProvider) + throws IOException { + final String emulatorHost = System.getenv("SPANNER_EMULATOR_HOST"); + // Only do the check if the emulator environment variable has been set to localhost. + if (options.getChannelProvider() == null + && emulatorHost != null + && options.getHost() != null + && options.getHost().startsWith("http://localhost") + && options.getHost().endsWith(emulatorHost)) { + // Do a quick check to see if the emulator is actually running. + try { + InstanceAdminStubSettings.Builder testEmulatorSettings = + options + .getInstanceAdminStubSettings() + .toBuilder() + .setTransportChannelProvider(channelProvider) + .setCredentialsProvider(credentialsProvider); + testEmulatorSettings + .listInstanceConfigsSettings() + .setSimpleTimeoutNoRetries(Duration.ofSeconds(10L)); + try (GrpcInstanceAdminStub stub = + GrpcInstanceAdminStub.create(testEmulatorSettings.build())) { + stub.listInstanceConfigsCallable() + .call( + ListInstanceConfigsRequest.newBuilder() + .setParent(String.format("projects/%s", options.getProjectId())) + .build()); + } + } catch (UnavailableException e) { + throw SpannerExceptionFactory.newSpannerException( + ErrorCode.UNAVAILABLE, + String.format( + "The environment variable SPANNER_EMULATOR_HOST has been set to %s, but no running emulator could be found at that address.\n" + + "Did you forget to start the emulator, or to unset the environment variable?", + emulatorHost)); + } + } + } + private static final class OperationFutureRetryAlgorithm implements ResultRetryAlgorithm> { private static final ImmutableList RETRYABLE_CODES =