Skip to content

Commit 4bc01c7

Browse files
fix for publisher shutdown review changes for awaitTermination method
1 parent 35ee213 commit 4bc01c7

File tree

2 files changed

+101
-110
lines changed

2 files changed

+101
-110
lines changed

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

+101-109
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@
2525
import com.google.api.core.BetaApi;
2626
import com.google.api.core.SettableApiFuture;
2727
import com.google.api.gax.batching.BatchingSettings;
28+
import com.google.api.gax.core.BackgroundResource;
29+
import com.google.api.gax.core.BackgroundResourceAggregation;
2830
import com.google.api.gax.core.CredentialsProvider;
2931
import com.google.api.gax.core.ExecutorAsBackgroundResource;
3032
import com.google.api.gax.core.ExecutorProvider;
@@ -40,6 +42,7 @@
4042
import com.google.cloud.pubsub.v1.stub.PublisherStub;
4143
import com.google.cloud.pubsub.v1.stub.PublisherStubSettings;
4244
import com.google.common.base.Preconditions;
45+
import com.google.common.collect.ImmutableList;
4346
import com.google.pubsub.v1.PublishRequest;
4447
import com.google.pubsub.v1.PublishResponse;
4548
import com.google.pubsub.v1.PubsubMessage;
@@ -86,15 +89,16 @@ public class Publisher {
8689
private final BatchingSettings batchingSettings;
8790

8891
private final Lock messagesBatchLock;
89-
private MessagesBatch messagesBatch;
92+
private List<OutstandingPublish> messagesBatch;
93+
private int batchedBytes;
9094

9195
private final AtomicBoolean activeAlarm;
9296

9397
private final PublisherStub publisherStub;
9498

9599
private final ScheduledExecutorService executor;
96100
private final AtomicBoolean shutdown;
97-
private final List<AutoCloseable> closeables;
101+
private final BackgroundResource backgroundResources;
98102
private final MessageWaiter messagesWaiter;
99103
private ScheduledFuture<?> currentAlarmFuture;
100104
private final ApiFunction<PubsubMessage, PubsubMessage> messageTransform;
@@ -115,15 +119,14 @@ private Publisher(Builder builder) throws IOException {
115119
this.batchingSettings = builder.batchingSettings;
116120
this.messageTransform = builder.messageTransform;
117121

118-
messagesBatch = new MessagesBatch();
122+
messagesBatch = new LinkedList<>();
119123
messagesBatchLock = new ReentrantLock();
120124
activeAlarm = new AtomicBoolean(false);
121125
executor = builder.executorProvider.getExecutor();
126+
127+
List<BackgroundResource> backgroundResourceList = new ArrayList<>();
122128
if (builder.executorProvider.shouldAutoClose()) {
123-
closeables =
124-
Collections.<AutoCloseable>singletonList(new ExecutorAsBackgroundResource(executor));
125-
} else {
126-
closeables = Collections.emptyList();
129+
backgroundResourceList.add(new ExecutorAsBackgroundResource(executor));
127130
}
128131

129132
// Publisher used to take maxAttempt == 0 to mean infinity, but to GAX it means don't retry.
@@ -151,7 +154,8 @@ private Publisher(Builder builder) throws IOException {
151154
.setRetrySettings(retrySettings)
152155
.setBatchingSettings(BatchingSettings.newBuilder().setIsEnabled(false).build());
153156
this.publisherStub = GrpcPublisherStub.create(stubSettings.build());
154-
157+
backgroundResourceList.add(publisherStub);
158+
backgroundResources = new BackgroundResourceAggregation(backgroundResourceList);
155159
shutdown = new AtomicBoolean(false);
156160
messagesWaiter = new MessageWaiter();
157161
}
@@ -197,75 +201,96 @@ public ApiFuture<String> publish(PubsubMessage message) {
197201
}
198202

199203
message = messageTransform.apply(message);
200-
List<OutstandingBatch> batchesToSend = new ArrayList<>();
201-
final OutstandingPublish outstandingPublish = new OutstandingPublish(message);
204+
final int messageSize = message.getSerializedSize();
205+
OutstandingBatch batchToSend = null;
206+
SettableApiFuture<String> publishResult = SettableApiFuture.<String>create();
207+
final OutstandingPublish outstandingPublish = new OutstandingPublish(publishResult, message);
202208
messagesBatchLock.lock();
203209
try {
204210
// Check if the next message makes the current batch exceed the max batch byte size.
205211
if (!messagesBatch.isEmpty()
206212
&& hasBatchingBytes()
207-
&& messagesBatch.getBatchedBytes() + outstandingPublish.messageSize
208-
>= getMaxBatchBytes()) {
209-
batchesToSend.add(messagesBatch.popOutstandingBatch());
213+
&& batchedBytes + messageSize >= getMaxBatchBytes()) {
214+
batchToSend = new OutstandingBatch(messagesBatch, batchedBytes);
215+
messagesBatch = new LinkedList<>();
216+
batchedBytes = 0;
210217
}
211218

212-
messagesBatch.addMessage(outstandingPublish, outstandingPublish.messageSize);
213-
214-
// Border case: If the message to send is greater or equals to the max batch size then send it
215-
// immediately.
216-
// Alternatively if after adding the message we have reached the batch max messages then we
217-
// have a batch to send.
218-
if ((hasBatchingBytes() && outstandingPublish.messageSize >= getMaxBatchBytes())
219-
|| messagesBatch.getMessagesCount() == getBatchingSettings().getElementCountThreshold()) {
220-
batchesToSend.add(messagesBatch.popOutstandingBatch());
219+
// Border case if the message to send is greater or equals to the max batch size then can't
220+
// be included in the current batch and instead sent immediately.
221+
if (!hasBatchingBytes() || messageSize < getMaxBatchBytes()) {
222+
batchedBytes += messageSize;
223+
messagesBatch.add(outstandingPublish);
224+
225+
// If after adding the message we have reached the batch max messages then we have a batch
226+
// to send.
227+
if (messagesBatch.size() == getBatchingSettings().getElementCountThreshold()) {
228+
batchToSend = new OutstandingBatch(messagesBatch, batchedBytes);
229+
messagesBatch = new LinkedList<>();
230+
batchedBytes = 0;
231+
}
221232
}
222233
// Setup the next duration based delivery alarm if there are messages batched.
223-
setupAlarm();
234+
if (!messagesBatch.isEmpty()) {
235+
setupDurationBasedPublishAlarm();
236+
} else if (currentAlarmFuture != null) {
237+
logger.log(Level.FINER, "Cancelling alarm, no more messages");
238+
if (activeAlarm.getAndSet(false)) {
239+
currentAlarmFuture.cancel(false);
240+
}
241+
}
224242
} finally {
225243
messagesBatchLock.unlock();
226244
}
227245

228246
messagesWaiter.incrementPendingMessages(1);
229247

230-
if (!batchesToSend.isEmpty()) {
231-
for (final OutstandingBatch batch : batchesToSend) {
232-
logger.log(Level.FINER, "Scheduling a batch for immediate sending.");
233-
executor.execute(
234-
new Runnable() {
235-
@Override
236-
public void run() {
237-
publishOutstandingBatch(batch);
238-
}
239-
});
240-
}
248+
if (batchToSend != null) {
249+
logger.log(Level.FINER, "Scheduling a batch for immediate sending.");
250+
final OutstandingBatch finalBatchToSend = batchToSend;
251+
executor.execute(
252+
new Runnable() {
253+
@Override
254+
public void run() {
255+
publishOutstandingBatch(finalBatchToSend);
256+
}
257+
});
258+
}
259+
260+
// If the message is over the size limit, it was not added to the pending messages and it will
261+
// be sent in its own batch immediately.
262+
if (hasBatchingBytes() && messageSize >= getMaxBatchBytes()) {
263+
logger.log(
264+
Level.FINER, "Message exceeds the max batch bytes, scheduling it for immediate send.");
265+
executor.execute(
266+
new Runnable() {
267+
@Override
268+
public void run() {
269+
publishOutstandingBatch(
270+
new OutstandingBatch(ImmutableList.of(outstandingPublish), messageSize));
271+
}
272+
});
241273
}
242274

243-
return outstandingPublish.publishResult;
275+
return publishResult;
244276
}
245277

246-
private void setupAlarm() {
247-
if (!messagesBatch.isEmpty()) {
248-
if (!activeAlarm.getAndSet(true)) {
249-
long delayThresholdMs = getBatchingSettings().getDelayThreshold().toMillis();
250-
logger.log(Level.FINER, "Setting up alarm for the next {0} ms.", delayThresholdMs);
251-
currentAlarmFuture =
252-
executor.schedule(
253-
new Runnable() {
254-
@Override
255-
public void run() {
256-
logger.log(Level.FINER, "Sending messages based on schedule.");
257-
activeAlarm.getAndSet(false);
258-
publishAllOutstanding();
259-
}
260-
},
261-
delayThresholdMs,
262-
TimeUnit.MILLISECONDS);
263-
}
264-
} else if (currentAlarmFuture != null) {
265-
logger.log(Level.FINER, "Cancelling alarm, no more messages");
266-
if (activeAlarm.getAndSet(false)) {
267-
currentAlarmFuture.cancel(false);
268-
}
278+
private void setupDurationBasedPublishAlarm() {
279+
if (!activeAlarm.getAndSet(true)) {
280+
long delayThresholdMs = getBatchingSettings().getDelayThreshold().toMillis();
281+
logger.log(Level.FINER, "Setting up alarm for the next {0} ms.", delayThresholdMs);
282+
currentAlarmFuture =
283+
executor.schedule(
284+
new Runnable() {
285+
@Override
286+
public void run() {
287+
logger.log(Level.FINER, "Sending messages based on schedule.");
288+
activeAlarm.getAndSet(false);
289+
publishAllOutstanding();
290+
}
291+
},
292+
delayThresholdMs,
293+
TimeUnit.MILLISECONDS);
269294
}
270295
}
271296

@@ -281,25 +306,24 @@ public void publishAllOutstanding() {
281306
if (messagesBatch.isEmpty()) {
282307
return;
283308
}
284-
batchToSend = messagesBatch.popOutstandingBatch();
309+
batchToSend = new OutstandingBatch(messagesBatch, batchedBytes);
310+
messagesBatch = new LinkedList<>();
311+
batchedBytes = 0;
285312
} finally {
286313
messagesBatchLock.unlock();
287314
}
288315
publishOutstandingBatch(batchToSend);
289316
}
290317

291-
private ApiFuture<PublishResponse> publishCall(OutstandingBatch outstandingBatch) {
318+
private void publishOutstandingBatch(final OutstandingBatch outstandingBatch) {
292319
PublishRequest.Builder publishRequest = PublishRequest.newBuilder();
293320
publishRequest.setTopic(topicName);
294321
for (OutstandingPublish outstandingPublish : outstandingBatch.outstandingPublishes) {
295322
publishRequest.addMessages(outstandingPublish.message);
296323
}
297324

298-
return publisherStub.publishCallable().futureCall(publishRequest.build());
299-
}
300-
301-
private void publishOutstandingBatch(final OutstandingBatch outstandingBatch) {
302-
ApiFutureCallback<PublishResponse> futureCallback =
325+
ApiFutures.addCallback(
326+
publisherStub.publishCallable().futureCall(publishRequest.build()),
303327
new ApiFutureCallback<PublishResponse>() {
304328
@Override
305329
public void onSuccess(PublishResponse result) {
@@ -338,9 +362,7 @@ public void onFailure(Throwable t) {
338362
messagesWaiter.incrementPendingMessages(-outstandingBatch.size());
339363
}
340364
}
341-
};
342-
343-
ApiFutures.addCallback(publishCall(outstandingBatch), futureCallback, directExecutor());
365+
});
344366
}
345367

346368
private static final class OutstandingBatch {
@@ -356,18 +378,21 @@ private static final class OutstandingBatch {
356378
this.batchSizeBytes = batchSizeBytes;
357379
}
358380

359-
int size() {
381+
public int getAttempt() {
382+
return attempt;
383+
}
384+
385+
public int size() {
360386
return outstandingPublishes.size();
361387
}
362388
}
363389

364390
private static final class OutstandingPublish {
365-
final SettableApiFuture<String> publishResult;
366-
final PubsubMessage message;
367-
final int messageSize;
391+
SettableApiFuture<String> publishResult;
392+
PubsubMessage message;
368393

369-
OutstandingPublish(PubsubMessage message) {
370-
this.publishResult = SettableApiFuture.create();
394+
OutstandingPublish(SettableApiFuture<String> publishResult, PubsubMessage message) {
395+
this.publishResult = publishResult;
371396
this.message = message;
372397
this.messageSize = message.getSerializedSize();
373398
}
@@ -397,10 +422,7 @@ public void shutdown() throws Exception {
397422
currentAlarmFuture.cancel(false);
398423
}
399424
publishAllOutstanding();
400-
for (AutoCloseable closeable : closeables) {
401-
closeable.close();
402-
}
403-
publisherStub.shutdown();
425+
backgroundResources.shutdown();
404426
}
405427

406428
/**
@@ -410,37 +432,7 @@ public void shutdown() throws Exception {
410432
* <p>Call this method to make sure all resources are freed properly.
411433
*/
412434
public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException {
413-
final long startDuration = System.currentTimeMillis();
414-
final long totalDurationMs = TimeUnit.MILLISECONDS.convert(duration, unit);
415-
messagesWaiter.waitNoMessages();
416-
long remainingDuration = getRemainingDuration(startDuration, totalDurationMs);
417-
boolean isAwaited =
418-
remainingDuration < totalDurationMs
419-
? publisherStub.awaitTermination(remainingDuration, TimeUnit.MILLISECONDS)
420-
: false;
421-
if (isAwaited) {
422-
for (AutoCloseable closeable : closeables) {
423-
ExecutorAsBackgroundResource executorAsBackgroundResource =
424-
(ExecutorAsBackgroundResource) closeable;
425-
remainingDuration = getRemainingDuration(startDuration, totalDurationMs);
426-
System.out.println(remainingDuration);
427-
isAwaited =
428-
remainingDuration < totalDurationMs
429-
? executorAsBackgroundResource.awaitTermination(
430-
getRemainingDuration(startDuration, totalDurationMs), TimeUnit.MILLISECONDS)
431-
: false;
432-
if (!isAwaited) {
433-
return false;
434-
}
435-
}
436-
} else {
437-
return false;
438-
}
439-
return true;
440-
}
441-
442-
private long getRemainingDuration(long startDuration, long totalDurationMs) {
443-
return totalDurationMs - (System.currentTimeMillis() - startDuration);
435+
return backgroundResources.awaitTermination(duration, unit);
444436
}
445437

446438
private boolean hasBatchingBytes() {

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

-1
Original file line numberDiff line numberDiff line change
@@ -586,7 +586,6 @@ public void testAwaitTermination() throws Exception {
586586
ApiFuture<String> publishFuture1 = sendTestMessage(publisher, "A");
587587
publisher.shutdown();
588588
assertTrue(publisher.awaitTermination(1, TimeUnit.MINUTES));
589-
assertTrue(publishFuture1.isDone());
590589
}
591590

592591
@Test

0 commit comments

Comments
 (0)