Skip to content

[ISSUE #10929] Add LmqPrefixIndex to accelerate wildcard dispatch and refine lite subscription model - #10930

Open
f1amingo wants to merge 2 commits into
apache:developfrom
f1amingo:feat/lmq-prefix-index
Open

[ISSUE #10929] Add LmqPrefixIndex to accelerate wildcard dispatch and refine lite subscription model#10930
f1amingo wants to merge 2 commits into
apache:developfrom
f1amingo:feat/lmq-prefix-index

Conversation

@f1amingo

Copy link
Copy Markdown
Contributor

Which Issue(s) This PR Fixes

Brief Description

Several lite-topic hot paths (doFullDispatchForWildcardGroup, getLiteTopicCount, collectByParentTopic, cleanByParentTopic) iterate the entire consume-queue table on every invocation, so their cost grows with the total number of lmqs on the broker instead of the lmqs under the target parent topic.

This PR adds LmqPrefixIndex, a prefix-ordered in-memory index over lmq names:

  • Index lifecycle: bootstrapped once at startup (bootstrapLmqPrefixIndex after lifecycle init), then maintained on the lmq hot path via onLmqCreate (first message of a new lmq) and onLmqDelete hooks.
  • Index-backed iteration: new forEachLiteTopicByPrefix / forEachLiteTopicByParent APIs; wildcard full dispatch, topic count and cleanup are rewritten on top of them, turning O(total lmqs) scans into O(matched lmqs). Cleanup switches to collect-then-delete to avoid lock nesting between iteration and deletion.
  • Subscription model refinement: replace SubscriberWrapper with a plain Map<String, List<ClientGroup>> return from getAllSubscribers (exact + wildcard sources), simplify getWildcardGroupClients signature, rename getLiteTopicSet to getLmqSet, and make LiteCtlListener callbacks default methods.

No protocol or external API change; dispatch and cleanup behavior stays equivalent.

How Did You Test This Change?

  • New unit tests LmqPrefixIndexTest covering prefix lookup boundaries, bootstrap and create/delete hooks
  • Extended AbstractLiteLifecycleManagerTest / LiteEventDispatcherTest for index-backed dispatch, count and cleanup paths
  • Rewritten LiteSubscriptionRegistryImplTest and new LiteSubscriptionTest for the subscription model refactoring
  • Full regression on the touched modules: mvn -pl broker -am test related suites (128+ tests) plus common/proxy lite suites, all green; mvn checkstyle clean

…ch and refine lite subscription model

- Add LmqPrefixIndex, a prefix-ordered in-memory index over lmq names, bootstrapped at startup and maintained via onLmqCreate/onLmqDelete hooks
- Rewrite wildcard full dispatch, getLiteTopicCount, collectByParentTopic and cleanByParentTopic on index-backed forEachLiteTopicByPrefix/ByParent, turning O(total lmqs) scans into O(matched lmqs)
- Refine lite subscription model: replace SubscriberWrapper with Map-based getAllSubscribers, simplify getWildcardGroupClients, rename getLiteTopicSet to getLmqSet, make LiteCtlListener callbacks default methods
@f1amingo
f1amingo force-pushed the feat/lmq-prefix-index branch from d571cb1 to 11f5473 Compare August 14, 2026 06:19

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR modifies 24 file(s) (2982 lines of diff).

Changed Files

WORKSPACE
broker/BUILD.bazel
broker/pom.xml
broker/src/main/java/org/apache/rocketmq/broker/BrokerController.java
broker/src/main/java/org/apache/rocketmq/broker/lite/AbstractLiteLifecycleManager.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LiteCtlListener.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LiteEventDispatcher.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LiteLifecycleManager.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LiteSubscriptionRegistry.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LiteSubscriptionRegistryImpl.java
broker/src/main/java/org/apache/rocketmq/broker/lite/LmqPrefixIndex.java
broker/src/main/java/org/apache/rocketmq/broker/lite/RocksDBLiteLifecycleManager.java
broker/src/main/java/org/apache/rocketmq/broker/lite/SubscriberWrapper.java
broker/src/main/java/org/apache/rocketmq/broker/processor/LiteManagerProcessor.java
broker/src/test/java/org/apache/rocketmq/broker/lite/AbstractLiteLifecycleManagerTest.java
broker/src/test/java/org/apache/rocketmq/broker/lite/LiteEventDispatcherTest.java
broker/src/test/java/org/apache/rocketmq/broker/lite/LiteLifecycleManagerTest.java
broker/src/test/java/org/apache/rocketmq/broker/lite/LiteSubscriptionRegistryImplTest.java
broker/src/test/java/org/apache/rocketmq/broker/lite/LmqPrefixIndexTest.java
broker/src/test/java/org/apache/rocketmq/broker/lite/RocksDBLiteLifecycleManagerTest.java
broker/src/test/java/org/apache/rocketmq/broker/processor/LiteManagerProcessorTest.java
common/src/main/java/org/apache/rocketmq/common/lite/LiteSubscription.java
common/src/test/java/org/apache/rocketmq/common/lite/LiteSubscriptionTest.java
pom.xml

Automated review by RockteMQ-AI

@codecov-commenter

codecov-commenter commented Aug 14, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 81.81818% with 36 lines in your changes missing coverage. Please review.
✅ Project coverage is 48.57%. Comparing base (293f588) to head (6cc2160).

Files with missing lines Patch % Lines
...etmq/broker/lite/LiteSubscriptionRegistryImpl.java 80.82% 7 Missing and 7 partials ⚠️
...etmq/broker/lite/AbstractLiteLifecycleManager.java 83.33% 6 Missing and 3 partials ⚠️
...ache/rocketmq/broker/lite/LiteEventDispatcher.java 66.66% 2 Missing and 5 partials ⚠️
...g/apache/rocketmq/broker/lite/LiteCtlListener.java 0.00% 3 Missing ⚠️
...a/org/apache/rocketmq/broker/BrokerController.java 50.00% 1 Missing and 1 partial ⚠️
...ocketmq/broker/processor/LiteManagerProcessor.java 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@              Coverage Diff              @@
##             develop   #10930      +/-   ##
=============================================
- Coverage      48.60%   48.57%   -0.04%     
- Complexity     13690    13696       +6     
=============================================
  Files           1381     1382       +1     
  Lines         101464   101461       -3     
  Branches       13187    13183       -4     
=============================================
- Hits           49318    49285      -33     
- Misses         46158    46162       +4     
- Partials        5988     6014      +26     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@RockteMQ-AI RockteMQ-AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Summary

This PR introduces LmqPrefixIndex, a PatriciaTrie-based prefix index over lmq names to accelerate wildcard dispatch and subscription queries. The change replaces O(n) scans with O(log n) prefix lookups, which is a significant performance improvement for brokers with many lmqs.

Overall: Good design with proper thread safety and comprehensive tests. A few minor concerns noted below.

Key Changes

  • LmqPrefixIndex: New PatriciaTrie-backed index with ReadWriteLock for thread-safe prefix queries
  • Lifecycle hooks: Added onLmqCreate()/onLmqDelete() to maintain the index incrementally
  • Bootstrap: Populates the index once at startup via bootstrapLmqPrefixIndex()
  • Refactoring: Renamed liteTopiclmq throughout for consistency, simplified SubscriberWrapper to direct Map returns

Findings

  • [Warning] LiteSubscription.java:32 — Removal of automatic updateTime() requires all callers to explicitly call touch()
  • [Info] AbstractLiteLifecycleManager.java:82 — Bootstrap should be called after full initialization

Suggestions

  1. Consider adding a unit test for concurrent add()/remove()/prefixLookup() operations on LmqPrefixIndex to verify the ReadWriteLock behavior under load
  2. The dispatch() method calls onLmqCreate() before doDispatch() — this is correct, but worth documenting that the subscription registry update happens asynchronously

Cross-repo Note

This change affects the lite subscription protocol. If there are corresponding changes needed in rocketmq-clients for the wildcard subscription feature, please coordinate the release.


Automated review by github-manager

updateTime();
this.liteTopicSet.addAll(set);
public LiteSubscription touch() {
this.updateTime = System.currentTimeMillis();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Warning] The removal of automatic updateTime() from addLmq()/removeLmq() and requiring explicit touch() is a behavioral change. Please verify all callers remember to call touch() after modifying the lmq set, otherwise the subscription may be incorrectly evicted due to stale updateTime.

*/
public abstract long getMaxOffsetInQueue(String lmqName);
public void bootstrapLmqPrefixIndex() {
long start = System.currentTimeMillis();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Info] bootstrapLmqPrefixIndex() iterates all lmqs at startup. Ensure this is called after the broker is fully initialized and no concurrent lmq create/delete operations are in progress, otherwise the index may be inconsistent. Consider adding a comment about the expected call order.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Enhancement] Lite wildcard dispatch and lifecycle queries get slower as lmq count grows due to full CQ-table scans

3 participants