private void stop() { writeLock.lock(); try { if (timeout != null && !timeout.isCancelled()) { timeout.cancel(); } this.clear(); } finally { writeLock.unlock(); } }
@Override public void operationComplete(Future<Void> future) throws Exception { if (future.isSuccess()) { if (ackType == AckType.Individual) { unAckedMessageTracker.remove(msgId); // increment counter by 1 for non-batch msg if (!(messageId instanceof BatchMessageIdImpl)) { stats.incrementNumAcksSent(1); } } else if (ackType == AckType.Cumulative) { stats.incrementNumAcksSent(unAckedMessageTracker.removeMessagesTill(msgId)); } if (log.isDebugEnabled()) { log.debug("[{}] [{}] [{}] Successfully acknowledged message - {}, acktype {}", subscription, topic, consumerName, messageId, ackType); } ackFuture.complete(null); } else { stats.incrementNumAcksFailed(); ackFuture.completeExceptionally(new PulsarClientException(future.cause())); } } });
public UnAckedMessageTracker(PulsarClientImpl client, ConsumerBase consumerBase, long ackTimeoutMillis) { currentSet = new ConcurrentOpenHashSet<MessageIdImpl>(); oldOpenSet = new ConcurrentOpenHashSet<MessageIdImpl>(); readWriteLock = new ReentrantReadWriteLock(); readLock = readWriteLock.readLock(); writeLock = readWriteLock.writeLock(); start(client, consumerBase, ackTimeoutMillis); }
unAckedMessageTracker.add(batchMessage); try { for (int i = 0; i < batchSize; ++i) {
@Override public void close() { stop(); } }
if (getState() == State.Closing || getState() == State.Closed) { batchMessageAckTracker.clear(); unAckedMessageTracker.close(); return CompletableFuture.completedFuture(null); setState(State.Closed); batchMessageAckTracker.clear(); unAckedMessageTracker.close(); client.cleanupConsumer(this); return CompletableFuture.completedFuture(null); setState(State.Closed); batchMessageAckTracker.clear(); unAckedMessageTracker.close(); closeFuture.complete(null); client.cleanupConsumer(this);
@Override public void run(Timeout t) throws Exception { if (isAckTimeout()) { log.warn("[{}] {} messages have timed-out", consumerBase, oldOpenSet.size()); Set<MessageIdImpl> messageIds = new HashSet<>(); oldOpenSet.forEach(messageIds::add); oldOpenSet.clear(); consumerBase.redeliverUnacknowledgedMessages(messageIds); } toggle(); timeout = client.timer().newTimeout(this, ackTimeoutMillis, TimeUnit.MILLISECONDS); } }, ackTimeoutMillis, TimeUnit.MILLISECONDS);
this.unAckedMessageTracker = new UnAckedMessageTracker(client, this, conf.getAckTimeoutMillis()); } else { this.unAckedMessageTracker = UnAckedMessageTracker.UNACKED_MESSAGE_TRACKER_DISABLED;
unAckedMessageTracker.add((MessageIdImpl) message.getMessageId()); boolean asyncReceivedWaiting = !pendingReceives.isEmpty(); if ((conf.getReceiverQueueSize() != 0 || waitingOnReceiveForZeroQueueSize) && !asyncReceivedWaiting) {
public void start(PulsarClientImpl client, ConsumerBase consumerBase, long ackTimeoutMillis) { this.stop(); timeout = client.timer().newTimeout(new TimerTask() { @Override public void run(Timeout t) throws Exception { if (isAckTimeout()) { log.warn("[{}] {} messages have timed-out", consumerBase, oldOpenSet.size()); Set<MessageIdImpl> messageIds = new HashSet<>(); oldOpenSet.forEach(messageIds::add); oldOpenSet.clear(); consumerBase.redeliverUnacknowledgedMessages(messageIds); } toggle(); timeout = client.timer().newTimeout(this, ackTimeoutMillis, TimeUnit.MILLISECONDS); } }, ackTimeoutMillis, TimeUnit.MILLISECONDS); }
@Override public CompletableFuture<Void> unsubscribeAsync() { if (getState() == State.Closing || getState() == State.Closed) { return FutureUtil .failedFuture(new PulsarClientException.AlreadyClosedException("Consumer was already closed")); } final CompletableFuture<Void> unsubscribeFuture = new CompletableFuture<>(); if (isConnected()) { setState(State.Closing); long requestId = client.newRequestId(); ByteBuf unsubscribe = Commands.newUnsubscribe(consumerId, requestId); ClientCnx cnx = cnx(); cnx.sendRequestWithId(unsubscribe, requestId).thenRun(() -> { cnx.removeConsumer(consumerId); log.info("[{}][{}] Successfully unsubscribed from topic", topic, subscription); batchMessageAckTracker.clear(); unAckedMessageTracker.close(); unsubscribeFuture.complete(null); setState(State.Closed); }).exceptionally(e -> { log.error("[{}][{}] Failed to unsubscribe: {}", topic, subscription, e.getCause().getMessage()); unsubscribeFuture.completeExceptionally(e.getCause()); setState(State.Ready); return null; }); } else { unsubscribeFuture.completeExceptionally(new PulsarClientException("Not connected to broker")); } return unsubscribeFuture; }
@Override public void redeliverUnacknowledgedMessages() { ClientCnx cnx = cnx(); if (isConnected() && cnx.getRemoteEndpointProtocolVersion() >= ProtocolVersion.v2.getNumber()) { int currentSize = 0; synchronized (this) { currentSize = incomingMessages.size(); incomingMessages.clear(); unAckedMessageTracker.clear(); batchMessageAckTracker.clear(); } cnx.ctx().writeAndFlush(Commands.newRedeliverUnacknowledgedMessages(consumerId), cnx.ctx().voidPromise()); if (currentSize > 0) { increaseAvailablePermits(cnx, currentSize); } if (log.isDebugEnabled()) { log.debug("[{}] [{}] [{}] Redeliver unacked messages and send {} permits", subscription, topic, consumerName, currentSize); } return; } if (cnx == null || (getState() == State.Connecting)) { log.warn("[{}] Client Connection needs to be establised for redelivery of unacknowledged messages", this); } else { log.warn("[{}] Reconnecting the client to redeliver the messages.", this); cnx.ctx().close(); } }
/** * Record the event that one message has been processed by the application. * * Periodically, it sends a Flow command to notify the broker that it can push more messages */ protected synchronized void messageProcessed(Message msg) { ClientCnx currentCnx = cnx(); ClientCnx msgCnx = ((MessageImpl) msg).getCnx(); lastDequeuedMessage = (MessageIdImpl) msg.getMessageId(); if (msgCnx != currentCnx) { // The processed message did belong to the old queue that was cleared after reconnection. return; } increaseAvailablePermits(currentCnx); stats.updateNumMsgsReceived(msg); if (conf.getAckTimeoutMillis() != 0) { // reset timer for messages that are received by the client MessageIdImpl id = (MessageIdImpl) msg.getMessageId(); if (id instanceof BatchMessageIdImpl) { id = new MessageIdImpl(id.getLedgerId(), id.getEntryId(), getPartitionIndex()); } unAckedMessageTracker.add(id); } }
currentSize = incomingMessages.size(); startMessageId = clearReceiverQueue(); unAckedMessageTracker.clear(); batchMessageAckTracker.clear();