/** Send a batch of messages. */ private int sendBatch() { final List<Message> batch = new ArrayList<>(); final List<CompletableFuture<String>> futures = new ArrayList<>(); // Drain queue up to batch size while (batch.size() < batchSize) { final QueuedMessage message = queue.poll(); if (message == null) { break; } batch.add(message.message); futures.add(message.future); } // Was there anything to send? if (batch.size() == 0) { return 0; } // Decrement the queue size counter size.updateAndGet(i -> i - batch.size()); // Send the batch request and increment the outstanding request counter outstanding.incrementAndGet(); final PubsubFuture<List<String>> batchFuture = pubsub.publish(project, topic, batch); listener.sendingBatch(Publisher.this, topic, unmodifiableList(batch), batchFuture); batchFuture .whenComplete( (List<String> messageIds, Throwable ex) -> { // Decrement the outstanding request counter outstanding.decrementAndGet(); // Fail all futures if the batch request failed if (ex != null) { futures.forEach(f -> f.completeExceptionally(ex)); return; } // Verify that the number of message id's and messages match up if (futures.size() != messageIds.size()) { futures.forEach( f -> f.completeExceptionally( new PubsubException( "message id count mismatch: " + futures.size() + " != " + messageIds.size()))); } // Complete each future with the appropriate message id for (int i = 0; i < futures.size(); i++) { final String messageId = messageIds.get(i); final CompletableFuture<String> future = futures.get(i); future.complete(messageId); } }) // When batch is complete, process pending topics. .whenComplete((v, t) -> sendPending()); return batch.size(); }