001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.activemq.broker.region; 018 019import org.apache.activemq.ActiveMQMessageAudit; 020import org.apache.activemq.broker.Broker; 021import org.apache.activemq.broker.ConnectionContext; 022import org.apache.activemq.broker.region.cursors.FilePendingMessageCursor; 023import org.apache.activemq.broker.region.cursors.PendingMessageCursor; 024import org.apache.activemq.broker.region.cursors.VMPendingMessageCursor; 025import org.apache.activemq.broker.region.policy.MessageEvictionStrategy; 026import org.apache.activemq.broker.region.policy.OldestMessageEvictionStrategy; 027import org.apache.activemq.command.*; 028import org.apache.activemq.thread.Scheduler; 029import org.apache.activemq.transaction.Synchronization; 030import org.apache.activemq.transport.TransmitCallback; 031import org.apache.activemq.usage.SystemUsage; 032import org.slf4j.Logger; 033import org.slf4j.LoggerFactory; 034 035import javax.jms.JMSException; 036import java.io.IOException; 037import java.util.ArrayList; 038import java.util.LinkedList; 039import java.util.List; 040import java.util.concurrent.atomic.AtomicInteger; 041import java.util.concurrent.atomic.AtomicLong; 042 043public class TopicSubscription extends AbstractSubscription { 044 045 private static final Logger LOG = LoggerFactory.getLogger(TopicSubscription.class); 046 private static final AtomicLong CURSOR_NAME_COUNTER = new AtomicLong(0); 047 048 protected PendingMessageCursor matched; 049 protected final SystemUsage usageManager; 050 boolean singleDestination = true; 051 Destination destination; 052 private final Scheduler scheduler; 053 054 private int maximumPendingMessages = -1; 055 private MessageEvictionStrategy messageEvictionStrategy = new OldestMessageEvictionStrategy(); 056 private final AtomicInteger discarded = new AtomicInteger(); 057 private final Object matchedListMutex = new Object(); 058 private int memoryUsageHighWaterMark = 95; 059 // allow duplicate suppression in a ring network of brokers 060 protected int maxProducersToAudit = 1024; 061 protected int maxAuditDepth = 1000; 062 protected boolean enableAudit = false; 063 protected ActiveMQMessageAudit audit; 064 protected boolean active = false; 065 protected boolean discarding = false; 066 private boolean useTopicSubscriptionInflightStats = true; 067 068 //Used for inflight message size calculations 069 protected final Object dispatchLock = new Object(); 070 protected final List<DispatchedNode> dispatched = new ArrayList<>(); 071 072 public TopicSubscription(Broker broker,ConnectionContext context, ConsumerInfo info, SystemUsage usageManager) throws Exception { 073 super(broker, context, info); 074 this.usageManager = usageManager; 075 String matchedName = "TopicSubscription:" + CURSOR_NAME_COUNTER.getAndIncrement() + "[" + info.getConsumerId().toString() + "]"; 076 if (info.getDestination().isTemporary() || broker.getTempDataStore()==null ) { 077 this.matched = new VMPendingMessageCursor(false); 078 } else { 079 this.matched = new FilePendingMessageCursor(broker,matchedName,false); 080 } 081 082 this.scheduler = broker.getScheduler(); 083 } 084 085 public void init() throws Exception { 086 this.matched.setSystemUsage(usageManager); 087 this.matched.setMemoryUsageHighWaterMark(getCursorMemoryHighWaterMark()); 088 this.matched.start(); 089 if (enableAudit) { 090 audit= new ActiveMQMessageAudit(maxAuditDepth, maxProducersToAudit); 091 } 092 this.active=true; 093 } 094 095 @Override 096 public void add(MessageReference node) throws Exception { 097 if (isDuplicate(node)) { 098 return; 099 } 100 // Lets use an indirect reference so that we can associate a unique 101 // locator /w the message. 102 node = new IndirectMessageReference(node.getMessage()); 103 getSubscriptionStatistics().getEnqueues().increment(); 104 synchronized (matchedListMutex) { 105 // if this subscriber is already discarding a message, we don't want to add 106 // any more messages to it as those messages can only be advisories generated in the process, 107 // which can trigger the recursive call loop 108 if (discarding) return; 109 110 if (!isFull() && matched.isEmpty()) { 111 // if maximumPendingMessages is set we will only discard messages which 112 // have not been dispatched (i.e. we allow the prefetch buffer to be filled) 113 dispatch(node); 114 setSlowConsumer(false); 115 } else { 116 if (info.getPrefetchSize() > 1 && matched.size() > info.getPrefetchSize()) { 117 // Slow consumers should log and set their state as such. 118 if (!isSlowConsumer()) { 119 String remoteAddr = null; 120 if (context != null && context.getConnection() != null) { 121 remoteAddr = context.getConnection().getRemoteAddress(); 122 } 123 LOG.warn("{}: has twice its prefetch limit pending, without an ack; it appears to be slow{}", toString(), (remoteAddr != null) ? ": " + remoteAddr : ""); 124 setSlowConsumer(true); 125 for (Destination dest: destinations) { 126 dest.slowConsumer(getContext(), this); 127 } 128 } 129 } 130 if (maximumPendingMessages != 0) { 131 boolean warnedAboutWait = false; 132 while (active) { 133 while (matched.isFull()) { 134 if (getContext().getStopping().get()) { 135 LOG.warn("{}: stopped waiting for space in pendingMessage cursor for: {}", toString(), node.getMessageId()); 136 getSubscriptionStatistics().getEnqueues().decrement(); 137 return; 138 } 139 if (!warnedAboutWait) { 140 LOG.info("{}: Pending message cursor [{}] is full, temp usage ({}%) or memory usage ({}%) limit reached, blocking message add() pending the release of resources.", 141 toString(), 142 matched, 143 matched.getSystemUsage().getTempUsage().getPercentUsage(), 144 matched.getSystemUsage().getMemoryUsage().getPercentUsage()); 145 warnedAboutWait = true; 146 } 147 matchedListMutex.wait(20); 148 } 149 // Temporary storage could be full - so just try to add the message 150 // see https://issues.apache.org/activemq/browse/AMQ-2475 151 if (matched.tryAddMessageLast(node, 10)) { 152 break; 153 } 154 } 155 if (maximumPendingMessages > 0) { 156 // calculate the high water mark from which point we 157 // will eagerly evict expired messages 158 int max = messageEvictionStrategy.getEvictExpiredMessagesHighWatermark(); 159 if (maximumPendingMessages > 0 && maximumPendingMessages < max) { 160 max = maximumPendingMessages; 161 } 162 if (!matched.isEmpty() && matched.size() > max) { 163 removeExpiredMessages(); 164 } 165 // lets discard old messages as we are a slow consumer 166 while (!matched.isEmpty() && matched.size() > maximumPendingMessages) { 167 int pageInSize = matched.size() - maximumPendingMessages; 168 // only page in a 1000 at a time - else we could blow the memory 169 pageInSize = Math.max(1000, pageInSize); 170 LinkedList<MessageReference> list = null; 171 MessageReference[] oldMessages=null; 172 synchronized(matched){ 173 list = matched.pageInList(pageInSize); 174 oldMessages = messageEvictionStrategy.evictMessages(list); 175 for (MessageReference ref : list) { 176 ref.decrementReferenceCount(); 177 } 178 } 179 int messagesToEvict = 0; 180 if (oldMessages != null){ 181 messagesToEvict = oldMessages.length; 182 for (int i = 0; i < messagesToEvict; i++) { 183 MessageReference oldMessage = oldMessages[i]; 184 discard(oldMessage); 185 } 186 } 187 // lets avoid an infinite loop if we are given a bad eviction strategy 188 // for a bad strategy lets just not evict 189 if (messagesToEvict == 0) { 190 LOG.warn("No messages to evict returned for {} from eviction strategy: {} out of {} candidates", 191 destination, messageEvictionStrategy, list.size()); 192 break; 193 } 194 } 195 } 196 dispatchMatched(); 197 } 198 } 199 } 200 } 201 202 private boolean isDuplicate(MessageReference node) { 203 boolean duplicate = false; 204 if (enableAudit && audit != null) { 205 duplicate = audit.isDuplicate(node); 206 if (LOG.isDebugEnabled()) { 207 if (duplicate) { 208 LOG.debug("{}, ignoring duplicate add: {}", this, node.getMessageId()); 209 } 210 } 211 } 212 return duplicate; 213 } 214 215 /** 216 * Discard any expired messages from the matched list. Called from a 217 * synchronized block. 218 * 219 * @throws IOException 220 */ 221 protected void removeExpiredMessages() throws IOException { 222 try { 223 matched.reset(); 224 while (matched.hasNext()) { 225 MessageReference node = matched.next(); 226 node.decrementReferenceCount(); 227 if (node.isExpired()) { 228 matched.remove(); 229 node.decrementReferenceCount(); 230 if (broker.isExpired(node)) { 231 ((Destination) node.getRegionDestination()).getDestinationStatistics().getExpired().increment(); 232 broker.messageExpired(getContext(), node, this); 233 } 234 break; 235 } 236 } 237 } finally { 238 matched.release(); 239 } 240 } 241 242 @Override 243 public void processMessageDispatchNotification(MessageDispatchNotification mdn) { 244 synchronized (matchedListMutex) { 245 try { 246 matched.reset(); 247 while (matched.hasNext()) { 248 MessageReference node = matched.next(); 249 node.decrementReferenceCount(); 250 if (node.getMessageId().equals(mdn.getMessageId())) { 251 synchronized(dispatchLock) { 252 matched.remove(); 253 getSubscriptionStatistics().getDispatched().increment(); 254 if (isUseTopicSubscriptionInflightStats()) { 255 dispatched.add(new DispatchedNode(node)); 256 getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize()); 257 } 258 node.decrementReferenceCount(); 259 } 260 break; 261 } 262 } 263 } finally { 264 matched.release(); 265 } 266 } 267 } 268 269 @Override 270 public synchronized void acknowledge(final ConnectionContext context, final MessageAck ack) throws Exception { 271 super.acknowledge(context, ack); 272 273 if (ack.isStandardAck()) { 274 updateStatsOnAck(context, ack); 275 } else if (ack.isPoisonAck()) { 276 if (ack.isInTransaction()) { 277 throw new JMSException("Poison ack cannot be transacted: " + ack); 278 } 279 updateStatsOnAck(context, ack); 280 contractPrefetchExtension(ack.getMessageCount()); 281 } else if (ack.isIndividualAck()) { 282 updateStatsOnAck(context, ack); 283 if (ack.isInTransaction()) { 284 expandPrefetchExtension(1); 285 } 286 } else if (ack.isExpiredAck()) { 287 updateStatsOnAck(ack); 288 contractPrefetchExtension(ack.getMessageCount()); 289 } else if (ack.isDeliveredAck()) { 290 // Message was delivered but not acknowledged: update pre-fetch counters. 291 expandPrefetchExtension(ack.getMessageCount()); 292 } else if (ack.isRedeliveredAck()) { 293 // No processing for redelivered needed 294 return; 295 } else { 296 throw new JMSException("Invalid acknowledgment: " + ack); 297 } 298 299 dispatchMatched(); 300 } 301 302 private void updateStatsOnAck(final ConnectionContext context, final MessageAck ack) { 303 if (context.isInTransaction()) { 304 context.getTransaction().addSynchronization(new Synchronization() { 305 306 @Override 307 public void afterRollback() { 308 contractPrefetchExtension(ack.getMessageCount()); 309 } 310 311 @Override 312 public void afterCommit() throws Exception { 313 contractPrefetchExtension(ack.getMessageCount()); 314 updateStatsOnAck(ack); 315 dispatchMatched(); 316 } 317 }); 318 } else { 319 updateStatsOnAck(ack); 320 } 321 } 322 323 @Override 324 public Response pullMessage(ConnectionContext context, final MessagePull pull) throws Exception { 325 326 // The slave should not deliver pull messages. 327 if (getPrefetchSize() == 0) { 328 329 final long currentDispatchedCount = getSubscriptionStatistics().getDispatched().getCount(); 330 prefetchExtension.set(pull.getQuantity()); 331 dispatchMatched(); 332 333 // If there was nothing dispatched.. we may need to setup a timeout. 334 if (currentDispatchedCount == getSubscriptionStatistics().getDispatched().getCount() || pull.isAlwaysSignalDone()) { 335 336 // immediate timeout used by receiveNoWait() 337 if (pull.getTimeout() == -1) { 338 // Send a NULL message to signal nothing pending. 339 dispatch(null); 340 prefetchExtension.set(0); 341 } 342 343 if (pull.getTimeout() > 0) { 344 scheduler.executeAfterDelay(new Runnable() { 345 346 @Override 347 public void run() { 348 pullTimeout(currentDispatchedCount, pull.isAlwaysSignalDone()); 349 } 350 }, pull.getTimeout()); 351 } 352 } 353 } 354 return null; 355 } 356 357 /** 358 * Occurs when a pull times out. If nothing has been dispatched since the 359 * timeout was setup, then send the NULL message. 360 */ 361 private final void pullTimeout(long currentDispatchedCount, boolean alwaysSendDone) { 362 synchronized (matchedListMutex) { 363 if (currentDispatchedCount == getSubscriptionStatistics().getDispatched().getCount() || alwaysSendDone) { 364 try { 365 dispatch(null); 366 } catch (Exception e) { 367 context.getConnection().serviceException(e); 368 } finally { 369 prefetchExtension.set(0); 370 } 371 } 372 } 373 } 374 375 /** 376 * Update the statistics on message ack. 377 * @param ack 378 */ 379 private void updateStatsOnAck(final MessageAck ack) { 380 //Allow disabling inflight stats to save memory usage 381 if (isUseTopicSubscriptionInflightStats()) { 382 synchronized(dispatchLock) { 383 boolean inAckRange = false; 384 List<DispatchedNode> removeList = new ArrayList<>(); 385 for (final DispatchedNode node : dispatched) { 386 MessageId messageId = node.getMessageId(); 387 if (ack.getFirstMessageId() == null 388 || ack.getFirstMessageId().equals(messageId)) { 389 inAckRange = true; 390 } 391 if (inAckRange) { 392 removeList.add(node); 393 if (ack.getLastMessageId().equals(messageId)) { 394 break; 395 } 396 } 397 } 398 399 for (final DispatchedNode node : removeList) { 400 dispatched.remove(node); 401 getSubscriptionStatistics().getInflightMessageSize().addSize(-node.getSize()); 402 403 final Destination destination = node.getDestination(); 404 incrementStatsOnAck(destination, ack, 1); 405 if (!ack.isInTransaction()) { 406 contractPrefetchExtension(1); 407 } 408 } 409 } 410 } else { 411 if (singleDestination && destination != null) { 412 incrementStatsOnAck(destination, ack, ack.getMessageCount()); 413 } 414 if (!ack.isInTransaction()) { 415 contractPrefetchExtension(ack.getMessageCount()); 416 } 417 } 418 } 419 420 private void incrementStatsOnAck(final Destination destination, final MessageAck ack, final int count) { 421 getSubscriptionStatistics().getDequeues().add(count); 422 destination.getDestinationStatistics().getDequeues().add(count); 423 destination.getDestinationStatistics().getInflight().subtract(count); 424 if (info.isNetworkSubscription()) { 425 destination.getDestinationStatistics().getForwards().add(count); 426 } 427 if (ack.isExpiredAck()) { 428 destination.getDestinationStatistics().getExpired().add(count); 429 } 430 } 431 432 @Override 433 public int countBeforeFull() { 434 return getPrefetchSize() == 0 ? prefetchExtension.get() : info.getPrefetchSize() + prefetchExtension.get() - getDispatchedQueueSize(); 435 } 436 437 @Override 438 public int getPendingQueueSize() { 439 return matched(); 440 } 441 442 @Override 443 public long getPendingMessageSize() { 444 return matched.messageSize(); 445 } 446 447 @Override 448 public int getDispatchedQueueSize() { 449 return (int)(getSubscriptionStatistics().getDispatched().getCount() - 450 getSubscriptionStatistics().getDequeues().getCount()); 451 } 452 453 public int getMaximumPendingMessages() { 454 return maximumPendingMessages; 455 } 456 457 @Override 458 public long getDispatchedCounter() { 459 return getSubscriptionStatistics().getDispatched().getCount(); 460 } 461 462 @Override 463 public long getEnqueueCounter() { 464 return getSubscriptionStatistics().getEnqueues().getCount(); 465 } 466 467 @Override 468 public long getDequeueCounter() { 469 return getSubscriptionStatistics().getDequeues().getCount(); 470 } 471 472 /** 473 * @return the number of messages discarded due to being a slow consumer 474 */ 475 public int discarded() { 476 return discarded.get(); 477 } 478 479 /** 480 * @return the number of matched messages (messages targeted for the 481 * subscription but not yet able to be dispatched due to the 482 * prefetch buffer being full). 483 */ 484 public int matched() { 485 return matched.size(); 486 } 487 488 /** 489 * Sets the maximum number of pending messages that can be matched against 490 * this consumer before old messages are discarded. 491 */ 492 public void setMaximumPendingMessages(int maximumPendingMessages) { 493 this.maximumPendingMessages = maximumPendingMessages; 494 } 495 496 public MessageEvictionStrategy getMessageEvictionStrategy() { 497 return messageEvictionStrategy; 498 } 499 500 /** 501 * Sets the eviction strategy used to decide which message to evict when the 502 * slow consumer needs to discard messages 503 */ 504 public void setMessageEvictionStrategy(MessageEvictionStrategy messageEvictionStrategy) { 505 this.messageEvictionStrategy = messageEvictionStrategy; 506 } 507 508 public synchronized int getMaxProducersToAudit() { 509 return maxProducersToAudit; 510 } 511 512 public synchronized void setMaxProducersToAudit(int maxProducersToAudit) { 513 this.maxProducersToAudit = maxProducersToAudit; 514 if (audit != null) { 515 audit.setMaximumNumberOfProducersToTrack(maxProducersToAudit); 516 } 517 } 518 519 public synchronized int getMaxAuditDepth() { 520 return maxAuditDepth; 521 } 522 523 public synchronized void setMaxAuditDepth(int maxAuditDepth) { 524 this.maxAuditDepth = maxAuditDepth; 525 if (audit != null) { 526 audit.setAuditDepth(maxAuditDepth); 527 } 528 } 529 530 public boolean isEnableAudit() { 531 return enableAudit; 532 } 533 534 public synchronized void setEnableAudit(boolean enableAudit) { 535 this.enableAudit = enableAudit; 536 if (enableAudit && audit == null) { 537 audit = new ActiveMQMessageAudit(maxAuditDepth,maxProducersToAudit); 538 } 539 } 540 541 // Implementation methods 542 // ------------------------------------------------------------------------- 543 @Override 544 public boolean isFull() { 545 return getPrefetchSize() == 0 ? prefetchExtension.get() == 0 : getDispatchedQueueSize() - prefetchExtension.get() >= info.getPrefetchSize(); 546 } 547 548 @Override 549 public int getInFlightSize() { 550 return getDispatchedQueueSize(); 551 } 552 553 /** 554 * @return true when 60% or more room is left for dispatching messages 555 */ 556 @Override 557 public boolean isLowWaterMark() { 558 return (getDispatchedQueueSize() - prefetchExtension.get()) <= (info.getPrefetchSize() * .4); 559 } 560 561 /** 562 * @return true when 10% or less room is left for dispatching messages 563 */ 564 @Override 565 public boolean isHighWaterMark() { 566 return (getDispatchedQueueSize() - prefetchExtension.get()) >= (info.getPrefetchSize() * .9); 567 } 568 569 /** 570 * @param memoryUsageHighWaterMark the memoryUsageHighWaterMark to set 571 */ 572 public void setMemoryUsageHighWaterMark(int memoryUsageHighWaterMark) { 573 this.memoryUsageHighWaterMark = memoryUsageHighWaterMark; 574 } 575 576 /** 577 * @return the memoryUsageHighWaterMark 578 */ 579 public int getMemoryUsageHighWaterMark() { 580 return this.memoryUsageHighWaterMark; 581 } 582 583 /** 584 * @return the usageManager 585 */ 586 public SystemUsage getUsageManager() { 587 return this.usageManager; 588 } 589 590 /** 591 * @return the matched 592 */ 593 public PendingMessageCursor getMatched() { 594 return this.matched; 595 } 596 597 /** 598 * @param matched the matched to set 599 */ 600 public void setMatched(PendingMessageCursor matched) { 601 this.matched = matched; 602 } 603 604 /** 605 * inform the MessageConsumer on the client to change it's prefetch 606 * 607 * @param newPrefetch 608 */ 609 @Override 610 public void updateConsumerPrefetch(int newPrefetch) { 611 if (context != null && context.getConnection() != null && context.getConnection().isManageable()) { 612 ConsumerControl cc = new ConsumerControl(); 613 cc.setConsumerId(info.getConsumerId()); 614 cc.setPrefetch(newPrefetch); 615 context.getConnection().dispatchAsync(cc); 616 } 617 } 618 619 private void dispatchMatched() throws IOException { 620 synchronized (matchedListMutex) { 621 if (!matched.isEmpty() && !isFull()) { 622 try { 623 matched.reset(); 624 625 while (matched.hasNext() && !isFull()) { 626 MessageReference message = matched.next(); 627 message.decrementReferenceCount(); 628 matched.remove(); 629 // Message may have been sitting in the matched list a while 630 // waiting for the consumer to ak the message. 631 if (message.isExpired()) { 632 discard(message); 633 continue; // just drop it. 634 } 635 dispatch(message); 636 } 637 } finally { 638 matched.release(); 639 } 640 } 641 } 642 } 643 644 private void dispatch(final MessageReference node) throws IOException { 645 Message message = node != null ? node.getMessage() : null; 646 if (node != null) { 647 node.incrementReferenceCount(); 648 } 649 // Make sure we can dispatch a message. 650 MessageDispatch md = new MessageDispatch(); 651 md.setMessage(message); 652 md.setConsumerId(info.getConsumerId()); 653 if (node != null) { 654 md.setDestination(((Destination)node.getRegionDestination()).getActiveMQDestination()); 655 synchronized(dispatchLock) { 656 getSubscriptionStatistics().getDispatched().increment(); 657 if (isUseTopicSubscriptionInflightStats()) { 658 dispatched.add(new DispatchedNode(node)); 659 getSubscriptionStatistics().getInflightMessageSize().addSize(node.getSize()); 660 } 661 } 662 663 // Keep track if this subscription is receiving messages from a single destination. 664 if (singleDestination) { 665 if (destination == null) { 666 destination = (Destination)node.getRegionDestination(); 667 } else { 668 if (destination != node.getRegionDestination()) { 669 singleDestination = false; 670 } 671 } 672 } 673 674 if (getPrefetchSize() == 0) { 675 decrementPrefetchExtension(1); 676 } 677 } 678 679 if (info.isDispatchAsync()) { 680 if (node != null) { 681 md.setTransmitCallback(new TransmitCallback() { 682 683 @Override 684 public void onSuccess() { 685 Destination regionDestination = (Destination) node.getRegionDestination(); 686 regionDestination.getDestinationStatistics().getDispatched().increment(); 687 regionDestination.getDestinationStatistics().getInflight().increment(); 688 node.decrementReferenceCount(); 689 } 690 691 @Override 692 public void onFailure() { 693 Destination regionDestination = (Destination) node.getRegionDestination(); 694 regionDestination.getDestinationStatistics().getDispatched().increment(); 695 regionDestination.getDestinationStatistics().getInflight().increment(); 696 node.decrementReferenceCount(); 697 } 698 }); 699 } 700 context.getConnection().dispatchAsync(md); 701 } else { 702 context.getConnection().dispatchSync(md); 703 if (node != null) { 704 Destination regionDestination = (Destination) node.getRegionDestination(); 705 regionDestination.getDestinationStatistics().getDispatched().increment(); 706 regionDestination.getDestinationStatistics().getInflight().increment(); 707 node.decrementReferenceCount(); 708 } 709 } 710 } 711 712 private void discard(MessageReference message) { 713 discarding = true; 714 try { 715 message.decrementReferenceCount(); 716 matched.remove(message); 717 discarded.incrementAndGet(); 718 if (destination != null) { 719 destination.getDestinationStatistics().getDequeues().increment(); 720 } 721 LOG.debug("{}, discarding message {}", this, message); 722 Destination dest = (Destination) message.getRegionDestination(); 723 if (dest != null) { 724 dest.messageDiscarded(getContext(), this, message); 725 } 726 broker.getRoot().sendToDeadLetterQueue(getContext(), message, this, new Throwable("TopicSubDiscard. ID:" + info.getConsumerId())); 727 } finally { 728 discarding = false; 729 } 730 } 731 732 @Override 733 public String toString() { 734 return "TopicSubscription:" + " consumer=" + info.getConsumerId() + ", destinations=" + destinations.size() + ", dispatched=" + getDispatchedQueueSize() + ", delivered=" 735 + getDequeueCounter() + ", matched=" + matched() + ", discarded=" + discarded() + ", prefetchExtension=" + prefetchExtension.get() 736 + ", usePrefetchExtension=" + isUsePrefetchExtension(); 737 } 738 739 @Override 740 public void destroy() { 741 this.active=false; 742 synchronized (matchedListMutex) { 743 try { 744 matched.destroy(); 745 } catch (Exception e) { 746 LOG.warn("Failed to destroy cursor", e); 747 } 748 } 749 setSlowConsumer(false); 750 synchronized(dispatchLock) { 751 dispatched.clear(); 752 } 753 } 754 755 @Override 756 public int getPrefetchSize() { 757 return info.getPrefetchSize(); 758 } 759 760 @Override 761 public void setPrefetchSize(int newSize) { 762 info.setPrefetchSize(newSize); 763 try { 764 dispatchMatched(); 765 } catch(Exception e) { 766 LOG.trace("Caught exception on dispatch after prefetch size change."); 767 } 768 } 769 770 public boolean isUseTopicSubscriptionInflightStats() { 771 return useTopicSubscriptionInflightStats; 772 } 773 774 public void setUseTopicSubscriptionInflightStats(boolean useTopicSubscriptionInflightStats) { 775 this.useTopicSubscriptionInflightStats = useTopicSubscriptionInflightStats; 776 } 777 778 private static class DispatchedNode { 779 private final int size; 780 private final MessageId messageId; 781 private final Destination destination; 782 783 public DispatchedNode(final MessageReference node) { 784 super(); 785 this.size = node.getSize(); 786 this.messageId = node.getMessageId(); 787 this.destination = node.getRegionDestination() instanceof Destination ? 788 ((Destination)node.getRegionDestination()) : null; 789 } 790 791 public long getSize() { 792 return size; 793 } 794 795 public MessageId getMessageId() { 796 return messageId; 797 } 798 799 public Destination getDestination() { 800 return destination; 801 } 802 } 803 804}