Skip to content

Commit a160c95

Browse files
feat: TPC support
1 parent 5e26596 commit a160c95

File tree

10 files changed

+172
-23
lines changed

10 files changed

+172
-23
lines changed

google-cloud-spanner/clirr-ignored-differences.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,4 +1043,16 @@
10431043
<className>com/google/cloud/spanner/connection/Connection</className>
10441044
<method>com.google.spanner.v1.TransactionOptions$ReadWrite$ReadLockMode getReadLockMode()</method>
10451045
</difference>
1046+
1047+
<!-- Removed getMonitoringHost() which was added as part of TPU -->
1048+
<difference>
1049+
<differenceType>7002</differenceType>
1050+
<className>com/google/cloud/spanner/SpannerOptions$SpannerEnvironment</className>
1051+
<method>java.lang.String getMonitoringHost()</method>
1052+
</difference>
1053+
<difference>
1054+
<differenceType>7002</differenceType>
1055+
<className>com/google/cloud/spanner/SpannerOptions$Builder</className>
1056+
<method>com.google.cloud.spanner.SpannerOptions$Builder setMonitoringHost(java.lang.String)</method>
1057+
</difference>
10461058
</differences>

google-cloud-spanner/src/main/java/com/google/cloud/spanner/BuiltInMetricsProvider.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,16 @@ final class BuiltInMetricsProvider {
6868
private BuiltInMetricsProvider() {}
6969

7070
OpenTelemetry getOrCreateOpenTelemetry(
71-
String projectId, @Nullable Credentials credentials, @Nullable String monitoringHost) {
71+
String projectId,
72+
@Nullable Credentials credentials,
73+
@Nullable String monitoringHost,
74+
String universeDomain) {
7275
try {
7376
if (this.openTelemetry == null) {
7477
SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
7578
BuiltInMetricsView.registerBuiltinMetrics(
76-
SpannerCloudMonitoringExporter.create(projectId, credentials, monitoringHost),
79+
SpannerCloudMonitoringExporter.create(
80+
projectId, credentials, monitoringHost, universeDomain),
7781
sdkMeterProviderBuilder);
7882
sdkMeterProviderBuilder.setResource(Resource.create(createResourceAttributes(projectId)));
7983
SdkMeterProvider sdkMeterProvider = sdkMeterProviderBuilder.build();
@@ -95,10 +99,13 @@ void enableGrpcMetrics(
9599
InstantiatingGrpcChannelProvider.Builder channelProviderBuilder,
96100
String projectId,
97101
@Nullable Credentials credentials,
98-
@Nullable String monitoringHost) {
102+
@Nullable String monitoringHost,
103+
String universeDomain) {
99104
GrpcOpenTelemetry grpcOpenTelemetry =
100105
GrpcOpenTelemetry.newBuilder()
101-
.sdk(this.getOrCreateOpenTelemetry(projectId, credentials, monitoringHost))
106+
.sdk(
107+
this.getOrCreateOpenTelemetry(
108+
projectId, credentials, monitoringHost, universeDomain))
102109
.enableMetrics(BuiltInMetricsConstant.GRPC_METRICS_TO_ENABLE)
103110
// Disable gRPCs default metrics as they are not needed for Spanner.
104111
.disableMetrics(BuiltInMetricsConstant.GRPC_METRICS_ENABLED_BY_DEFAULT)

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerCloudMonitoringExporter.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.cloud.monitoring.v3.MetricServiceClient;
2828
import com.google.cloud.monitoring.v3.MetricServiceSettings;
2929
import com.google.common.annotations.VisibleForTesting;
30+
import com.google.common.base.Strings;
3031
import com.google.common.collect.Iterables;
3132
import com.google.common.util.concurrent.MoreExecutors;
3233
import com.google.monitoring.v3.CreateTimeSeriesRequest;
@@ -71,7 +72,10 @@ class SpannerCloudMonitoringExporter implements MetricExporter {
7172
private final String spannerProjectId;
7273

7374
static SpannerCloudMonitoringExporter create(
74-
String projectId, @Nullable Credentials credentials, @Nullable String monitoringHost)
75+
String projectId,
76+
@Nullable Credentials credentials,
77+
@Nullable String monitoringHost,
78+
String universeDomain)
7579
throws IOException {
7680
MetricServiceSettings.Builder settingsBuilder = MetricServiceSettings.newBuilder();
7781
CredentialsProvider credentialsProvider;
@@ -84,6 +88,9 @@ static SpannerCloudMonitoringExporter create(
8488
if (monitoringHost != null) {
8589
settingsBuilder.setEndpoint(monitoringHost);
8690
}
91+
if (Strings.isNullOrEmpty(universeDomain)) {
92+
settingsBuilder.setUniverseDomain(universeDomain);
93+
}
8794

8895
Duration timeout = Duration.ofMinutes(1);
8996
// TODO: createServiceTimeSeries needs special handling if the request failed. Leaving

google-cloud-spanner/src/main/java/com/google/cloud/spanner/SpannerOptions.java

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,8 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
120120
private static final String PG_ADAPTER_CLIENT_LIB_TOKEN = "pg-adapter";
121121

122122
private static final String API_SHORT_NAME = "Spanner";
123-
private static final String DEFAULT_HOST = "https://spanner.googleapis.com";
123+
private static final String SPANNER_SERVICE_NAME = "spanner";
124+
private static final String GOOGLE_DEFAULT_UNIVERSE = "googleapis.com";
124125
private static final String EXPERIMENTAL_HOST_PROJECT_ID = "default";
125126

126127
private static final ImmutableSet<String> SCOPES =
@@ -780,9 +781,18 @@ protected SpannerOptions(Builder builder) {
780781
databaseRole = builder.databaseRole;
781782
sessionLabels = builder.sessionLabels;
782783
try {
783-
spannerStubSettings = builder.spannerStubSettingsBuilder.build();
784-
instanceAdminStubSettings = builder.instanceAdminStubSettingsBuilder.build();
785-
databaseAdminStubSettings = builder.databaseAdminStubSettingsBuilder.build();
784+
spannerStubSettings =
785+
builder.spannerStubSettingsBuilder.setUniverseDomain(getResolvedUniverseDomain()).build();
786+
instanceAdminStubSettings =
787+
builder
788+
.instanceAdminStubSettingsBuilder
789+
.setUniverseDomain(getResolvedUniverseDomain())
790+
.build();
791+
databaseAdminStubSettings =
792+
builder
793+
.databaseAdminStubSettingsBuilder
794+
.setUniverseDomain(getResolvedUniverseDomain())
795+
.build();
786796
} catch (IOException e) {
787797
throw SpannerExceptionFactory.newSpannerException(e);
788798
}
@@ -824,6 +834,11 @@ protected SpannerOptions(Builder builder) {
824834
defaultTransactionOptions = builder.defaultTransactionOptions;
825835
}
826836

837+
private String getResolvedUniverseDomain() {
838+
String universeDomain = getUniverseDomain();
839+
return Strings.isNullOrEmpty(universeDomain) ? GOOGLE_DEFAULT_UNIVERSE : universeDomain;
840+
}
841+
827842
/**
828843
* The environment to read configuration values from. The default implementation uses environment
829844
* variables.
@@ -2035,7 +2050,11 @@ public ApiTracerFactory getApiTracerFactory() {
20352050
public void enablegRPCMetrics(InstantiatingGrpcChannelProvider.Builder channelProviderBuilder) {
20362051
if (SpannerOptions.environment.isEnableGRPCBuiltInMetrics()) {
20372052
this.builtInMetricsProvider.enableGrpcMetrics(
2038-
channelProviderBuilder, this.getProjectId(), getCredentials(), this.monitoringHost);
2053+
channelProviderBuilder,
2054+
this.getProjectId(),
2055+
getCredentials(),
2056+
this.monitoringHost,
2057+
getUniverseDomain());
20392058
}
20402059
}
20412060

@@ -2081,7 +2100,7 @@ private ApiTracerFactory getDefaultApiTracerFactory() {
20812100
private ApiTracerFactory createMetricsApiTracerFactory() {
20822101
OpenTelemetry openTelemetry =
20832102
this.builtInMetricsProvider.getOrCreateOpenTelemetry(
2084-
this.getProjectId(), getCredentials(), this.monitoringHost);
2103+
this.getProjectId(), getCredentials(), this.monitoringHost, getUniverseDomain());
20852104

20862105
return openTelemetry != null
20872106
? new BuiltInMetricsTracerFactory(
@@ -2181,7 +2200,11 @@ public static GrpcTransportOptions getDefaultGrpcTransportOptions() {
21812200

21822201
@Override
21832202
protected String getDefaultHost() {
2184-
return DEFAULT_HOST;
2203+
String universeDomain = getUniverseDomain();
2204+
if (Strings.isNullOrEmpty(universeDomain)) {
2205+
universeDomain = GOOGLE_DEFAULT_UNIVERSE;
2206+
}
2207+
return String.format("https://%s.%s", SPANNER_SERVICE_NAME, universeDomain);
21852208
}
21862209

21872210
private static class SpannerDefaults implements ServiceDefaults<Spanner, SpannerOptions> {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionOptions.java

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import static com.google.cloud.spanner.connection.ConnectionProperties.TRACING_PREFIX;
5050
import static com.google.cloud.spanner.connection.ConnectionProperties.TRACK_CONNECTION_LEAKS;
5151
import static com.google.cloud.spanner.connection.ConnectionProperties.TRACK_SESSION_LEAKS;
52+
import static com.google.cloud.spanner.connection.ConnectionProperties.UNIVERSE_DOMAIN;
5253
import static com.google.cloud.spanner.connection.ConnectionProperties.USER_AGENT;
5354
import static com.google.cloud.spanner.connection.ConnectionProperties.USE_AUTO_SAVEPOINTS_FOR_EMULATOR;
5455
import static com.google.cloud.spanner.connection.ConnectionProperties.USE_PLAIN_TEXT;
@@ -769,16 +770,14 @@ static String determineHost(
769770
boolean autoConfigEmulator,
770771
boolean usePlainText,
771772
Map<String, String> environment) {
772-
String host;
773+
String host = null;
773774
if (Objects.equals(endpoint, DEFAULT_ENDPOINT) && matcher.group(Builder.HOST_GROUP) == null) {
774775
if (autoConfigEmulator) {
775776
if (Strings.isNullOrEmpty(environment.get(SPANNER_EMULATOR_HOST_ENV_VAR))) {
776777
return DEFAULT_EMULATOR_HOST;
777778
} else {
778779
return PLAIN_TEXT_PROTOCOL + "//" + environment.get(SPANNER_EMULATOR_HOST_ENV_VAR);
779780
}
780-
} else {
781-
return DEFAULT_HOST;
782781
}
783782
} else if (!Objects.equals(endpoint, DEFAULT_ENDPOINT)) {
784783
// Add '//' at the start of the endpoint to conform to the standard URL specification.
@@ -792,6 +791,9 @@ static String determineHost(
792791
host = String.format("%s:15000", host);
793792
}
794793
}
794+
if (host == null) {
795+
return null;
796+
}
795797
if (usePlainText) {
796798
return PLAIN_TEXT_PROTOCOL + host;
797799
}
@@ -1086,6 +1088,10 @@ Boolean isEnableDirectAccess() {
10861088
return getInitialConnectionPropertyValue(ENABLE_DIRECT_ACCESS);
10871089
}
10881090

1091+
String getUniverseDomain() {
1092+
return getInitialConnectionPropertyValue(UNIVERSE_DOMAIN);
1093+
}
1094+
10891095
String getClientCertificate() {
10901096
return getInitialConnectionPropertyValue(CLIENT_CERTIFICATE);
10911097
}

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/ConnectionProperties.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,14 @@ public class ConnectionProperties {
200200
BOOLEANS,
201201
BooleanConverter.INSTANCE,
202202
Context.STARTUP);
203+
static final ConnectionProperty<String> UNIVERSE_DOMAIN =
204+
create(
205+
"universeDomain",
206+
"Configure the connection to try to connect to Spanner using "
207+
+ "a different partner Google Universe than GDU (googleapis.com).",
208+
"googleapis.com",
209+
StringValueConverter.INSTANCE,
210+
Context.STARTUP);
203211
static final ConnectionProperty<Boolean> USE_AUTO_SAVEPOINTS_FOR_EMULATOR =
204212
create(
205213
"useAutoSavepointsForEmulator",

google-cloud-spanner/src/main/java/com/google/cloud/spanner/connection/SpannerPool.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ static class SpannerPoolKey {
165165
private final String clientCertificateKey;
166166
private final boolean isExperimentalHost;
167167
private final Boolean enableDirectAccess;
168+
private final String universeDomain;
168169

169170
@VisibleForTesting
170171
static SpannerPoolKey of(ConnectionOptions options) {
@@ -200,6 +201,7 @@ private SpannerPoolKey(ConnectionOptions options) throws IOException {
200201
this.clientCertificateKey = options.getClientCertificateKey();
201202
this.isExperimentalHost = options.isExperimentalHost();
202203
this.enableDirectAccess = options.isEnableDirectAccess();
204+
this.universeDomain = options.getUniverseDomain();
203205
}
204206

205207
@Override
@@ -226,7 +228,8 @@ public boolean equals(Object o) {
226228
&& Objects.equals(this.clientCertificate, other.clientCertificate)
227229
&& Objects.equals(this.clientCertificateKey, other.clientCertificateKey)
228230
&& Objects.equals(this.isExperimentalHost, other.isExperimentalHost)
229-
&& Objects.equals(this.enableDirectAccess, other.enableDirectAccess);
231+
&& Objects.equals(this.enableDirectAccess, other.enableDirectAccess)
232+
&& Objects.equals(this.universeDomain, other.universeDomain);
230233
}
231234

232235
@Override
@@ -249,7 +252,8 @@ public int hashCode() {
249252
this.clientCertificate,
250253
this.clientCertificateKey,
251254
this.isExperimentalHost,
252-
this.enableDirectAccess);
255+
this.enableDirectAccess,
256+
this.universeDomain);
253257
}
254258
}
255259

@@ -419,6 +423,9 @@ Spanner createSpanner(SpannerPoolKey key, ConnectionOptions options) {
419423
if (key.enableDirectAccess != null) {
420424
builder.setEnableDirectAccess(key.enableDirectAccess);
421425
}
426+
if (key.universeDomain != null) {
427+
builder.setUniverseDomain(key.universeDomain);
428+
}
422429
if (options.getConfigurator() != null) {
423430
options.getConfigurator().configure(builder);
424431
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerCloudMonitoringExporterTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ public void testExportingHistogramDataWithExemplars() {
454454
@Test
455455
public void getAggregationTemporality() throws IOException {
456456
SpannerCloudMonitoringExporter actualExporter =
457-
SpannerCloudMonitoringExporter.create(projectId, null, null);
457+
SpannerCloudMonitoringExporter.create(projectId, null, null, null);
458458
assertThat(actualExporter.getAggregationTemporality(InstrumentType.COUNTER))
459459
.isEqualTo(AggregationTemporality.CUMULATIVE);
460460
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/SpannerOptionsTest.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -761,11 +761,11 @@ public void testMonitoringHost() {
761761
String metricsEndpoint = "test-endpoint:443";
762762
assertNull(SpannerOptions.newBuilder().setProjectId("p").build().getMonitoringHost());
763763
assertThat(
764-
SpannerOptions.newBuilder()
765-
.setProjectId("p")
766-
.setMonitoringHost(metricsEndpoint)
767-
.build()
768-
.getMonitoringHost())
764+
SpannerOptions.newBuilder()
765+
.setProjectId("p")
766+
.setMonitoringHost(metricsEndpoint)
767+
.build()
768+
.getMonitoringHost())
769769
.isEqualTo(metricsEndpoint);
770770
}
771771

google-cloud-spanner/src/test/java/com/google/cloud/spanner/connection/ConnectionOptionsTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import static org.junit.Assert.assertNull;
2727
import static org.junit.Assert.assertThrows;
2828
import static org.junit.Assert.assertTrue;
29+
import static org.mockito.Mockito.mock;
2930

3031
import com.google.api.gax.core.CredentialsProvider;
3132
import com.google.api.gax.core.NoCredentialsProvider;
@@ -36,6 +37,7 @@
3637
import com.google.auth.oauth2.ServiceAccountCredentials;
3738
import com.google.cloud.NoCredentials;
3839
import com.google.cloud.spanner.ErrorCode;
40+
import com.google.cloud.spanner.Spanner;
3941
import com.google.cloud.spanner.SpannerException;
4042
import com.google.cloud.spanner.SpannerOptions;
4143
import com.google.common.collect.ImmutableMap;
@@ -47,6 +49,7 @@
4749
import java.util.Arrays;
4850
import java.util.Collections;
4951
import java.util.Objects;
52+
import java.util.concurrent.atomic.AtomicBoolean;
5053
import java.util.regex.Matcher;
5154
import org.junit.Test;
5255
import org.junit.function.ThrowingRunnable;
@@ -1329,4 +1332,80 @@ public void testEnableDirectAccess() {
13291332
"spanner://localhost:15000/projects/default/instances/default/databases/singers-db;usePlainText=true;enableDirectAccess=true");
13301333
assertTrue(builderWithDirectPathParam.build().isEnableDirectAccess());
13311334
}
1335+
1336+
@Test
1337+
public void testUniverseDomain() {
1338+
ConnectionImpl connection = mock(ConnectionImpl.class);
1339+
1340+
// No universeDomain
1341+
AtomicBoolean executedConfigurator = new AtomicBoolean(false);
1342+
ConnectionOptions optionsWithNoUniverseDomainParam =
1343+
ConnectionOptions.newBuilder()
1344+
.setUri("cloudspanner:/projects/default/instances/default/databases/singers-db")
1345+
.setConfigurator(
1346+
optionsBuilder -> {
1347+
executedConfigurator.set(true);
1348+
SpannerOptions spannerOptions = optionsBuilder.build();
1349+
assertEquals("googleapis.com", spannerOptions.getUniverseDomain());
1350+
assertEquals("https://spanner.googleapis.com", spannerOptions.getHost());
1351+
})
1352+
.build();
1353+
Spanner spanner = SpannerPool.INSTANCE.getSpanner(optionsWithNoUniverseDomainParam, connection);
1354+
spanner.close();
1355+
assertTrue(executedConfigurator.get());
1356+
1357+
executedConfigurator.set(false);
1358+
ConnectionOptions optionsWithUniverseDomainParam =
1359+
ConnectionOptions.newBuilder()
1360+
.setUri(
1361+
"cloudspanner:/projects/default/instances/default/databases/singers-db;universeDomain=abc.goog")
1362+
.setConfigurator(
1363+
optionsBuilder -> {
1364+
executedConfigurator.set(true);
1365+
SpannerOptions spannerOptions = optionsBuilder.build();
1366+
assertEquals("abc.goog", spannerOptions.getUniverseDomain());
1367+
assertEquals("https://spanner.abc.goog", spannerOptions.getHost());
1368+
})
1369+
.build();
1370+
spanner = SpannerPool.INSTANCE.getSpanner(optionsWithUniverseDomainParam, connection);
1371+
spanner.close();
1372+
assertTrue(executedConfigurator.get());
1373+
1374+
executedConfigurator.set(false);
1375+
ConnectionOptions optionsWithHostAndUniverseDomainParam =
1376+
ConnectionOptions.newBuilder()
1377+
.setUri(
1378+
"cloudspanner://spanner.abc.goog/projects/default/instances/default/databases/singers-db;universeDomain=abc.goog")
1379+
.setConfigurator(
1380+
optionsBuilder -> {
1381+
executedConfigurator.set(true);
1382+
SpannerOptions spannerOptions = optionsBuilder.build();
1383+
assertEquals("abc.goog", spannerOptions.getUniverseDomain());
1384+
assertEquals("https://spanner.abc.goog", spannerOptions.getHost());
1385+
})
1386+
.build();
1387+
spanner = SpannerPool.INSTANCE.getSpanner(optionsWithHostAndUniverseDomainParam, connection);
1388+
spanner.close();
1389+
assertTrue(executedConfigurator.get());
1390+
1391+
executedConfigurator.set(false);
1392+
ConnectionOptions optionsWithLocalHostAndUniverseDomainParam =
1393+
ConnectionOptions.newBuilder()
1394+
.setUri(
1395+
"cloudspanner://localhost:15000/projects/default/instances/default/databases/singers-db;usePlainText=true;universeDomain=abc.goog")
1396+
.setConfigurator(
1397+
optionsBuilder -> {
1398+
executedConfigurator.set(true);
1399+
SpannerOptions spannerOptions = optionsBuilder.build();
1400+
assertEquals("abc.goog", spannerOptions.getUniverseDomain());
1401+
assertEquals("http://localhost:15000", spannerOptions.getHost());
1402+
})
1403+
.build();
1404+
spanner =
1405+
SpannerPool.INSTANCE.getSpanner(optionsWithLocalHostAndUniverseDomainParam, connection);
1406+
spanner.close();
1407+
assertTrue(executedConfigurator.get());
1408+
1409+
connection.close();
1410+
}
13321411
}

0 commit comments

Comments
 (0)