Before Creating the Enhancement Request
Summary
Support wildcard pattern matching for LiteTopic consumer subscriptions, enabling hierarchical topic organization and fine-grained subscriber-side filtering.
Motivation
PR #10204 introduced Lite wildcard groups, but the current implementation has several limitations:
-
No consumer-side pattern filtering — A wildcard consumer group receives all LiteTopics under the parent topic. Consumers cannot express which subset they want; they always get everything.
-
Full LMQ scan on every dispatch cycle — doFullDispatchForWildcardGroup calls forEachLiteTopic() which scans the entire ConsumeQueue table (O(N) over all LMQs), not just the topics under the bound parent topic.
-
No hierarchical topic organization — LiteTopic names are flat strings with no way to express logical grouping (e.g., "all payment-related subtopics").
This makes it difficult to build systems where different consumers within the same group handle different subsets of events.
Describe the Solution You'd Like
Wildcard pattern matching for LiteTopic consumer subscriptions, inspired by NATS subject matching.
Key Design Decisions
Separator: __ (double underscore)
. is already used in RocketMQ topic names (e.g., %LMQ%$parent$child) and in ConsumeQueue file paths
__ is visually distinctive, rarely appears in topic names, and avoids conflicts
Pattern Tokens
| Token |
Meaning |
Position |
NATS Equivalent |
* |
Matches exactly one __-delimited segment |
Anywhere |
* |
** |
Matches one or more segments |
Must be at the end |
> |
Examples (parent topic = order_events)
| Pattern |
Matches |
Does Not Match |
pay__refund |
pay__refund |
pay__refund__notify, notify__refund |
pay__* |
pay__refund, pay__success |
pay__refund__notify |
*__refund |
pay__refund, notify__refund |
pay__refund__notify |
pay__*__notify |
pay__refund__notify, pay__success__notify |
pay__refund |
** |
pay, pay__refund, pay__refund__notify |
(matches all) |
pay__** |
pay__refund, pay__refund__notify |
notify__refund |
Implementation Plan
Core matching engine: LitePatternMatcher in common module, providing three static methods:
matches(pattern, liteTopic) — recursive segment-by-segment matching, no regex
expand(pattern, candidates) — expand a pattern against a set of candidate topics
validate(pattern) — validate pattern syntax
Two-phase subscription expansion:
- Eager expansion at
COMPLETE_ADD time — expand all patterns against existing LiteTopics under the parent topic, register matched topics to liteTopic2Group
- Lazy re-expansion when new LiteTopics are created via
onRegister() — check if the new topic matches any client's patterns, add to subscription if so
Dispatch optimization: Replace full LMQ scan (forEachLiteTopic, O(N)) with targeted iteration via collectByParentTopic (O(M), where M = topics under the parent topic).
Multi-pattern support (v1): liteTopicSet is a Set<String>, so multiple patterns can be sent in a single COMPLETE_ADD request. The union of all matched topics becomes the client's effective subscription.
Protocol compatibility: Reuse LiteSubscriptionDTO.liteTopicSet to carry pattern strings for wildcard groups. No wire protocol changes needed — the broker distinguishes via isWildcardGroup().
Consumer API Example
DefaultLitePullConsumer consumer = new DefaultLitePullConsumer("order_consumer_group");
consumer.setLiteBindTopic("order_events");
consumer.subscribeLite("pay__*"); // single-level payment events
consumer.subscribeLite("notify__**"); // all notification events (multi-level)
Key Changes
| Component |
Change |
LitePatternMatcher (new) |
Core matching engine in common |
LiteSubscription |
+ wildcardPatterns: Set<String> |
LiteSubscriptionRegistryImpl |
Multi-pattern expansion in addCompleteSubscription |
LiteEventDispatcher |
Lazy pattern re-expansion in onRegister; dispatch optimization |
Describe Alternatives You've Considered
| Alternative |
Why Rejected |
| Store compiled Trie per wildcard group |
Over-engineering for v1; String.split + recursive matching is fast enough for < 10K topics |
Support ** in the middle of a pattern (e.g., order__**__refund) |
NATS does not support this; greedy vs. non-greedy makes matching ambiguous; deferred to v2 |
Use . as separator |
Conflicts with existing topic naming conventions in RocketMQ |
| Per-message pattern matching (no eager expansion) |
Would require checking patterns on every doDispatch call; eager expansion is more efficient |
Add a dedicated pattern field to LiteSubscriptionDTO |
Unnecessary; reusing liteTopicSet is simpler and avoids wire protocol changes |
Additional Context
- Inspired by NATS subject-based addressing and MQTT topic filters (with
+ / #), but using __ separator to avoid conflicts with RocketMQ's existing naming conventions
- Aligned with FlowMQ's "unified topic routing" philosophy — one topic namespace, multiple protocol bindings, unified wildcard matching
- Performance target:
matches() < 5μs per call, expand() < 10ms for 1,000 candidates
- Full backward compatibility: wildcard groups without patterns continue to receive all LiteTopics under the parent topic (existing behavior)
- Registers via
LiteSubscriptionCtlRequestBody without any wire protocol changes
- Reuses the existing
collectByParentTopic API for dispatch optimization
Before Creating the Enhancement Request
Summary
Support wildcard pattern matching for LiteTopic consumer subscriptions, enabling hierarchical topic organization and fine-grained subscriber-side filtering.
Motivation
PR #10204 introduced Lite wildcard groups, but the current implementation has several limitations:
No consumer-side pattern filtering — A wildcard consumer group receives all LiteTopics under the parent topic. Consumers cannot express which subset they want; they always get everything.
Full LMQ scan on every dispatch cycle —
doFullDispatchForWildcardGroupcallsforEachLiteTopic()which scans the entire ConsumeQueue table (O(N) over all LMQs), not just the topics under the bound parent topic.No hierarchical topic organization — LiteTopic names are flat strings with no way to express logical grouping (e.g., "all payment-related subtopics").
This makes it difficult to build systems where different consumers within the same group handle different subsets of events.
Describe the Solution You'd Like
Wildcard pattern matching for LiteTopic consumer subscriptions, inspired by NATS subject matching.
Key Design Decisions
Separator:
__(double underscore).is already used in RocketMQ topic names (e.g.,%LMQ%$parent$child) and in ConsumeQueue file paths__is visually distinctive, rarely appears in topic names, and avoids conflictsPattern Tokens
*__-delimited segment***>Examples (parent topic =
order_events)pay__refundpay__refundpay__refund__notify,notify__refundpay__*pay__refund,pay__successpay__refund__notify*__refundpay__refund,notify__refundpay__refund__notifypay__*__notifypay__refund__notify,pay__success__notifypay__refund**pay,pay__refund,pay__refund__notifypay__**pay__refund,pay__refund__notifynotify__refundImplementation Plan
Core matching engine:
LitePatternMatcherincommonmodule, providing three static methods:matches(pattern, liteTopic)— recursive segment-by-segment matching, no regexexpand(pattern, candidates)— expand a pattern against a set of candidate topicsvalidate(pattern)— validate pattern syntaxTwo-phase subscription expansion:
COMPLETE_ADDtime — expand all patterns against existing LiteTopics under the parent topic, register matched topics toliteTopic2GrouponRegister()— check if the new topic matches any client's patterns, add to subscription if soDispatch optimization: Replace full LMQ scan (
forEachLiteTopic, O(N)) with targeted iteration viacollectByParentTopic(O(M), where M = topics under the parent topic).Multi-pattern support (v1):
liteTopicSetis aSet<String>, so multiple patterns can be sent in a singleCOMPLETE_ADDrequest. The union of all matched topics becomes the client's effective subscription.Protocol compatibility: Reuse
LiteSubscriptionDTO.liteTopicSetto carry pattern strings for wildcard groups. No wire protocol changes needed — the broker distinguishes viaisWildcardGroup().Consumer API Example
Key Changes
LitePatternMatcher(new)commonLiteSubscriptionwildcardPatterns: Set<String>LiteSubscriptionRegistryImpladdCompleteSubscriptionLiteEventDispatcheronRegister; dispatch optimizationDescribe Alternatives You've Considered
String.split+ recursive matching is fast enough for < 10K topics**in the middle of a pattern (e.g.,order__**__refund).as separatordoDispatchcall; eager expansion is more efficientpatternfield toLiteSubscriptionDTOliteTopicSetis simpler and avoids wire protocol changesAdditional Context
+/#), but using__separator to avoid conflicts with RocketMQ's existing naming conventionsmatches()< 5μs per call,expand()< 10ms for 1,000 candidatesLiteSubscriptionCtlRequestBodywithout any wire protocol changescollectByParentTopicAPI for dispatch optimization