Skip to content

Commit 2a4c19e

Browse files
committed
feat: support setting compression option
1 parent b7bac19 commit 2a4c19e

File tree

4 files changed

+76
-0
lines changed

4 files changed

+76
-0
lines changed

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

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
import com.google.spanner.admin.database.v1.RestoreDatabaseRequest;
5050
import com.google.spanner.v1.ExecuteSqlRequest.QueryOptions;
5151
import io.grpc.CallCredentials;
52+
import io.grpc.CompressorRegistry;
53+
import io.grpc.ExperimentalApi;
5254
import io.grpc.ManagedChannelBuilder;
5355
import java.io.IOException;
5456
import java.net.MalformedURLException;
@@ -58,6 +60,7 @@
5860
import java.util.Map.Entry;
5961
import java.util.Set;
6062
import javax.annotation.Nonnull;
63+
import javax.annotation.Nullable;
6164
import org.threeten.bp.Duration;
6265

6366
/** Options for the Cloud Spanner service. */
@@ -104,6 +107,7 @@ public class SpannerOptions extends ServiceOptions<Spanner, SpannerOptions> {
104107
private final Map<DatabaseId, QueryOptions> mergedQueryOptions;
105108

106109
private final CallCredentialsProvider callCredentialsProvider;
110+
private final String compressorName;
107111

108112
/**
109113
* Interface that can be used to provide {@link CallCredentials} instead of {@link Credentials} to
@@ -174,6 +178,7 @@ private SpannerOptions(Builder builder) {
174178
this.mergedQueryOptions = ImmutableMap.copyOf(merged);
175179
}
176180
callCredentialsProvider = builder.callCredentialsProvider;
181+
compressorName = builder.compressorName;
177182
}
178183

179184
/**
@@ -238,6 +243,7 @@ public static class Builder
238243
private boolean autoThrottleAdministrativeRequests = false;
239244
private Map<DatabaseId, QueryOptions> defaultQueryOptions = new HashMap<>();
240245
private CallCredentialsProvider callCredentialsProvider;
246+
private String compressorName;
241247
private String emulatorHost = System.getenv("SPANNER_EMULATOR_HOST");
242248

243249
private Builder() {
@@ -309,6 +315,7 @@ private Builder() {
309315
this.autoThrottleAdministrativeRequests = options.autoThrottleAdministrativeRequests;
310316
this.defaultQueryOptions = options.defaultQueryOptions;
311317
this.callCredentialsProvider = options.callCredentialsProvider;
318+
this.compressorName = options.compressorName;
312319
this.channelProvider = options.channelProvider;
313320
this.channelConfigurator = options.channelConfigurator;
314321
this.interceptorProvider = options.interceptorProvider;
@@ -558,6 +565,28 @@ public Builder setCallCredentialsProvider(CallCredentialsProvider callCredential
558565
return this;
559566
}
560567

568+
/**
569+
* Sets the compression to use for all gRPC calls. The compressor must be a valid name known in
570+
* the {@link CompressorRegistry}.
571+
*
572+
* <p>Supported values are:
573+
*
574+
* <ul>
575+
* <li>gzip: Enable gzip compression
576+
* <li>identity: Disable compression
577+
* <li><code>null</code>: Use default comporession
578+
* <ul>
579+
*/
580+
@ExperimentalApi("https://github.com/grpc/grpc-java/issues/1704")
581+
public Builder setCompressorName(@Nullable String compressorName) {
582+
Preconditions.checkArgument(
583+
compressorName == null
584+
|| CompressorRegistry.getDefaultInstance().lookupCompressor(compressorName) != null,
585+
String.format("%s is not a known compressor", compressorName));
586+
this.compressorName = compressorName;
587+
return this;
588+
}
589+
561590
/**
562591
* Specifying this will allow the client to prefetch up to {@code prefetchChunks} {@code
563592
* PartialResultSet} chunks for each read and query. The data size of each chunk depends on the
@@ -690,6 +719,10 @@ public CallCredentialsProvider getCallCredentialsProvider() {
690719
return callCredentialsProvider;
691720
}
692721

722+
public String getCompressorName() {
723+
return compressorName;
724+
}
725+
693726
/** Returns the default query options to use for the specific database. */
694727
public QueryOptions getDefaultQueryOptions(DatabaseId databaseId) {
695728
// Use the specific query options for the database if any have been specified. These have

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ private void awaitTermination() throws InterruptedException {
226226
private final String projectName;
227227
private final SpannerMetadataProvider metadataProvider;
228228
private final CallCredentialsProvider callCredentialsProvider;
229+
private final String compressorName;
229230
private final Duration waitTimeout =
230231
systemProperty(PROPERTY_TIMEOUT_SECONDS, DEFAULT_TIMEOUT_SECONDS);
231232
private final Duration idleTimeout =
@@ -279,6 +280,7 @@ public GapicSpannerRpc(final SpannerOptions options) {
279280
mergedHeaderProvider.getHeaders(),
280281
internalHeaderProviderBuilder.getResourceHeaderKey());
281282
this.callCredentialsProvider = options.getCallCredentialsProvider();
283+
this.compressorName = options.getCompressorName();
282284

283285
// Create a managed executor provider.
284286
this.executorProvider =
@@ -1239,6 +1241,9 @@ GrpcCallContext newCallContext(@Nullable Map<Option, ?> options, String resource
12391241
context.withCallOptions(context.getCallOptions().withCallCredentials(callCredentials));
12401242
}
12411243
}
1244+
if (compressorName != null) {
1245+
context = context.withCallOptions(context.getCallOptions().withCompression(compressorName));
1246+
}
12421247
return context.withStreamWaitTimeout(waitTimeout).withStreamIdleTimeout(idleTimeout);
12431248
}
12441249

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,4 +505,21 @@ public String getOptimizerVersion() {
505505
assertThat(options.getDefaultQueryOptions(DatabaseId.of("p", "i", "o")))
506506
.isEqualTo(QueryOptions.newBuilder().setOptimizerVersion("2").build());
507507
}
508+
509+
@Test
510+
public void testCompressorName() {
511+
assertThat(SpannerOptions.newBuilder().setCompressorName("gzip").build().getCompressorName())
512+
.isEqualTo("gzip");
513+
assertThat(
514+
SpannerOptions.newBuilder().setCompressorName("identity").build().getCompressorName())
515+
.isEqualTo("identity");
516+
assertThat(SpannerOptions.newBuilder().setCompressorName(null).build().getCompressorName())
517+
.isNull();
518+
try {
519+
SpannerOptions.newBuilder().setCompressorName("foo");
520+
fail("missing expected exception");
521+
} catch (IllegalArgumentException e) {
522+
// ignore, this is the expected exception.
523+
}
524+
}
508525
}

