@@ -78,7 +78,8 @@ ConsumerImpl::ConsumerImpl(const ClientImplPtr client, const std::string& topic,
7878 readCompacted_(conf.isReadCompacted()),
7979 startMessageId_(startMessageId),
8080 maxPendingChunkedMessage_(conf.getMaxPendingChunkedMessage()),
81- autoAckOldestChunkedMessageOnQueueFull_(conf.isAutoAckOldestChunkedMessageOnQueueFull()) {
81+ autoAckOldestChunkedMessageOnQueueFull_(conf.isAutoAckOldestChunkedMessageOnQueueFull()),
82+ expireTimeOfIncompleteChunkedMessageMs_(conf.getExpireTimeOfIncompleteChunkedMessageMs()) {
8283 std::stringstream consumerStrStream;
8384 consumerStrStream << " [" << topic_ << " , " << subscription_ << " , " << consumerId_ << " ] " ;
8485 consumerStr_ = consumerStrStream.str ();
@@ -109,6 +110,8 @@ ConsumerImpl::ConsumerImpl(const ClientImplPtr client, const std::string& topic,
109110 if (conf.isEncryptionEnabled ()) {
110111 msgCrypto_ = std::make_shared<MessageCrypto>(consumerStr_, false );
111112 }
113+
114+ checkExpiredChunkedTimer_ = executor_->createDeadlineTimer ();
112115}
113116
114117ConsumerImpl::~ConsumerImpl () {
@@ -319,6 +322,45 @@ void ConsumerImpl::unsubscribeAsync(ResultCallback originalCallback) {
319322 }
320323}
321324
325+ void ConsumerImpl::triggerCheckExpiredChunkedTimer () {
326+ checkExpiredChunkedTimer_->expires_from_now (
327+ boost::posix_time::milliseconds (expireTimeOfIncompleteChunkedMessageMs_));
328+ std::weak_ptr<ConsumerImplBase> weakSelf{shared_from_this ()};
329+ checkExpiredChunkedTimer_->async_wait ([this , weakSelf](const boost::system::error_code& ec) -> void {
330+ auto self = weakSelf.lock ();
331+ if (!self) {
332+ return ;
333+ }
334+ if (ec) {
335+ LOG_DEBUG (getName () << " Check expired chunked messages was failed or cancelled, code[" << ec
336+ << " ]." );
337+ return ;
338+ }
339+ Lock lock (chunkProcessMutex_);
340+ long currentTimeMs = TimeUtils::currentTimeMillis ();
341+ chunkedMessageCache_.removeOldestValuesIf (
342+ [this , currentTimeMs](const std::string& uuid, const ChunkedMessageCtx& ctx) -> bool {
343+ bool expired =
344+ currentTimeMs > ctx.getReceivedTimeMs () + expireTimeOfIncompleteChunkedMessageMs_;
345+ if (!expired) {
346+ return false ;
347+ }
348+ for (const MessageId& msgId : ctx.getChunkedMessageIds ()) {
349+ LOG_INFO (" Removing expired chunk messages: uuid: " << uuid << " , messageId: " << msgId);
350+ doAcknowledgeIndividual (msgId, [uuid, msgId](Result result) {
351+ if (result != ResultOk) {
352+ LOG_WARN (" Failed to acknowledge discarded chunk, uuid: "
353+ << uuid << " , messageId: " << msgId);
354+ }
355+ });
356+ }
357+ return true ;
358+ });
359+ triggerCheckExpiredChunkedTimer ();
360+ return ;
361+ });
362+ }
363+
322364Optional<SharedBuffer> ConsumerImpl::processMessageChunk (const SharedBuffer& payload,
323365 const proto::MessageMetadata& metadata,
324366 const MessageId& messageId,
@@ -331,6 +373,14 @@ Optional<SharedBuffer> ConsumerImpl::processMessageChunk(const SharedBuffer& pay
331373 << payload.readableBytes () << " bytes" );
332374
333375 Lock lock (chunkProcessMutex_);
376+
377+ // Lazy task scheduling to expire incomplete chunk message
378+ bool expected = false ;
379+ if (expireTimeOfIncompleteChunkedMessageMs_ > 0 &&
380+ expireChunkMessageTaskScheduled_.compare_exchange_strong (expected, true )) {
381+ triggerCheckExpiredChunkedTimer ();
382+ }
383+
334384 auto it = chunkedMessageCache_.find (uuid);
335385
336386 if (chunkId == 0 ) {
@@ -1448,6 +1498,7 @@ std::shared_ptr<ConsumerImpl> ConsumerImpl::get_shared_this_ptr() {
14481498void ConsumerImpl::cancelTimers () noexcept {
14491499 boost::system::error_code ec;
14501500 batchReceiveTimer_->cancel (ec);
1501+ checkExpiredChunkedTimer_->cancel (ec);
14511502}
14521503
14531504} /* namespace pulsar */
0 commit comments