This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new fb6455a03 fix(consumer): keep an unknown total lag from being a
partial subtotal (#7053)
fb6455a03 is described below
commit fb6455a0352e7431144eb355ea66f1000b7fbc55
Author: 0 <[email protected]>
AuthorDate: Wed Oct 28 01:08:47 2026 +0800
fix(consumer): keep an unknown total lag from being a partial subtotal
(#7053)
When the queue scan did not cover every queue, analyzeConsumerGroupHealth
fell back
to the sum of the queues it did see and reported that subtotal as the
group's total
lag - a partial measurement presented as a complete one. reportedLag now
yields null
unless every queue in the progress list had a readable lag, and the single
render
site already treats null as unknown. The health-issue path keeps using the
known
subtotal (summaryReportedLag ?? totalKnownLag), so this changes only what is
reported as the total, not which issues are raised.
This complements rather than duplicates the delay-availability work that
landed in
#5918: that change covers delaySeconds, which has no sentinel and needs a
boolean
flag, while totalLag already carries the -1 sentinel, so a single
isLagAvailable
conversion at the boundary is enough. The test file conflicted with those
additions;
both sides' cases are kept.
---
web/src/utils/consumerGroupDiagnostics.test.ts | 70 +++++++++++++++++++++++++-
web/src/utils/consumerGroupDiagnostics.ts | 7 +--
2 files changed, 73 insertions(+), 4 deletions(-)
diff --git a/web/src/utils/consumerGroupDiagnostics.test.ts
b/web/src/utils/consumerGroupDiagnostics.test.ts
index 19f6cce89..02402de34 100644
--- a/web/src/utils/consumerGroupDiagnostics.test.ts
+++ b/web/src/utils/consumerGroupDiagnostics.test.ts
@@ -173,7 +173,7 @@ describe('consumer group diagnostics', () => {
expect(diagnostics.status).toBe('warning');
expect(diagnostics.summary).toMatchObject({
- reportedLag: 0,
+ reportedLag: null,
unknownQueueCount: 1,
maxQueueLag: null,
maxHeartbeatAgeSeconds: 660,
@@ -213,4 +213,72 @@ describe('consumer group diagnostics', () => {
expect(diagnostics.issues.map((item) =>
item.code)).toContain('HIGH_CONSUME_DELAY');
expect(diagnostics.issues.map((item) =>
item.code)).not.toContain('CONSUME_DELAY_UNKNOWN');
});
+
+ it('keeps the reported lag unknown when only some queue lags are available',
() => {
+ const diagnostics = analyzeConsumerGroupHealth(
+ group({ totalLag: -1 }),
+ [subscription()],
+ [queue({ diffTotal: 17 }), queue({ broker: 'broker-b', queueId: 1,
diffTotal: -1 })],
+ { now: '2026-08-31T12:01:00Z' },
+ );
+
+ expect(diagnostics.summary).toMatchObject({
+ totalKnownLag: 17,
+ reportedLag: null,
+ unknownQueueCount: 1,
+ });
+ });
+
+ it('falls back to the queue subtotal only when every queue lag is
available', () => {
+ const diagnostics = analyzeConsumerGroupHealth(
+ group({ totalLag: -1 }),
+ [subscription()],
+ [queue({ diffTotal: 10 }), queue({ broker: 'broker-b', queueId: 1,
diffTotal: 7 })],
+ { now: '2026-08-31T12:01:00Z' },
+ );
+
+ expect(diagnostics.summary).toMatchObject({
+ totalKnownLag: 17,
+ reportedLag: 17,
+ unknownQueueCount: 0,
+ });
+ });
+
+ it('preserves an independently measured zero total lag', () => {
+ const diagnostics = analyzeConsumerGroupHealth(
+ group({ totalLag: 0 }),
+ [subscription()],
+ [queue({ diffTotal: -1 })],
+ { now: '2026-08-31T12:01:00Z' },
+ );
+
+ expect(diagnostics.summary).toMatchObject({
+ totalKnownLag: 0,
+ reportedLag: 0,
+ unknownQueueCount: 1,
+ });
+ });
+
+ it('keeps the reported lag unknown without queue observations', () => {
+ const diagnostics = analyzeConsumerGroupHealth(group({ totalLag: -1 }),
[subscription()], [], {
+ now: '2026-08-31T12:01:00Z',
+ });
+
+ expect(diagnostics.summary).toMatchObject({
+ totalKnownLag: 0,
+ reportedLag: null,
+ });
+ });
+
+ it('still warns about missing clients when only the known subtotal is
positive', () => {
+ const diagnostics = analyzeConsumerGroupHealth(
+ group({ onlineInstances: 0, totalLag: -1, instances: [] }),
+ [subscription()],
+ [queue({ diffTotal: 17 }), queue({ broker: 'broker-b', queueId: 1,
diffTotal: -1 })],
+ { now: '2026-08-31T12:01:00Z' },
+ );
+
+ expect(diagnostics.summary.reportedLag).toBeNull();
+ expect(diagnostics.issues.map((item) =>
item.code)).toContain('NO_ACTIVE_CLIENTS_WITH_LAG');
+ });
});
diff --git a/web/src/utils/consumerGroupDiagnostics.ts
b/web/src/utils/consumerGroupDiagnostics.ts
index e402512dc..83d923855 100644
--- a/web/src/utils/consumerGroupDiagnostics.ts
+++ b/web/src/utils/consumerGroupDiagnostics.ts
@@ -142,7 +142,7 @@ const heartbeatAgeSeconds = (lastHeartbeat: string |
undefined, now: number): nu
const knownLag = (progress: QueueProgress[]): number =>
progress.reduce((sum, queue) => sum + (isLagAvailable(queue.diffTotal) ?
queue.diffTotal : 0), 0);
-const reportedLag = (group: ConsumerGroup, fallback: number): number | null =>
+const reportedLag = (group: ConsumerGroup, fallback: number | null): number |
null =>
isLagAvailable(group.totalLag) ? group.totalLag : fallback;
const topicCount = (group: ConsumerGroup, subscriptions: SubscriptionEntry[]):
number => {
@@ -425,7 +425,8 @@ export const analyzeConsumerGroupHealth = (
.map((queue) => queue.diffTotal)
.filter((lag): lag is number => isLagAvailable(lag));
const totalKnownLag = knownLag(progress);
- const summaryReportedLag = reportedLag(group, totalKnownLag);
+ const allQueuesKnown = progress.length > 0 && knownQueueLags.length ===
progress.length;
+ const summaryReportedLag = reportedLag(group, allQueuesKnown ? totalKnownLag
: null);
const unknownQueueCount = progress.length - knownQueueLags.length;
const heartbeatAges = (group.instances ?? [])
.map((client) => heartbeatAgeSeconds(client.lastHeartbeat, now))
@@ -433,7 +434,7 @@ export const analyzeConsumerGroupHealth = (
const issues = [
...subscriptionIssues(subscriptions),
...progressIssues(progress, knownQueueLags, unknownQueueCount,
normalizedOptions),
- ...runtimeIssues(group, summaryReportedLag, now, normalizedOptions),
+ ...runtimeIssues(group, summaryReportedLag ?? totalKnownLag, now,
normalizedOptions),
];
const status = maxStatus(issues);