google-cloud-spanner/src/test/java/com/google/cloud/spanner/it/ITSpannerOptionsTest.java

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,27 @@ public void testMultipleSpannersFromSameSpannerOptions() throws InterruptedExcep
168168
assertThat(getNumberOfThreadsWithName(SPANNER_THREAD_NAME)).isAtMost(baseThreadCount);
169169
}
170170

171+
@Test
172+
public void testCompression() {
173+
for (String compressorName : new String[] {"gzip", "identity", null}) {
174+
SpannerOptions options =
175+
env.getTestHelper().getOptions().toBuilder().setCompressorName(compressorName).build();
176+
try (Spanner spanner = options.getService()) {
177+
DatabaseClient client = spanner.getDatabaseClient(db.getId());
178+
try (ResultSet rs =
179+
client
180+
.singleUse()
181+
.executeQuery(Statement.of("SELECT 1 AS COL1 UNION ALL SELECT 2 AS COL2"))) {
182+
assertThat(rs.next()).isTrue();
183+
assertThat(rs.getLong(0)).isEqualTo(1L);
184+
assertThat(rs.next()).isTrue();
185+
assertThat(rs.getLong(0)).isEqualTo(2L);
186+
assertThat(rs.next()).isFalse();
187+
}
188+
}
189+
}
190+
}
191+
171192
private void waitForStartup() throws InterruptedException {
172193
// Wait until the IT environment has already started all base worker threads.
173194
int threadCount;

0 commit comments

Comments
 (0)