Skip to content

Commit 7cdf8bc

Browse files
fix: Ensure that messages that are in pending batches for an ordering key are canceled when a previous publish for the ordering keys fails (#366)
* feat: Add flow control support to publisher * make suggested fixes * chore: Remove note that ordering keys requires enablements. * feat: Add support for server-side flow control * Revert "chore: Remove note that ordering keys requires enablements." This reverts commit 9c113c3. * fix: Fix import order * fix: Make error message more clear about where ordering must be enabled when publishing. * fix: Ensure that messages that are in pending batches for an ordering key are canceled when a previous publish for the ordering keys fails.
1 parent 68ad21d commit 7cdf8bc

File tree

3 files changed

+75
-0
lines changed

3 files changed

+75
-0
lines changed

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

+20
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ public ApiFuture<String> publish(PubsubMessage message) {
256256
List<OutstandingBatch> batchesToSend;
257257
messagesBatchLock.lock();
258258
try {
259+
if (sequentialExecutor.keyHasError(orderingKey)) {
260+
outstandingPublish.publishResult.setException(
261+
SequentialExecutorService.CallbackExecutor.CANCELLATION_EXCEPTION);
262+
return outstandingPublish.publishResult;
263+
}
259264
MessagesBatch messagesBatch = messagesBatches.get(orderingKey);
260265
if (messagesBatch == null) {
261266
messagesBatch = new MessagesBatch(batchingSettings, orderingKey);
@@ -462,6 +467,21 @@ public void onSuccess(PublishResponse result) {
462467
@Override
463468
public void onFailure(Throwable t) {
464469
try {
470+
if (outstandingBatch.orderingKey != null && !outstandingBatch.orderingKey.isEmpty()) {
471+
messagesBatchLock.lock();
472+
try {
473+
MessagesBatch messagesBatch = messagesBatches.get(outstandingBatch.orderingKey);
474+
if (messagesBatch != null) {
475+
for (OutstandingPublish outstanding : messagesBatch.messages) {
476+
outstanding.publishResult.setException(
477+
SequentialExecutorService.CallbackExecutor.CANCELLATION_EXCEPTION);
478+
}
479+
messagesBatches.remove(outstandingBatch.orderingKey);
480+
}
481+
} finally {
482+
messagesBatchLock.unlock();
483+
}
484+
}
465485
outstandingBatch.onFailure(t);
466486
} finally {
467487
messagesWaiter.incrementPendingCount(-outstandingBatch.size());

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

+4
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,10 @@ public void cancel(Throwable e) {
243243
return future;
244244
}
245245

246+
boolean keyHasError(String key) {
247+
return keysWithErrors.contains(key);
248+
}
249+
246250
void resumePublish(String key) {
247251
keysWithErrors.remove(key);
248252
}

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

+51
Original file line numberDiff line numberDiff line change
@@ -566,6 +566,57 @@ public void testResumePublish() throws Exception {
566566
shutdownTestPublisher(publisher);
567567
}
568568

569+
@Test
570+
public void testPublishThrowExceptionForUnsubmittedOrderingKeyMessage() throws Exception {
571+
Publisher publisher =
572+
getTestPublisherBuilder()
573+
.setExecutorProvider(SINGLE_THREAD_EXECUTOR)
574+
.setBatchingSettings(
575+
Publisher.Builder.DEFAULT_BATCHING_SETTINGS
576+
.toBuilder()
577+
.setElementCountThreshold(2L)
578+
.setDelayThreshold(Duration.ofSeconds(500))
579+
.build())
580+
.setEnableMessageOrdering(true)
581+
.build();
582+
583+
// Send two messages that will fulfill the first batch, which will return a failure.
584+
testPublisherServiceImpl.addPublishError(new StatusException(Status.INVALID_ARGUMENT));
585+
ApiFuture<String> publishFuture1 = sendTestMessageWithOrderingKey(publisher, "A", "a");
586+
ApiFuture<String> publishFuture2 = sendTestMessageWithOrderingKey(publisher, "B", "a");
587+
588+
// A third message will fail because the first attempt to publish failed.
589+
ApiFuture<String> publishFuture3 = sendTestMessageWithOrderingKey(publisher, "C", "a");
590+
591+
try {
592+
publishFuture1.get();
593+
fail("Should have failed.");
594+
} catch (ExecutionException e) {
595+
}
596+
597+
try {
598+
publishFuture2.get();
599+
fail("Should have failed.");
600+
} catch (ExecutionException e) {
601+
}
602+
603+
try {
604+
publishFuture3.get();
605+
fail("Should have failed.");
606+
} catch (ExecutionException e) {
607+
assertEquals(SequentialExecutorService.CallbackExecutor.CANCELLATION_EXCEPTION, e.getCause());
608+
}
609+
610+
// A subsequent attempt fails immediately.
611+
ApiFuture<String> publishFuture4 = sendTestMessageWithOrderingKey(publisher, "D", "a");
612+
try {
613+
publishFuture4.get();
614+
fail("Should have failed.");
615+
} catch (ExecutionException e) {
616+
assertEquals(SequentialExecutorService.CallbackExecutor.CANCELLATION_EXCEPTION, e.getCause());
617+
}
618+
}
619+
569620
private ApiFuture<String> sendTestMessageWithOrderingKey(
570621
Publisher publisher, String data, String orderingKey) {
571622
return publisher.publish(

0 commit comments

Comments
 (0)