Before Creating the Bug Report
Runtime platform environment
Linux, JDK 21, develop (ff8f6f7)
RocketMQ version
5.x develop
Describe the Bug
The split-metadata pagination loops terminate one entry too early. The client-side loops in MQClientAPIImpl break when:
if (topicSeq >= totalTopicNum - 1) { // line 3125
if (groupSeq >= totalGroupNum - 1) { // line 3033
The broker pages [seq, seq + maxNum) with no overlap (TopicConfigManager#subTopicConfig / SubscriptionGroupManager#subGroupTable, totalTopicNum = size()), so after page k the client has fetched k·pageSize entries. The loop therefore stops when k·pageSize >= N − 1, which is already true when exactly one entry remains unfetched, i.e. for every N ≡ 1 (mod pageSize).
Example: 2001 topics with the default page size 2000 — page 1 returns 2000 entries, 2000 >= 2001 - 1 is true, the loop breaks, and topic #2001 is never requested. No error, no retry — silently truncated metadata.
The same off-by-one exists broker-side in BrokerOuterAPI (getAllTopicConfig at line 834, getAllSubscriptionGroup at line 984); BrokerOuterAPI#getAllTopicConfig is used by SlaveSynchronize#syncTopicConfig, so a slave whose master has N ≡ 1 (mod 2000) topics permanently misses the last topic (the dataVersion comparison then matches, so no resync happens).
Steps to Reproduce
- Set
maxPageSizeInGetMetadata to 100 (or have 2001 topics with the default 2000).
- Call
DefaultMQAdminExt#getAllTopicConfig on a broker with 101 topics.
- Result contains 100 topics — the last one is missing.
Expected Behavior
The loop must terminate only when seq >= totalNum; the last entry must be fetched.
Corresponding PR
Before Creating the Bug Report
Runtime platform environment
Linux, JDK 21, develop (ff8f6f7)
RocketMQ version
5.x develop
Describe the Bug
The split-metadata pagination loops terminate one entry too early. The client-side loops in
MQClientAPIImplbreak when:The broker pages
[seq, seq + maxNum)with no overlap (TopicConfigManager#subTopicConfig / SubscriptionGroupManager#subGroupTable,totalTopicNum = size()), so after page k the client has fetchedk·pageSizeentries. The loop therefore stops whenk·pageSize >= N − 1, which is already true when exactly one entry remains unfetched, i.e. for every N ≡ 1 (mod pageSize).Example: 2001 topics with the default page size 2000 — page 1 returns 2000 entries,
2000 >= 2001 - 1is true, the loop breaks, and topic #2001 is never requested. No error, no retry — silently truncated metadata.The same off-by-one exists broker-side in
BrokerOuterAPI(getAllTopicConfig at line 834, getAllSubscriptionGroup at line 984);BrokerOuterAPI#getAllTopicConfigis used bySlaveSynchronize#syncTopicConfig, so a slave whose master has N ≡ 1 (mod 2000) topics permanently misses the last topic (the dataVersion comparison then matches, so no resync happens).Steps to Reproduce
maxPageSizeInGetMetadatato 100 (or have 2001 topics with the default 2000).DefaultMQAdminExt#getAllTopicConfigon a broker with 101 topics.Expected Behavior
The loop must terminate only when
seq >= totalNum; the last entry must be fetched.Corresponding PR
Closes #11041in the PR description; contains the regression test that fails before the fix and passes after it).