Skip to content

Commit 4ad1a3f

Browse files
rajanya-googlerenovate-botmmicatkagcf-owl-bot[bot]
authored
feat: Adding grpc compression support for publisher client (#1000)
* Adding gRPC compression support to the library * Minor comment fix * Formatting the code * Adding unit test for compression * Adding integration test for compression * Formatting * Refactoring integration tests to add support for overriding endpoint * Adding sample for publish with compression; Updating README * Adding integration test for compression sample * Adding parameter compressionBytesThreshold to Publisher; Adding logging support in the compression example * Addressing PR comments * Addressing checkstyle * Addressed PR comment * Addressing PR comment to put a Precondition for compression and its threshold * Addressing PR review * Removing logging from example * Adding logging properties * Making the publish call unified with context as per PR comments * Removing sample code as per tianzi@'s comments * Minor fixes * Adding gRPC compression support to the library * Minor comment fix * Formatting the code * Adding unit test for compression * Adding integration test for compression * Formatting * Refactoring integration tests to add support for overriding endpoint * Adding sample for publish with compression; Updating README * Adding integration test for compression sample * Adding parameter compressionBytesThreshold to Publisher; Adding logging support in the compression example * Addressing PR comments * Addressing checkstyle * Addressed PR comment * Addressing PR comment to put a Precondition for compression and its threshold * Addressing PR review * Removing logging from example * Adding logging properties * Making the publish call unified with context as per PR comments * Removing sample code as per tianzi@'s comments * Minor fixes * Fixing IT * Creating a class variable publishContext to remove the overhead of GrpcCallContext.createDefault() with every publish call * fixing lint format * Addressed PR comments * Removing test * build(deps): update dependency com.google.cloud:google-cloud-shared-config to v1.4.0 (#1105) [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [com.google.cloud:google-cloud-shared-config](https://togithub.com/googleapis/java-shared-config) | `1.3.3` -> `1.4.0` | [![age](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/compatibility-slim/1.3.3)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/com.google.cloud:google-cloud-shared-config/1.4.0/confidence-slim/1.3.3)](https://docs.renovatebot.com/merge-confidence/) | --- ### Release Notes <details> <summary>googleapis/java-shared-config</summary> ### [`v1.4.0`](https://togithub.com/googleapis/java-shared-config/blob/HEAD/CHANGELOG.md#&#8203;140-httpsgithubcomgoogleapisjava-shared-configcomparev133v140-2022-04-28) [Compare Source](https://togithub.com/googleapis/java-shared-config/compare/v1.3.3...v1.4.0) ##### Features - **java:** remove native image module ([#&#8203;471](https://togithub.com/googleapis/java-shared-config/issues/471)) ([7fcba01](https://togithub.com/googleapis/java-shared-config/commit/7fcba016b3138d7beaa4e962853f9bc80f59438c)) ##### [1.3.3](https://togithub.com/googleapis/java-shared-config/compare/v1.3.2...v1.3.3) (2022-04-19) ##### Bug Fixes - **java:** remove protobuf feature from native profile ([#&#8203;461](https://togithub.com/googleapis/java-shared-config/issues/461)) ([ffd07cb](https://togithub.com/googleapis/java-shared-config/commit/ffd07cb18ee7d45d4daee1d9ea6f6d321fdca874)) ##### Dependencies - update dependency com.google.cloud:native-image-support to v0.12.11 ([#&#8203;459](https://togithub.com/googleapis/java-shared-config/issues/459)) ([d20008d](https://togithub.com/googleapis/java-shared-config/commit/d20008df15209708fdf9d06828b567778190f4d0)) - update dependency com.google.cloud:native-image-support to v0.13.1 ([#&#8203;465](https://togithub.com/googleapis/java-shared-config/issues/465)) ([b202064](https://togithub.com/googleapis/java-shared-config/commit/b2020648816feb4740ad70acedfed470d7da5bcf)) ##### [1.3.2](https://togithub.com/googleapis/java-shared-config/compare/v1.3.1...v1.3.2) (2022-03-28) ##### Dependencies - revert google-java-format to 1.7 ([#&#8203;453](https://togithub.com/googleapis/java-shared-config/issues/453)) ([cbc777f](https://togithub.com/googleapis/java-shared-config/commit/cbc777f3e9ab75edb6fa2e0268a7446ae4111725)) ##### [1.3.1](https://togithub.com/googleapis/java-shared-config/compare/v1.3.0...v1.3.1) (2022-03-25) ##### Dependencies - update dependency com.google.cloud:native-image-support to v0.12.10 ([#&#8203;443](https://togithub.com/googleapis/java-shared-config/issues/443)) ([5b39d5e](https://togithub.com/googleapis/java-shared-config/commit/5b39d5ee15121f052226ff873b6ab101e9c71de5)) - update dependency com.google.googlejavaformat:google-java-format to v1.15.0 ([#&#8203;426](https://togithub.com/googleapis/java-shared-config/issues/426)) ([4c3c4b6](https://togithub.com/googleapis/java-shared-config/commit/4c3c4b66129632181e6bc363a0ecccf4f5aac914)) - update dependency org.graalvm.buildtools:junit-platform-native to v0.9.11 ([#&#8203;448](https://togithub.com/googleapis/java-shared-config/issues/448)) ([f7f518e](https://togithub.com/googleapis/java-shared-config/commit/f7f518e87d1d9feb9ac54d7c099f97d8751ee3da)) - update dependency org.graalvm.buildtools:native-maven-plugin to v0.9.11 ([#&#8203;449](https://togithub.com/googleapis/java-shared-config/issues/449)) ([3e1c0b5](https://togithub.com/googleapis/java-shared-config/commit/3e1c0b5a1d2f4a0db88c06a0d683ed90cbc745e2)) </details> --- ### Configuration 📅 **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-pubsub). * fix: added exactly once delivery files to owlbot config (#1106) * chore(bazel): update version of Protobuf to v3.20.1 (#1079) - [ ] Regenerate this pull request now. PiperOrigin-RevId: 444328399 Source-Link: googleapis/googleapis@c7ca416 Source-Link: googleapis/googleapis-gen@d617054 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiZDYxNzA1NDUzYTYyYjNlY2RhNzhhYTMwYzE5Mjg0MGViYzVhOGE5MCJ9 feat: AuditConfig for IAM v1 PiperOrigin-RevId: 439356405 Source-Link: googleapis/googleapis@afa2ba1 Source-Link: googleapis/googleapis-gen@3e40c17 Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiM2U0MGMxN2UxNTEwYzk1ZmFiNThmYzIxNDNjY2I2MWNjZWNhNTk4OSJ9 * chore(deps): upgrade gapic-generator-java to 2.7.0 and update gax-java to 2.16.0 (#1107) - [ ] Regenerate this pull request now. PiperOrigin-RevId: 446250659 Source-Link: googleapis/googleapis@dc4ef31 Source-Link: googleapis/googleapis-gen@5fdda4d Copy-Tag: eyJwIjoiLmdpdGh1Yi8uT3dsQm90LnlhbWwiLCJoIjoiNWZkZGE0ZGRmYmFiODc5OThlNzdlNGE0NTNlMGZmODc5ODZkMmRiOCJ9 * build(deps): update dependency org.apache.maven.plugins:maven-project-info-reports-plugin to v3.3.0 (#1104) [![WhiteSource Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com) This PR contains the following updates: | Package | Change | Age | Adoption | Passing | Confidence | |---|---|---|---|---|---| | [org.apache.maven.plugins:maven-project-info-reports-plugin](https://maven.apache.org/plugins/) ([source](https://togithub.com/apache/maven-project-info-reports-plugin)) | `3.2.2` -> `3.3.0` | [![age](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/age-slim)](https://docs.renovatebot.com/merge-confidence/) | [![adoption](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/adoption-slim)](https://docs.renovatebot.com/merge-confidence/) | [![passing](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/compatibility-slim/3.2.2)](https://docs.renovatebot.com/merge-confidence/) | [![confidence](https://badges.renovateapi.com/packages/maven/org.apache.maven.plugins:maven-project-info-reports-plugin/3.3.0/confidence-slim/3.2.2)](https://docs.renovatebot.com/merge-confidence/) | --- ### Configuration 📅 **Schedule**: At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, click this checkbox. --- This PR has been generated by [WhiteSource Renovate](https://renovate.whitesourcesoftware.com). View repository job log [here](https://app.renovatebot.com/dashboard#github/googleapis/java-pubsub). * Formatting * Formatting Co-authored-by: WhiteSource Renovate <[email protected]> Co-authored-by: Mike Micatka <[email protected]> Co-authored-by: gcf-owl-bot[bot] <78513119+gcf-owl-bot[bot]@users.noreply.github.com>
1 parent dc1670c commit 4ad1a3f

File tree

3 files changed

+142
-6
lines changed

3 files changed

+142
-6
lines changed

google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/Publisher.java

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.api.gax.core.ExecutorProvider;
3636
import com.google.api.gax.core.FixedExecutorProvider;
3737
import com.google.api.gax.core.InstantiatingExecutorProvider;
38+
import com.google.api.gax.grpc.GrpcCallContext;
3839
import com.google.api.gax.retrying.RetrySettings;
3940
import com.google.api.gax.rpc.HeaderProvider;
4041
import com.google.api.gax.rpc.NoHeaderProvider;
@@ -50,6 +51,7 @@
5051
import com.google.pubsub.v1.PubsubMessage;
5152
import com.google.pubsub.v1.TopicName;
5253
import com.google.pubsub.v1.TopicNames;
54+
import io.grpc.CallOptions;
5355
import java.io.IOException;
5456
import java.util.ArrayList;
5557
import java.util.HashMap;
@@ -89,6 +91,8 @@
8991
public class Publisher implements PublisherInterface {
9092
private static final Logger logger = Logger.getLogger(Publisher.class.getName());
9193

94+
private static final String GZIP_COMPRESSION = "gzip";
95+
9296
private final String topicName;
9397

9498
private final BatchingSettings batchingSettings;
@@ -114,6 +118,12 @@ public class Publisher implements PublisherInterface {
114118

115119
private MessageFlowController flowController = null;
116120

121+
private final boolean enableCompression;
122+
private final long compressionBytesThreshold;
123+
124+
private final GrpcCallContext publishContext;
125+
private final GrpcCallContext publishContextWithCompression;
126+
117127
/** The maximum number of messages in one request. Defined by the API. */
118128
public static long getApiMaxRequestElementCount() {
119129
return 1000L;
@@ -140,6 +150,8 @@ private Publisher(Builder builder) throws IOException {
140150

141151
this.enableMessageOrdering = builder.enableMessageOrdering;
142152
this.messageTransform = builder.messageTransform;
153+
this.enableCompression = builder.enableCompression;
154+
this.compressionBytesThreshold = builder.compressionBytesThreshold;
143155

144156
messagesBatches = new HashMap<>();
145157
messagesBatchLock = new ReentrantLock();
@@ -191,6 +203,10 @@ private Publisher(Builder builder) throws IOException {
191203
backgroundResources = new BackgroundResourceAggregation(backgroundResourceList);
192204
shutdown = new AtomicBoolean(false);
193205
messagesWaiter = new Waiter();
206+
this.publishContext = GrpcCallContext.createDefault();
207+
this.publishContextWithCompression =
208+
GrpcCallContext.createDefault()
209+
.withCallOptions(CallOptions.DEFAULT.withCompression(GZIP_COMPRESSION));
194210
}
195211

196212
/** Topic which the publisher publishes to. */
@@ -431,13 +447,18 @@ private void publishAllWithoutInflightForKey(final String orderingKey) {
431447
}
432448

433449
private ApiFuture<PublishResponse> publishCall(OutstandingBatch outstandingBatch) {
450+
GrpcCallContext context = publishContext;
451+
if (enableCompression && outstandingBatch.batchSizeBytes >= compressionBytesThreshold) {
452+
context = publishContextWithCompression;
453+
}
434454
return publisherStub
435455
.publishCallable()
436456
.futureCall(
437457
PublishRequest.newBuilder()
438458
.setTopic(topicName)
439459
.addAllMessages(outstandingBatch.getMessages())
440-
.build());
460+
.build(),
461+
context);
441462
}
442463

443464
private void publishOutstandingBatch(final OutstandingBatch outstandingBatch) {
@@ -688,6 +709,8 @@ public static final class Builder {
688709
InstantiatingExecutorProvider.newBuilder()
689710
.setExecutorThreadCount(THREADS_PER_CPU * Runtime.getRuntime().availableProcessors())
690711
.build();
712+
static final boolean DEFAULT_ENABLE_COMPRESSION = false;
713+
static final long DEFAULT_COMPRESSION_BYTES_THRESHOLD = 240L;
691714

692715
String topicName;
693716
private String endpoint = PublisherStubSettings.getDefaultEndpoint();
@@ -717,6 +740,9 @@ public PubsubMessage apply(PubsubMessage input) {
717740
}
718741
};
719742

743+
private boolean enableCompression = DEFAULT_ENABLE_COMPRESSION;
744+
private long compressionBytesThreshold = DEFAULT_COMPRESSION_BYTES_THRESHOLD;
745+
720746
private Builder(String topic) {
721747
this.topicName = Preconditions.checkNotNull(topic);
722748
}
@@ -827,6 +853,21 @@ public Builder setEndpoint(String endpoint) {
827853
return this;
828854
}
829855

856+
/** Gives the ability to enable transport compression. */
857+
public Builder setEnableCompression(boolean enableCompression) {
858+
this.enableCompression = enableCompression;
859+
return this;
860+
}
861+
862+
/**
863+
* Sets the threshold (in bytes) above which messages are compressed for transport. Only takes
864+
* effect if setEnableCompression(true) is also called."
865+
*/
866+
public Builder setCompressionBytesThreshold(long compressionBytesThreshold) {
867+
this.compressionBytesThreshold = compressionBytesThreshold;
868+
return this;
869+
}
870+
830871
/** Returns the default BatchingSettings used by the client if settings are not provided. */
831872
public static BatchingSettings getDefaultBatchingSettings() {
832873
return DEFAULT_BATCHING_SETTINGS;

google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/it/ITPubSubTest.java

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,7 @@
4040
import java.util.concurrent.Future;
4141
import java.util.concurrent.LinkedBlockingQueue;
4242
import java.util.concurrent.TimeUnit;
43-
import org.junit.AfterClass;
44-
import org.junit.Assert;
45-
import org.junit.BeforeClass;
46-
import org.junit.Rule;
47-
import org.junit.Test;
43+
import org.junit.*;
4844
import org.junit.rules.Timeout;
4945

5046
public class ITPubSubTest {
@@ -403,6 +399,70 @@ public void failed(Subscriber.State from, Throwable failure) {
403399
topicAdminClient.deleteTopic(topicName);
404400
}
405401

402+
@Test
403+
public void testPublishSubscribeWithCompression() throws Exception {
404+
TopicName topicName =
405+
TopicName.newBuilder()
406+
.setProject(projectId)
407+
.setTopic(formatForTest("testing-compression-topic"))
408+
.build();
409+
SubscriptionName subscriptionName =
410+
SubscriptionName.of(projectId, formatForTest("testing-compression-subscription"));
411+
412+
topicAdminClient.createTopic(topicName);
413+
414+
subscriptionAdminClient.createSubscription(
415+
getSubscription(subscriptionName, topicName, PushConfig.newBuilder().build(), 10, false));
416+
417+
final BlockingQueue<Object> receiveQueue = new LinkedBlockingQueue<>();
418+
Subscriber subscriber =
419+
Subscriber.newBuilder(
420+
subscriptionName.toString(),
421+
new MessageReceiver() {
422+
@Override
423+
public void receiveMessage(
424+
final PubsubMessage message, final AckReplyConsumer consumer) {
425+
receiveQueue.offer(MessageAndConsumer.create(message, consumer));
426+
}
427+
})
428+
.build();
429+
subscriber.addListener(
430+
new Subscriber.Listener() {
431+
public void failed(Subscriber.State from, Throwable failure) {
432+
receiveQueue.offer(failure);
433+
}
434+
},
435+
MoreExecutors.directExecutor());
436+
subscriber.startAsync();
437+
438+
Publisher publisher = Publisher.newBuilder(topicName).setEnableCompression(true).build();
439+
440+
String msg1 = generateMessage("msg1", 1000);
441+
String msg2 = generateMessage("msg2", 1500);
442+
publisher
443+
.publish(PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(msg1)).build())
444+
.get();
445+
publisher
446+
.publish(PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(msg2)).build())
447+
.get();
448+
publisher.shutdown();
449+
publisher.awaitTermination(1, TimeUnit.MINUTES);
450+
451+
// Ack the first message.
452+
MessageAndConsumer toAck1 = pollQueueMessageAndConsumer(receiveQueue);
453+
toAck1.consumer().ack();
454+
455+
// Ack the second message.
456+
MessageAndConsumer toAck2 = pollQueueMessageAndConsumer(receiveQueue);
457+
toAck2.consumer().ack();
458+
459+
assertNotEquals(toAck1.message().getData(), toAck2.message().getData());
460+
461+
subscriber.stopAsync().awaitTerminated();
462+
subscriptionAdminClient.deleteSubscription(subscriptionName);
463+
topicAdminClient.deleteTopic(topicName);
464+
}
465+
406466
private MessageAndConsumer pollQueueMessageAndConsumer(BlockingQueue<Object> queue)
407467
throws InterruptedException {
408468
Object obj = pollQueue(queue);
@@ -434,4 +494,14 @@ private Object pollQueue(BlockingQueue<Object> queue) throws InterruptedExceptio
434494

435495
return obj;
436496
}
497+
498+
/** Generates message of given bytes by repeatedly concatenating a token. */
499+
private String generateMessage(String token, int bytes) {
500+
String result = "";
501+
int tokenBytes = token.length();
502+
for (int i = 0; i < Math.floor(bytes / tokenBytes) + 1; i++) {
503+
result = result.concat(token);
504+
}
505+
return result;
506+
}
437507
}

google-cloud-pubsub/src/test/java/com/google/cloud/pubsub/v1/PublisherImplTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,31 @@ public void testPublishMixedSizeAndDuration() throws Exception {
282282
shutdownTestPublisher(publisher);
283283
}
284284

285+
@Test
286+
public void testPublishWithCompression() throws Exception {
287+
Publisher publisher =
288+
getTestPublisherBuilder()
289+
.setBatchingSettings(
290+
Publisher.Builder.DEFAULT_BATCHING_SETTINGS
291+
.toBuilder()
292+
.setElementCountThreshold(2L)
293+
.setDelayThreshold(Duration.ofSeconds(100))
294+
.build())
295+
.setEnableCompression(true)
296+
.setCompressionBytesThreshold(100)
297+
.build();
298+
299+
testPublisherServiceImpl.addPublishResponse(
300+
PublishResponse.newBuilder().addMessageIds("1").addMessageIds("2"));
301+
ApiFuture<String> publishFuture1 = sendTestMessage(publisher, "A");
302+
ApiFuture<String> publishFuture2 = sendTestMessage(publisher, "B");
303+
assertEquals("1", publishFuture1.get());
304+
assertEquals("2", publishFuture2.get());
305+
306+
fakeExecutor.advanceTime(Duration.ofSeconds(100));
307+
shutdownTestPublisher(publisher);
308+
}
309+
285310
private ApiFuture<String> sendTestMessage(Publisher publisher, String data) {
286311
return publisher.publish(
287312
PubsubMessage.newBuilder().setData(ByteString.copyFromUtf8(data)).build());

0 commit comments

Comments
 (0)