Skip to content

Commit 7a555d4

Browse files
committed
feat(bigquery): make BigQuery AutoCloseable with default no-op close method
1 parent 5af88f8 commit 7a555d4

3 files changed

Lines changed: 110 additions & 1 deletion

File tree

java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQuery.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
*
4141
* @see <a href="https://cloud.google.com/bigquery/what-is-bigquery">Google Cloud BigQuery</a>
4242
*/
43-
public interface BigQuery extends Service<BigQueryOptions> {
43+
public interface BigQuery extends Service<BigQueryOptions>, AutoCloseable {
4444

4545
/**
4646
* Fields of a BigQuery Dataset resource.
@@ -1821,4 +1821,14 @@ Object queryWithTimeout(
18211821
* represents the subset of granted permissions.
18221822
*/
18231823
List<String> testIamPermissions(TableId table, List<String> permissions, IAMOption... options);
1824+
1825+
/**
1826+
* Closes any background resources and transport channels held by this service.
1827+
*
1828+
* <p>The default implementation does nothing. Implementations that manage background resources
1829+
* (such as gRPC channels or storage clients) should override this method to release them
1830+
* deterministically.
1831+
*/
1832+
@Override
1833+
default void close() {}
18241834
}

java-bigquery/google-cloud-bigquery/src/main/java/com/google/cloud/bigquery/BigQueryImpl.java

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,7 @@ private void closeClient() {
504504

505505
private transient ConcurrentHashMap<String, BigQueryReadClient> bqReadClients;
506506
private transient boolean isGlobalClientUserProvided;
507+
private boolean closed = false;
507508

508509
/**
509510
* Lazily creates or retrieves the shared {@link BigQueryReadClient} instance used for streaming
@@ -527,6 +528,9 @@ BigQueryReadClient getBigQueryReadClient() {
527528
* @throws BigQueryException if initializing the storage read client fails
528529
*/
529530
BigQueryReadClient getBigQueryReadClient(String location) {
531+
if (closed) {
532+
throw new IllegalStateException("BigQuery service has been closed");
533+
}
530534
String cacheKey = location != null ? location.toLowerCase() : "global";
531535
if (bqReadClients == null) {
532536
synchronized (this) {
@@ -543,6 +547,9 @@ BigQueryReadClient getBigQueryReadClient(String location) {
543547
return client;
544548
}
545549
synchronized (this) {
550+
if (closed) {
551+
throw new IllegalStateException("BigQuery service has been closed");
552+
}
546553
client = bqReadClients.get(cacheKey);
547554
if (client == null && isGlobalClientUserProvided) {
548555
client = bqReadClients.get("global");
@@ -583,6 +590,32 @@ void setBigQueryReadClient(String location, BigQueryReadClient client) {
583590
}
584591
}
585592

593+
/**
594+
* Closes any background resources and transport channels held by this {@link BigQueryImpl},
595+
* including the underlying {@link BigQueryReadClient} instances used for Arrow query streaming.
596+
*/
597+
@Override
598+
public void close() {
599+
List<BigQueryReadClient> clientsToClose = new ArrayList<>();
600+
synchronized (this) {
601+
if (closed) {
602+
return;
603+
}
604+
closed = true;
605+
if (bqReadClients != null) {
606+
clientsToClose.addAll(bqReadClients.values());
607+
bqReadClients.clear();
608+
}
609+
}
610+
for (BigQueryReadClient client : clientsToClose) {
611+
try {
612+
client.close();
613+
} catch (Exception e) {
614+
// Ignore exceptions during teardown
615+
}
616+
}
617+
}
618+
586619
/**
587620
* Configures a {@link BigQueryReadSettings.Builder} with credentials, header provider, and
588621
* universe domain mapped from the given {@link BigQueryOptions}.

java-bigquery/google-cloud-bigquery/src/test/java/com/google/cloud/bigquery/BigQueryImplTest.java

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4260,4 +4260,70 @@ void testTestIamPermissionsWhenNoPermissionsGranted() throws IOException {
42604260
.testIamPermissionsSkipExceptionTranslation(
42614261
resourceId, checkedPermissions, EMPTY_RPC_OPTIONS);
42624262
}
4263+
4264+
@Test
4265+
void testCloseClosesBigQueryReadClient() {
4266+
BigQueryReadClient mockReadClient =
4267+
mock(BigQueryReadClient.class, withSettings().withoutAnnotations());
4268+
bigquery = options.getService();
4269+
((BigQueryImpl) bigquery).setBigQueryReadClient(mockReadClient);
4270+
4271+
bigquery.close();
4272+
4273+
verify(mockReadClient, times(1)).close();
4274+
}
4275+
4276+
@Test
4277+
void testCloseIsIdempotent() {
4278+
BigQueryReadClient mockReadClient =
4279+
mock(BigQueryReadClient.class, withSettings().withoutAnnotations());
4280+
bigquery = options.getService();
4281+
((BigQueryImpl) bigquery).setBigQueryReadClient(mockReadClient);
4282+
4283+
bigquery.close();
4284+
bigquery.close();
4285+
4286+
verify(mockReadClient, times(1)).close();
4287+
}
4288+
4289+
@Test
4290+
void testCloseWithoutReadClientDoesNotThrow() {
4291+
bigquery = options.getService();
4292+
bigquery.close();
4293+
}
4294+
4295+
@Test
4296+
void testTryWithResources() {
4297+
BigQueryReadClient mockReadClient =
4298+
mock(BigQueryReadClient.class, withSettings().withoutAnnotations());
4299+
try (BigQuery bq = options.getService()) {
4300+
((BigQueryImpl) bq).setBigQueryReadClient(mockReadClient);
4301+
assertNotNull(bq);
4302+
}
4303+
verify(mockReadClient, times(1)).close();
4304+
}
4305+
4306+
@Test
4307+
void testGetBigQueryReadClientAfterCloseThrows() {
4308+
bigquery = options.getService();
4309+
bigquery.close();
4310+
assertThrows(
4311+
IllegalStateException.class, () -> ((BigQueryImpl) bigquery).getBigQueryReadClient());
4312+
}
4313+
4314+
@Test
4315+
void testCloseClosesAllRegionalBigQueryReadClients() {
4316+
BigQueryReadClient mockReadClientUs =
4317+
mock(BigQueryReadClient.class, withSettings().withoutAnnotations());
4318+
BigQueryReadClient mockReadClientEu =
4319+
mock(BigQueryReadClient.class, withSettings().withoutAnnotations());
4320+
bigquery = options.getService();
4321+
((BigQueryImpl) bigquery).setBigQueryReadClient("us", mockReadClientUs);
4322+
((BigQueryImpl) bigquery).setBigQueryReadClient("eu", mockReadClientEu);
4323+
4324+
bigquery.close();
4325+
4326+
verify(mockReadClientUs, times(1)).close();
4327+
verify(mockReadClientEu, times(1)).close();
4328+
}
42634329
}

0 commit comments

Comments
 (0)