Skip to content

Commit 53bcb3e

Browse files
arpan14olavloitegcf-owl-bot[bot]
authored
feat: support public methods to use autogenerated admin clients. (#2878)
* fix: prevent illegal negative timeout values into thread sleep() method while retrying exceptions in unit tests. * For details on issue see - #2206 * Fixing lint issues. * feat: support emulator with autogenerated admin clients. * chore: modifying public interfaces and adding an integration test. * chore: add deprecated annotations and add docs. * chore: making interface methods default. * chore: add clirr ignore checks for public methods. * Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Spanner.java Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Spanner.java Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Spanner.java Co-authored-by: Knut Olav Løite <koloite@gmail.com> * Update google-cloud-spanner/src/main/java/com/google/cloud/spanner/Spanner.java Co-authored-by: Knut Olav Løite <koloite@gmail.com> * chore: address PR comments. * chore: decouple client stub objects and rely on common stub settings object. * chore: remove unused map objects. * chore: fix broken unit tests. * chore: handle case when client is terminated/shutdown * chore: change to create methods and avoid stale instances. * Revert "chore: fix broken unit tests." This reverts commit 07c9cd1. * test: add more unit tests. * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Knut Olav Løite <koloite@gmail.com> Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 0e96d1f commit 53bcb3e

File tree

9 files changed

+541
-15
lines changed

9 files changed

+541
-15
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ If you are using Maven without the BOM, add this to your dependencies:
5050
If you are using Gradle 5.x or later, add this to your dependencies:
5151

5252
```Groovy
53-
implementation platform('com.google.cloud:libraries-bom:26.31.0')
53+
implementation platform('com.google.cloud:libraries-bom:26.32.0')
5454
5555
implementation 'com.google.cloud:google-cloud-spanner'
5656
```

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

+25-2
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,30 @@
539539
<className>com/google/cloud/spanner/Dialect</className>
540540
<method>java.lang.String getDefaultSchema()</method>
541541
</difference>
542+
<!-- Exposing auto-generated admin/instance clients for admin operations -->
543+
<difference>
544+
<differenceType>7012</differenceType>
545+
<className>com/google/cloud/spanner/Spanner</className>
546+
<method>com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient createDatabaseAdminClient()</method>
547+
</difference>
548+
<difference>
549+
<differenceType>7012</differenceType>
550+
<className>com/google/cloud/spanner/Spanner</className>
551+
<method>com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient createInstanceAdminClient()</method>
552+
</difference>
553+
554+
<!-- (Internal change, use stub objects for generating auto-generated client object) -->
555+
<difference>
556+
<differenceType>7012</differenceType>
557+
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
558+
<method>com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings getDatabaseAdminStubSettings()</method>
559+
</difference>
560+
<difference>
561+
<differenceType>7012</differenceType>
562+
<className>com/google/cloud/spanner/spi/v1/SpannerRpc</className>
563+
<method>com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings getInstanceAdminStubSettings()</method>
564+
</difference>
565+
542566
<difference>
543567
<differenceType>7005</differenceType>
544568
<className>com/google/cloud/spanner/PartitionedDmlTransaction</className>
@@ -556,6 +580,5 @@
556580
<differenceType>7012</differenceType>
557581
<className>com/google/cloud/spanner/connection/Connection</className>
558582
<method>void setDirectedRead(com.google.spanner.v1.DirectedReadOptions)</method>
559-
</difference>
560-
583+
</difference>
561584
</differences>

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

+57-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,12 @@
2626
* quota.
2727
*/
2828
public interface Spanner extends Service<SpannerOptions>, AutoCloseable {
29-
/** Returns a {@code DatabaseAdminClient} to do admin operations on Cloud Spanner databases. */
29+
30+
/**
31+
* Returns a {@code DatabaseAdminClient} to execute admin operations on Cloud Spanner databases.
32+
*
33+
* @return {@code DatabaseAdminClient}
34+
*/
3035
/*
3136
* <!--SNIPPET get_dbadmin_client-->
3237
* <pre>{@code
@@ -38,7 +43,34 @@ public interface Spanner extends Service<SpannerOptions>, AutoCloseable {
3843
*/
3944
DatabaseAdminClient getDatabaseAdminClient();
4045

41-
/** Returns an {@code InstanceAdminClient} to do admin operations on Cloud Spanner instances. */
46+
/**
47+
* Returns a {@link com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient} to execute
48+
* admin operations on Cloud Spanner databases. This method always creates a new instance of
49+
* {@link com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient} which is an {@link
50+
* AutoCloseable} resource. For optimising the number of clients, caller may choose to cache the
51+
* clients instead of repeatedly invoking this method and creating new instances.
52+
*
53+
* @return {@link com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient}
54+
*/
55+
/*
56+
* <!--SNIPPET get_dbadmin_client-->
57+
* <pre>{@code
58+
* SpannerOptions options = SpannerOptions.newBuilder().build();
59+
* Spanner spanner = options.getService();
60+
* DatabaseAdminClient dbAdminClient = spanner.createDatabaseAdminClient();
61+
* }</pre>
62+
* <!--SNIPPET get_dbadmin_client-->
63+
*/
64+
default com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient
65+
createDatabaseAdminClient() {
66+
throw new UnsupportedOperationException("Not implemented");
67+
}
68+
69+
/**
70+
* Returns an {@code InstanceAdminClient} to execute admin operations on Cloud Spanner instances.
71+
*
72+
* @return {@code InstanceAdminClient}
73+
*/
4274
/*
4375
* <!--SNIPPET get_instance_admin_client-->
4476
* <pre>{@code
@@ -50,6 +82,29 @@ public interface Spanner extends Service<SpannerOptions>, AutoCloseable {
5082
*/
5183
InstanceAdminClient getInstanceAdminClient();
5284

85+
/**
86+
* Returns a {@link com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient} to execute
87+
* admin operations on Cloud Spanner databases. This method always creates a new instance of
88+
* {@link com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient} which is an {@link
89+
* AutoCloseable} resource. For optimising the number of clients, caller may choose to cache the
90+
* clients instead of repeatedly invoking this method and creating new instances.
91+
*
92+
* @return {@link com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient}
93+
*/
94+
/*
95+
* <!--SNIPPET get_instance_admin_client-->
96+
* <pre>{@code
97+
* SpannerOptions options = SpannerOptions.newBuilder().build();
98+
* Spanner spanner = options.getService();
99+
* InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient();
100+
* }</pre>
101+
* <!--SNIPPET get_instance_admin_client-->
102+
*/
103+
default com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient
104+
createInstanceAdminClient() {
105+
throw new UnsupportedOperationException("Not implemented");
106+
}
107+
53108
/**
54109
* Returns a {@code DatabaseClient} for the given database. It uses a pool of sessions to talk to
55110
* the database.

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

+29
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.google.cloud.grpc.GrpcTransportOptions;
2626
import com.google.cloud.spanner.SessionClient.SessionId;
2727
import com.google.cloud.spanner.SpannerOptions.CloseableExecutorProvider;
28+
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings;
29+
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings;
2830
import com.google.cloud.spanner.spi.v1.GapicSpannerRpc;
2931
import com.google.cloud.spanner.spi.v1.SpannerRpc;
3032
import com.google.cloud.spanner.spi.v1.SpannerRpc.Paginated;
@@ -40,6 +42,7 @@
4042
import io.opencensus.trace.Tracing;
4143
import io.opentelemetry.api.common.Attributes;
4244
import io.opentelemetry.api.common.AttributesBuilder;
45+
import java.io.IOException;
4346
import java.util.ArrayList;
4447
import java.util.HashMap;
4548
import java.util.List;
@@ -207,11 +210,37 @@ public DatabaseAdminClient getDatabaseAdminClient() {
207210
return dbAdminClient;
208211
}
209212

213+
@Override
214+
public com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient
215+
createDatabaseAdminClient() {
216+
try {
217+
final DatabaseAdminStubSettings settings =
218+
Preconditions.checkNotNull(gapicRpc.getDatabaseAdminStubSettings());
219+
return com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient.create(
220+
settings.createStub());
221+
} catch (IOException ex) {
222+
throw SpannerExceptionFactory.newSpannerException(ex);
223+
}
224+
}
225+
210226
@Override
211227
public InstanceAdminClient getInstanceAdminClient() {
212228
return instanceClient;
213229
}
214230

231+
@Override
232+
public com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient
233+
createInstanceAdminClient() {
234+
try {
235+
final InstanceAdminStubSettings settings =
236+
Preconditions.checkNotNull(gapicRpc.getInstanceAdminStubSettings());
237+
return com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient.create(
238+
settings.createStub());
239+
} catch (IOException ex) {
240+
throw SpannerExceptionFactory.newSpannerException(ex);
241+
}
242+
}
243+
215244
@Override
216245
public DatabaseClient getDatabaseClient(DatabaseId db) {
217246
synchronized (this) {

google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpc.java

+21-10
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ public class GapicSpannerRpc implements SpannerRpc {
243243
private final Set<Code> readRetryableCodes;
244244
private final SpannerStub partitionedDmlStub;
245245
private final RetrySettings partitionedDmlRetrySettings;
246+
private final InstanceAdminStubSettings instanceAdminStubSettings;
246247
private final InstanceAdminStub instanceAdminStub;
247248
private final DatabaseAdminStubSettings databaseAdminStubSettings;
248249
private final DatabaseAdminStub databaseAdminStub;
@@ -435,16 +436,15 @@ public GapicSpannerRpc(final SpannerOptions options) {
435436
.withCheckInterval(pdmlSettings.getStreamWatchdogCheckInterval()));
436437
}
437438
this.partitionedDmlStub = GrpcSpannerStub.create(pdmlSettings.build());
438-
439-
this.instanceAdminStub =
440-
GrpcInstanceAdminStub.create(
441-
options
442-
.getInstanceAdminStubSettings()
443-
.toBuilder()
444-
.setTransportChannelProvider(channelProvider)
445-
.setCredentialsProvider(credentialsProvider)
446-
.setStreamWatchdogProvider(watchdogProvider)
447-
.build());
439+
this.instanceAdminStubSettings =
440+
options
441+
.getInstanceAdminStubSettings()
442+
.toBuilder()
443+
.setTransportChannelProvider(channelProvider)
444+
.setCredentialsProvider(credentialsProvider)
445+
.setStreamWatchdogProvider(watchdogProvider)
446+
.build();
447+
this.instanceAdminStub = GrpcInstanceAdminStub.create(instanceAdminStubSettings);
448448

449449
this.databaseAdminStubSettings =
450450
options
@@ -510,6 +510,7 @@ public <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createUnaryCalla
510510
this.executeQueryRetryableCodes = null;
511511
this.partitionedDmlStub = null;
512512
this.databaseAdminStubSettings = null;
513+
this.instanceAdminStubSettings = null;
513514
this.spannerWatchdog = null;
514515
this.partitionedDmlRetrySettings = null;
515516
}
@@ -2004,6 +2005,16 @@ public boolean isClosed() {
20042005
return rpcIsClosed;
20052006
}
20062007

2008+
@Override
2009+
public DatabaseAdminStubSettings getDatabaseAdminStubSettings() {
2010+
return databaseAdminStubSettings;
2011+
}
2012+
2013+
@Override
2014+
public InstanceAdminStubSettings getInstanceAdminStubSettings() {
2015+
return instanceAdminStubSettings;
2016+
}
2017+
20072018
private static final class GrpcStreamingCall implements StreamingCall {
20082019
private final ApiCallContext callContext;
20092020
private final StreamController controller;

google-cloud-spanner/src/main/java/com/google/cloud/spanner/spi/v1/SpannerRpc.java

+22
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
import com.google.cloud.spanner.Restore;
2929
import com.google.cloud.spanner.SpannerException;
3030
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStub;
31+
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings;
3132
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStub;
33+
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings;
3234
import com.google.cloud.spanner.v1.stub.SpannerStubSettings;
3335
import com.google.common.collect.ImmutableList;
3436
import com.google.iam.v1.GetPolicyOptions;
@@ -500,4 +502,24 @@ TestIamPermissionsResponse testInstanceAdminIAMPermissions(
500502
void shutdown();
501503

502504
boolean isClosed();
505+
506+
/**
507+
* Getter method to obtain the auto-generated instance admin client stub settings.
508+
*
509+
* @return InstanceAdminStubSettings
510+
*/
511+
@InternalApi
512+
default InstanceAdminStubSettings getInstanceAdminStubSettings() {
513+
throw new UnsupportedOperationException("Not implemented");
514+
}
515+
516+
/**
517+
* Getter method to obtain the auto-generated database admin client stub settings.
518+
*
519+
* @return DatabaseAdminStubSettings
520+
*/
521+
@InternalApi
522+
default DatabaseAdminStubSettings getDatabaseAdminStubSettings() {
523+
throw new UnsupportedOperationException("Not implemented");
524+
}
503525
}

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

+72
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import static com.google.common.truth.Truth.assertThat;
2020
import static org.hamcrest.CoreMatchers.is;
2121
import static org.hamcrest.MatcherAssert.assertThat;
22+
import static org.junit.Assert.assertNotNull;
2223
import static org.junit.Assert.assertThrows;
2324
import static org.mockito.Mockito.when;
2425

@@ -29,9 +30,16 @@
2930
import com.google.cloud.grpc.GrpcTransportOptions;
3031
import com.google.cloud.spanner.SpannerException.DoNotConstructDirectly;
3132
import com.google.cloud.spanner.SpannerImpl.ClosedException;
33+
import com.google.cloud.spanner.admin.database.v1.DatabaseAdminClient;
34+
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStub;
35+
import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings;
36+
import com.google.cloud.spanner.admin.instance.v1.InstanceAdminClient;
37+
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStub;
38+
import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings;
3239
import com.google.cloud.spanner.spi.v1.SpannerRpc;
3340
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
3441
import io.opentelemetry.api.OpenTelemetry;
42+
import java.io.IOException;
3543
import java.io.PrintWriter;
3644
import java.io.StringWriter;
3745
import java.util.Collections;
@@ -55,6 +63,10 @@
5563
public class SpannerImplTest {
5664
@Mock private SpannerRpc rpc;
5765
@Mock private SpannerOptions spannerOptions;
66+
@Mock private DatabaseAdminStubSettings databaseAdminStubSettings;
67+
@Mock private DatabaseAdminStub databaseAdminStub;
68+
@Mock private InstanceAdminStubSettings instanceAdminStubSettings;
69+
@Mock private InstanceAdminStub instanceAdminStub;
5870
private SpannerImpl impl;
5971

6072
@Captor ArgumentCaptor<Map<SpannerRpc.Option, Object>> options;
@@ -286,6 +298,66 @@ public void testClosedException() {
286298
assertThat(sw.toString()).contains("closeSpannerAndIncludeStacktrace");
287299
}
288300

301+
@Test
302+
public void testCreateDatabaseAdminClient_whenNullAdminSettings_assertPreconditionFailure() {
303+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
304+
assertThrows(NullPointerException.class, spanner::createDatabaseAdminClient);
305+
}
306+
307+
@Test
308+
public void testCreateDatabaseAdminClient_whenMockAdminSettings_assertMethodInvocation()
309+
throws IOException {
310+
when(rpc.getDatabaseAdminStubSettings()).thenReturn(databaseAdminStubSettings);
311+
when(databaseAdminStubSettings.createStub()).thenReturn(databaseAdminStub);
312+
313+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
314+
315+
DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient();
316+
assertNotNull(databaseAdminClient);
317+
}
318+
319+
@Test(expected = SpannerException.class)
320+
public void testCreateDatabaseAdminClient_whenMockAdminSettings_assertException()
321+
throws IOException {
322+
when(rpc.getDatabaseAdminStubSettings()).thenReturn(databaseAdminStubSettings);
323+
when(databaseAdminStubSettings.createStub()).thenThrow(IOException.class);
324+
325+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
326+
327+
DatabaseAdminClient databaseAdminClient = spanner.createDatabaseAdminClient();
328+
assertNotNull(databaseAdminClient);
329+
}
330+
331+
@Test
332+
public void testCreateInstanceAdminClient_whenNullAdminSettings_assertPreconditionFailure() {
333+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
334+
assertThrows(NullPointerException.class, spanner::createInstanceAdminClient);
335+
}
336+
337+
@Test
338+
public void testCreateInstanceAdminClient_whenMockAdminSettings_assertMethodInvocation()
339+
throws IOException {
340+
when(rpc.getInstanceAdminStubSettings()).thenReturn(instanceAdminStubSettings);
341+
when(instanceAdminStubSettings.createStub()).thenReturn(instanceAdminStub);
342+
343+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
344+
345+
InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient();
346+
assertNotNull(instanceAdminClient);
347+
}
348+
349+
@Test(expected = SpannerException.class)
350+
public void testCreateInstanceAdminClient_whenMockAdminSettings_assertException()
351+
throws IOException {
352+
when(rpc.getInstanceAdminStubSettings()).thenReturn(instanceAdminStubSettings);
353+
when(instanceAdminStubSettings.createStub()).thenThrow(IOException.class);
354+
355+
Spanner spanner = new SpannerImpl(rpc, spannerOptions);
356+
357+
InstanceAdminClient instanceAdminClient = spanner.createInstanceAdminClient();
358+
assertNotNull(instanceAdminClient);
359+
}
360+
289361
private void closeSpannerAndIncludeStacktrace(Spanner spanner) {
290362
spanner.close();
291363
}

0 commit comments

Comments
 (0)