Skip to content

Commit 50f8425

Browse files
authored
feat: enable client to server compression (#2117)
Enable compression of network traffic from client to server. See the below screenshots from Wireshark for tests that show that the compression works both ways. Note: The rate of compression will depend on the type of data that is being sent. The below example for client->server communication is for sending 1,000 very similar mutations to Spanner. That compresses very well with gzip. More random data will compress less. ### Server -> Client - No Compression ![server-_client identity](https://user-images.githubusercontent.com/1196707/196627944-4d6f34fa-78a9-4023-a848-51a0ffe5228e.png) ### Server -> Client - Gzip Compression ![server-_client gzip](https://user-images.githubusercontent.com/1196707/196627948-d2d574ed-3f69-4b3c-8613-c162e21e207d.png) ### Client -> Server - No Compression ![client-_server identity](https://user-images.githubusercontent.com/1196707/196627950-b01ecb88-f6e8-4fd9-b7f8-257ad4211ac9.png) ### Client -> Server - Gzip Compression ![client-_server gzip](https://user-images.githubusercontent.com/1196707/196627952-ecf8b4bd-cd44-4941-af55-6aecaf03f69e.png)
1 parent 44f27fc commit 50f8425

File tree

4 files changed

+67
-1
lines changed

4 files changed

+67
-1
lines changed

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1060,7 +1060,8 @@ public Builder setCallCredentialsProvider(CallCredentialsProvider callCredential
10601060

10611061
/**
10621062
* Sets the compression to use for all gRPC calls. The compressor must be a valid name known in
1063-
* the {@link CompressorRegistry}.
1063+
* the {@link CompressorRegistry}. This will enable compression both from the client to the
1064+
* server and from the server to the client.
10641065
*
10651066
* <p>Supported values are:
10661067
*

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

+5
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ public GapicSpannerRpc(final SpannerOptions options) {
389389
MoreObjects.firstNonNull(
390390
options.getInterceptorProvider(),
391391
SpannerInterceptorProvider.createDefault()))
392+
// This sets the response compressor (Server -> Client).
392393
.withEncoding(compressorName))
393394
.setHeaderProvider(headerProviderWithUserAgent)
394395
// Attempts direct access to spanner service over gRPC to improve throughput,
@@ -1901,6 +1902,10 @@ <ReqT, RespT> GrpcCallContext newCallContext(
19011902
if (options != null) {
19021903
context = context.withChannelAffinity(Option.CHANNEL_HINT.getLong(options).intValue());
19031904
}
1905+
if (compressorName != null) {
1906+
// This sets the compressor for Client -> Server.
1907+
context = context.withCallOptions(context.getCallOptions().withCompression(compressorName));
1908+
}
19041909
context = context.withExtraHeaders(metadataProvider.newExtraHeaders(resource, projectName));
19051910
if (callCredentialsProvider != null) {
19061911
CallCredentials callCredentials = callCredentialsProvider.getCallCredentials();

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

+7
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,12 @@ public <ReqT, RespT> ServerCall.Listener<ReqT> interceptCall(
130130
ServerCall<ReqT, RespT> call,
131131
Metadata headers,
132132
ServerCallHandler<ReqT, RespT> next) {
133+
// Verify that the compressor name header is set.
134+
assertEquals(
135+
"gzip",
136+
headers.get(
137+
Metadata.Key.of(
138+
"x-response-encoding", Metadata.ASCII_STRING_MARSHALLER)));
133139
Attributes attributes = call.getAttributes();
134140
@SuppressWarnings({"unchecked", "deprecation"})
135141
Attributes.Key<InetSocketAddress> key =
@@ -179,6 +185,7 @@ private SpannerOptions createSpannerOptions() {
179185
return input;
180186
})
181187
.setNumChannels(numChannels)
188+
.setCompressorName("gzip")
182189
.setSessionPoolOption(
183190
SessionPoolOptions.newBuilder()
184191
.setMinSessions(numChannels * 2)

google-cloud-spanner/src/test/java/com/google/cloud/spanner/spi/v1/GapicSpannerRpcTest.java

+53
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.MatcherAssert.assertThat;
2121
import static org.junit.Assert.assertEquals;
22+
import static org.junit.Assert.assertNull;
2223
import static org.junit.Assert.assertThrows;
2324
import static org.junit.Assume.assumeTrue;
2425

@@ -259,6 +260,58 @@ public void testNoCallCredentials() {
259260
rpc.shutdown();
260261
}
261262

263+
@Test
264+
public void testClientCompressorGzip() {
265+
SpannerOptions options =
266+
SpannerOptions.newBuilder().setProjectId("some-project").setCompressorName("gzip").build();
267+
GapicSpannerRpc rpc = new GapicSpannerRpc(options, false);
268+
assertEquals(
269+
"gzip",
270+
rpc.newCallContext(
271+
optionsMap,
272+
"/some/resource",
273+
GetSessionRequest.getDefaultInstance(),
274+
SpannerGrpc.getGetSessionMethod())
275+
.getCallOptions()
276+
.getCompressor());
277+
rpc.shutdown();
278+
}
279+
280+
@Test
281+
public void testClientCompressorIdentity() {
282+
SpannerOptions options =
283+
SpannerOptions.newBuilder()
284+
.setProjectId("some-project")
285+
.setCompressorName("identity")
286+
.build();
287+
GapicSpannerRpc rpc = new GapicSpannerRpc(options, false);
288+
assertEquals(
289+
"identity",
290+
rpc.newCallContext(
291+
optionsMap,
292+
"/some/resource",
293+
GetSessionRequest.getDefaultInstance(),
294+
SpannerGrpc.getGetSessionMethod())
295+
.getCallOptions()
296+
.getCompressor());
297+
rpc.shutdown();
298+
}
299+
300+
@Test
301+
public void testClientCompressorDefault() {
302+
SpannerOptions options = SpannerOptions.newBuilder().setProjectId("some-project").build();
303+
GapicSpannerRpc rpc = new GapicSpannerRpc(options, false);
304+
assertNull(
305+
rpc.newCallContext(
306+
optionsMap,
307+
"/some/resource",
308+
GetSessionRequest.getDefaultInstance(),
309+
SpannerGrpc.getGetSessionMethod())
310+
.getCallOptions()
311+
.getCompressor());
312+
rpc.shutdown();
313+
}
314+
262315
private static final class TimeoutHolder {
263316

264317
private Duration timeout;

0 commit comments

Comments
 (0)