Skip to content

Commit 74941d8

Browse files
feat!: Making Datastore implement Closeable (googleapis#1246)
* Making Datastore closeable and fixing integration test * Closing datastore in datastore test * fix lint * fix clirr config * test to verify if datastore client is getting closed after invoking close() method * Using a consistent port and reinitializing Datastore instance in @BeforeClass method * trying to fix connection reset problem * upgrading datastore emulator to fix DatastoreTest * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --------- Co-authored-by: Owl Bot <gcf-owl-bot[bot]@users.noreply.github.com>
1 parent 7048bdd commit 74941d8

File tree

13 files changed

+137
-29
lines changed

13 files changed

+137
-29
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,20 @@ 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.27.0')
53+
implementation platform('com.google.cloud:libraries-bom:26.29.0')
5454
5555
implementation 'com.google.cloud:google-cloud-datastore'
5656
```
5757
If you are using Gradle without BOM, add this to your dependencies:
5858

5959
```Groovy
60-
implementation 'com.google.cloud:google-cloud-datastore:2.17.5'
60+
implementation 'com.google.cloud:google-cloud-datastore:2.17.6'
6161
```
6262

6363
If you are using SBT, add this to your dependencies:
6464

6565
```Scala
66-
libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "2.17.5"
66+
libraryDependencies += "com.google.cloud" % "google-cloud-datastore" % "2.17.6"
6767
```
6868
<!-- {x-version-update-end} -->
6969

@@ -380,7 +380,7 @@ Java is a registered trademark of Oracle and/or its affiliates.
380380
[kokoro-badge-link-5]: http://storage.googleapis.com/cloud-devrel-public/java/badges/java-datastore/java11.html
381381
[stability-image]: https://img.shields.io/badge/stability-stable-green
382382
[maven-version-image]: https://img.shields.io/maven-central/v/com.google.cloud/google-cloud-datastore.svg
383-
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-datastore/2.17.5
383+
[maven-version-link]: https://central.sonatype.com/artifact/com.google.cloud/google-cloud-datastore/2.17.6
384384
[authentication]: https://github.com/googleapis/google-cloud-java#authentication
385385
[auth-scopes]: https://developers.google.com/identity/protocols/oauth2/scopes
386386
[predefined-iam-roles]: https://cloud.google.com/iam/docs/understanding-roles#predefined_roles

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,4 +38,25 @@
3838
<differenceType>5001</differenceType>
3939
<to>com/google/cloud/http/BaseHttpServiceException</to>
4040
</difference>
41+
<difference>
42+
<className>com/google/cloud/datastore/Datastore</className>
43+
<method>void close()</method>
44+
<differenceType>7012</differenceType>
45+
</difference>
46+
<difference>
47+
<className>com/google/cloud/datastore/spi/v1/DatastoreRpc</className>
48+
<method>void close()</method>
49+
<differenceType>7012</differenceType>
50+
</difference>
51+
<difference>
52+
<className>com/google/cloud/datastore/Datastore</className>
53+
<method>boolean isClosed()</method>
54+
<differenceType>7012</differenceType>
55+
</difference>
56+
<difference>
57+
<className>com/google/cloud/datastore/spi/v1/DatastoreRpc</className>
58+
<method>boolean isClosed()</method>
59+
<differenceType>7012</differenceType>
60+
</difference>
61+
4162
</differences>

google-cloud-datastore/src/main/java/com/google/cloud/datastore/Datastore.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
import java.util.List;
2323

2424
/** An interface for Google Cloud Datastore. */
25-
public interface Datastore extends Service<DatastoreOptions>, DatastoreReaderWriter {
25+
public interface Datastore extends Service<DatastoreOptions>, DatastoreReaderWriter, AutoCloseable {
2626

2727
/**
2828
* Returns a new Datastore transaction.
@@ -49,9 +49,9 @@ public interface Datastore extends Service<DatastoreOptions>, DatastoreReaderWri
4949
* @param <T> the type of the return value
5050
*/
5151
interface TransactionCallable<T> {
52+
5253
T run(DatastoreReaderWriter readerWriter) throws Exception;
5354
}
54-
5555
/**
5656
* Invokes the callback's {@link Datastore.TransactionCallable#run} method with a {@link
5757
* DatastoreReaderWriter} that is associated with a new transaction. The transaction will be
@@ -508,4 +508,15 @@ interface TransactionCallable<T> {
508508
default AggregationResults runAggregation(AggregationQuery query, ReadOption... options) {
509509
throw new UnsupportedOperationException("Not implemented.");
510510
}
511+
512+
/**
513+
* Closes the gRPC channels associated with this instance and frees up their resources. This
514+
* method blocks until all channels are closed. Once this method is called, this Datastore client
515+
* is no longer usable.
516+
*/
517+
@Override
518+
void close() throws Exception;
519+
520+
/** Returns true if this background resource has been shut down. */
521+
boolean isClosed();
511522
}

google-cloud-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,12 @@
4848
import java.util.Optional;
4949
import java.util.Set;
5050
import java.util.concurrent.Callable;
51+
import java.util.logging.Level;
52+
import java.util.logging.Logger;
5153

5254
final class DatastoreImpl extends BaseService<DatastoreOptions> implements Datastore {
5355

56+
Logger logger = Logger.getLogger(Datastore.class.getName());
5457
private final DatastoreRpc datastoreRpc;
5558
private final RetrySettings retrySettings;
5659
private static final ExceptionHandler TRANSACTION_EXCEPTION_HANDLER =
@@ -90,6 +93,20 @@ public Transaction newTransaction() {
9093
return new TransactionImpl(this);
9194
}
9295

96+
@Override
97+
public void close() throws Exception {
98+
try {
99+
datastoreRpc.close();
100+
} catch (Exception e) {
101+
logger.log(Level.WARNING, "Failed to close channels", e);
102+
}
103+
}
104+
105+
@Override
106+
public boolean isClosed() {
107+
return datastoreRpc.isClosed();
108+
}
109+
93110
static class ReadWriteTransactionCallable<T> implements Callable<T> {
94111

95112
private final Datastore datastore;

google-cloud-datastore/src/main/java/com/google/cloud/datastore/RetryAndTraceDatastoreRpcDecorator.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,16 @@ public RunAggregationQueryResponse runAggregationQuery(RunAggregationQueryReques
109109
() -> datastoreRpc.runAggregationQuery(request), SPAN_NAME_RUN_AGGREGATION_QUERY);
110110
}
111111

112+
@Override
113+
public void close() throws Exception {
114+
datastoreRpc.close();
115+
}
116+
117+
@Override
118+
public boolean isClosed() {
119+
return datastoreRpc.isClosed();
120+
}
121+
112122
public <O> O invokeRpc(Callable<O> block, String startSpan) {
113123
Span span = traceUtil.startSpan(startSpan);
114124
try (Scope scope = traceUtil.getTracer().withSpan(span)) {

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import com.google.datastore.v1.RunQueryResponse;
3737

3838
/** Provides access to the remote Datastore service. */
39-
public interface DatastoreRpc extends ServiceRpc {
39+
public interface DatastoreRpc extends ServiceRpc, AutoCloseable {
4040

4141
/**
4242
* Sends an allocate IDs request.
@@ -96,4 +96,10 @@ BeginTransactionResponse beginTransaction(BeginTransactionRequest request)
9696
default RunAggregationQueryResponse runAggregationQuery(RunAggregationQueryRequest request) {
9797
throw new UnsupportedOperationException("Not implemented.");
9898
}
99+
100+
@Override
101+
void close() throws Exception;
102+
103+
/** Returns true if this background resource has been shut down. */
104+
boolean isClosed();
99105
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
import java.util.Collections;
6464

6565
@InternalApi
66-
public class GrpcDatastoreRpc implements AutoCloseable, DatastoreRpc {
66+
public class GrpcDatastoreRpc implements DatastoreRpc {
6767

6868
private final GrpcDatastoreStub datastoreStub;
6969
private final ClientContext clientContext;
@@ -146,6 +146,11 @@ public RunAggregationQueryResponse runAggregationQuery(RunAggregationQueryReques
146146
return datastoreStub.runAggregationQueryCallable().call(request);
147147
}
148148

149+
@Override
150+
public boolean isClosed() {
151+
return closed && datastoreStub.isShutdown();
152+
}
153+
149154
private boolean isEmulator(DatastoreOptions datastoreOptions) {
150155
return isLocalHost(datastoreOptions.getHost())
151156
|| NoCredentials.getInstance().equals(datastoreOptions.getCredentials());

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,14 @@ public RunAggregationQueryResponse runAggregationQuery(RunAggregationQueryReques
211211
throw translate(ex);
212212
}
213213
}
214+
215+
@Override
216+
public void close() throws Exception {
217+
throw new UnsupportedOperationException("close() is not supported");
218+
}
219+
220+
@Override
221+
public boolean isClosed() {
222+
throw new UnsupportedOperationException("isClosed() is not supported");
223+
}
214224
}

google-cloud-datastore/src/main/java/com/google/cloud/datastore/testing/LocalDatastoreHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,12 @@ public class LocalDatastoreHelper extends BaseEmulatorHelper<DatastoreOptions> {
5757
private static final String GCLOUD_CMD_TEXT = "gcloud beta emulators datastore start";
5858
private static final String GCLOUD_CMD_PORT_FLAG = "--host-port=";
5959
private static final String VERSION_PREFIX = "cloud-datastore-emulator ";
60-
private static final String MIN_VERSION = "1.2.0";
60+
private static final String MIN_VERSION = "2.0.2"; // latest version compatible with java 8
6161

6262
// Downloadable emulator settings
6363
private static final String BIN_NAME = "cloud-datastore-emulator/cloud_datastore_emulator";
6464
private static final String FILENAME = "cloud-datastore-emulator-" + MIN_VERSION + ".zip";
65-
private static final String MD5_CHECKSUM = "ec2237a0f0ac54964c6bd95e12c73720";
65+
private static final String MD5_CHECKSUM = "e0d1170519cf52e2e5f9f93892cdf70c";
6666
private static final String BIN_CMD_PORT_FLAG = "--port=";
6767
private static final URL EMULATOR_URL;
6868
private static final String EMULATOR_URL_ENV_VAR = "DATASTORE_EMULATOR_URL";

google-cloud-datastore/src/test/java/com/google/cloud/datastore/DatastoreTest.java

Lines changed: 23 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import static org.junit.Assert.assertNotNull;
3232
import static org.junit.Assert.assertNull;
3333
import static org.junit.Assert.assertSame;
34+
import static org.junit.Assert.assertThrows;
3435
import static org.junit.Assert.assertTrue;
3536
import static org.junit.Assert.fail;
3637

@@ -39,7 +40,6 @@
3940
import com.google.cloud.datastore.Query.ResultType;
4041
import com.google.cloud.datastore.StructuredQuery.OrderBy;
4142
import com.google.cloud.datastore.StructuredQuery.PropertyFilter;
42-
import com.google.cloud.datastore.it.MultipleAttemptsRule;
4343
import com.google.cloud.datastore.spi.DatastoreRpcFactory;
4444
import com.google.cloud.datastore.spi.v1.DatastoreRpc;
4545
import com.google.cloud.datastore.testing.LocalDatastoreHelper;
@@ -79,29 +79,22 @@
7979
import java.util.List;
8080
import java.util.Map;
8181
import java.util.Set;
82-
import java.util.concurrent.TimeoutException;
8382
import java.util.function.Predicate;
8483
import org.easymock.EasyMock;
8584
import org.junit.AfterClass;
8685
import org.junit.Assert;
8786
import org.junit.Before;
8887
import org.junit.BeforeClass;
89-
import org.junit.ClassRule;
9088
import org.junit.Test;
9189
import org.junit.runner.RunWith;
9290
import org.junit.runners.JUnit4;
9391
import org.threeten.bp.Duration;
9492

9593
@RunWith(JUnit4.class)
9694
public class DatastoreTest {
97-
private static final int NUMBER_OF_ATTEMPTS = 5;
98-
99-
@ClassRule
100-
public static MultipleAttemptsRule rr = new MultipleAttemptsRule(NUMBER_OF_ATTEMPTS, 10);
101-
102-
private static LocalDatastoreHelper helper = LocalDatastoreHelper.create(1.0);
103-
private static final DatastoreOptions options = helper.getOptions();
104-
private static final Datastore datastore = options.getService();
95+
private static final LocalDatastoreHelper helper = LocalDatastoreHelper.create(1.0, 9090);
96+
private static DatastoreOptions options = helper.getOptions();
97+
private static Datastore datastore;
10598
private static final String PROJECT_ID = options.getProjectId();
10699
private static final String KIND1 = "kind1";
107100
private static final String KIND2 = "kind2";
@@ -177,6 +170,8 @@ public class DatastoreTest {
177170
@BeforeClass
178171
public static void beforeClass() throws IOException, InterruptedException {
179172
helper.start();
173+
options = helper.getOptions();
174+
datastore = options.getService();
180175
}
181176

182177
@Before
@@ -197,7 +192,8 @@ public void setUp() {
197192
}
198193

199194
@AfterClass
200-
public static void afterClass() throws IOException, InterruptedException, TimeoutException {
195+
public static void afterClass() throws Exception {
196+
datastore.close();
201197
helper.stop(Duration.ofMinutes(1));
202198
}
203199

@@ -1386,6 +1382,21 @@ public void testDatabaseIdKeyFactory() {
13861382
checkKeyProperties(incompleteKey);
13871383
}
13881384

1385+
@Test
1386+
public void testDatastoreClose() throws Exception {
1387+
Datastore datastore = options.toBuilder().build().getService();
1388+
Entity entity = datastore.get(KEY3);
1389+
assertNull(entity);
1390+
1391+
datastore.close();
1392+
assertTrue(datastore.isClosed());
1393+
1394+
assertThrows(
1395+
"io.grpc.StatusRuntimeException: UNAVAILABLE: Channel shutdown invoked",
1396+
DatastoreException.class,
1397+
() -> datastore.get(KEY3));
1398+
}
1399+
13891400
private void checkKeyProperties(BaseKey key) {
13901401
assertEquals(options.getDatabaseId(), key.getDatabaseId());
13911402
assertEquals(options.getProjectId(), key.getProjectId());

0 commit comments

Comments
 (0)