-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Spanner: Create new instance if existing Spanner is closed #5200
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
4a89f89
599535f
9bbd0e2
5561b8e
d273479
fb2a4db
5fbb912
7692138
b8bf651
2cc0635
1ca1c92
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -494,24 +494,40 @@ static String getServiceAccountProjectId(String credentialsPath) { | |
| */ | ||
| @SuppressWarnings("unchecked") | ||
| public ServiceT getService() { | ||
| if (service == null) { | ||
| if (shouldRefreshService(service)) { | ||
| service = serviceFactory.create((OptionsT) this); | ||
| } | ||
| return service; | ||
| } | ||
|
|
||
| /** | ||
| * @param cachedService The currently cached service object | ||
| * @return true if the currently cached service object should be refreshed. | ||
| */ | ||
| protected boolean shouldRefreshService(ServiceT cachedService) { | ||
| return cachedService == null; | ||
| } | ||
|
|
||
| /** | ||
| * Returns a Service RPC object for the current service. For instance, when using Google Cloud | ||
| * Storage, it returns a StorageRpc object. | ||
| */ | ||
| @SuppressWarnings("unchecked") | ||
| public ServiceRpc getRpc() { | ||
| if (rpc == null) { | ||
| if (shouldRefreshRpc(rpc)) { | ||
| rpc = serviceRpcFactory.create((OptionsT) this); | ||
| } | ||
| return rpc; | ||
| } | ||
|
|
||
| /** | ||
| * @param cachedService The currently cached service object | ||
|
||
| * @return true if the currently cached service object should be refreshed. | ||
| */ | ||
| protected boolean shouldRefreshRpc(ServiceRpc cachedRpc) { | ||
| return cachedRpc == null; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the project ID. Return value can be null (for services that don't require a project | ||
| * ID). | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -17,10 +17,13 @@ | |
| package com.google.cloud.spanner; | ||
|
|
||
| import static com.google.common.truth.Truth.assertThat; | ||
| import static org.hamcrest.CoreMatchers.is; | ||
| import static org.junit.Assert.assertThat; | ||
|
|
||
| import com.google.api.gax.retrying.RetrySettings; | ||
| import com.google.api.gax.rpc.ServerStreamingCallSettings; | ||
| import com.google.api.gax.rpc.UnaryCallSettings; | ||
| import com.google.cloud.NoCredentials; | ||
| import com.google.cloud.TransportOptions; | ||
| import com.google.cloud.spanner.admin.database.v1.stub.DatabaseAdminStubSettings; | ||
| import com.google.cloud.spanner.admin.instance.v1.stub.InstanceAdminStubSettings; | ||
|
|
@@ -353,4 +356,26 @@ public void testNullSessionLabels() { | |
| thrown.expect(NullPointerException.class); | ||
| SpannerOptions.newBuilder().setSessionLabels(null); | ||
| } | ||
|
|
||
| @Test | ||
| public void testDoNotCacheClosedSpannerInstance() { | ||
| SpannerOptions options = | ||
| SpannerOptions.newBuilder() | ||
| .setProjectId("[PROJECT]") | ||
| .setCredentials(NoCredentials.getInstance()) | ||
| .build(); | ||
| // Getting a service twice should give the same instance. | ||
| Spanner service1 = options.getService(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for completeness and coverage, can you add in a few assertions like:
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added assertions. |
||
| Spanner service2 = options.getService(); | ||
| assertThat(service1 == service2, is(true)); | ||
| // Closing a service instance should cause the SpannerOptions to create a new service. | ||
| service1.close(); | ||
| Spanner service3 = options.getService(); | ||
| assertThat(service3 == service1, is(false)); | ||
| assertThat(service3.isClosed(), is(false)); | ||
| // Getting another service from the SpannerOptions should return the new cached instance. | ||
| Spanner service4 = options.getService(); | ||
| assertThat(service3 == service4, is(true)); | ||
| service3.close(); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this and
shouldRefreshRpcbe static?It looks like we're not using anything from the instance but we're allowing for that possibility in the future. If so, should we instead provide a protected getter for the cached service/rpc?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to let these two methods be overridable. The base
ServiceandServiceRpcclasses are not closeable and therefore only does acachedService == nullcheck.The
Spannerclass (a subclass ofService) implementsAutoCloseableandSpannerOptionstherefore implements a custom check in theshouldRefreshService()method that callsSpanner#isClosed()instead of thecachedService == nullcheck.