fix(rocketmq): search consumer groups beyond first page

This commit is contained in:
jearhe authored and GitHub committed 2026-09-01 13:55:02 +08:00
1 parent 4d3d64bd34
commit 7f9e35d1e8
3 files changed
+223 -28

No files matched your search

@@ -37,7 +37,6 @@ const { confirmMqWrite } = useMqMutationGuard(() => props.connectionId);
const subscriptions = ref<SubscriptionInfo[]>([]);
const loading = ref(false);
const enriching = ref(false);
const truncatedHint = ref<string>();
const enrichFailedHint = ref<string>();
const error = ref<string>();
let loadSeq = 0;
@@ -170,7 +169,6 @@ async function loadSubscriptions() {
const topicRef = getListTopicRef();
if (!topicRef) {
subscriptions.value = [];
truncatedHint.value = undefined;
enrichFailedHint.value = undefined;
return;
}
@@ -178,7 +176,6 @@ async function loadSubscriptions() {
loading.value = true;
enriching.value = false;
error.value = undefined;
truncatedHint.value = undefined;
enrichFailedHint.value = undefined;
try {
// Fast list first (no enrich / online members) so large clusters paint quickly.
@@ -186,20 +183,16 @@ async function loadSubscriptions() {
if (seq !== loadSeq) return;
subscriptions.value = page;
syncSelectedSubscription(page);
if (isClusterWideMode.value && page.length >= 500) {
truncatedHint.value = t("mqSubscriptions.truncatedHint", { count: page.length });
}
if (isClusterWideMode.value) {
enriching.value = true;
try {
const enriched = await mqEnrichSubscriptions(props.connectionId, topicRef);
if (seq !== loadSeq) return;
subscriptions.value = enriched;
// Keep the complete fast list even if an older/slow agent returns only a partial enrichment page.
const merged = mergeSubscriptionEnrichment(page, enriched);
subscriptions.value = merged;
// Detail dialog holds a snapshot; refresh so topics/members arrive after enrich.
syncSelectedSubscription(enriched);
if (enriched.length >= 500) {
truncatedHint.value = t("mqSubscriptions.truncatedHint", { count: enriched.length });
}
syncSelectedSubscription(merged);
} catch (e: unknown) {
// Keep the fast list if enrichment times out; surface why online columns stayed empty.
if (seq === loadSeq) {
@@ -216,6 +209,14 @@ async function loadSubscriptions() {
}
}
function mergeSubscriptionEnrichment(fastList: SubscriptionInfo[], enrichedList: SubscriptionInfo[]): SubscriptionInfo[] {
const enrichedByName = new Map(enrichedList.map((subscription) => [subscription.name, subscription]));
return fastList.map((subscription) => {
const enriched = enrichedByName.get(subscription.name);
return enriched ? { ...subscription, ...enriched } : subscription;
});
}
function openCreateDialog() {
if (props.readOnly) {
error.value = t("mqSubscriptions.readOnly");
@@ -526,7 +527,6 @@ watch(
<template v-else>
<div v-if="error" class="panel-error">{{ error }}</div>
<div v-if="truncatedHint && !error" class="panel-hint">{{ truncatedHint }}</div>
<div v-if="enrichFailedHint && !error" class="panel-hint">{{ enrichFailedHint }}</div>
<div v-if="!error && loading && !subscriptions.length" class="panel-loading">{{ t("mqSubscriptions.loading") }}</div>
@@ -2,7 +2,7 @@
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { createApp, nextTick, type App } from "vue";
import type { PeekedMessage } from "@/types/mq";
import type { PeekedMessage, SubscriptionInfo } from "@/types/mq";
const backend = vi.hoisted(() => ({
mqListSubscriptions: vi.fn(),
@@ -90,7 +90,7 @@ async function mountPanel(overrides: Record<string, unknown> = {}) {
return root;
}
function subscription(name: string, consumerGroupType: string) {
function subscription(name: string, consumerGroupType: string): SubscriptionInfo {
return {
name,
subType: consumerGroupType,
@@ -330,3 +330,30 @@ describe("SubscriptionsPanel RocketMQ filter count", () => {
expect(count()).toBe("2 / 4");
});
});
describe("SubscriptionsPanel RocketMQ pagination", () => {
it("keeps groups after the first page when enrichment is partial", async () => {
const rows = Array.from({ length: 1000 }, (_, index) => subscription(`group-${String(index + 1).padStart(4, "0")}`, "NORMAL"));
rows.push(subscription("z-target-group", "NORMAL"));
backend.mqListSubscriptions.mockResolvedValue(rows);
backend.mqEnrichSubscriptions.mockResolvedValue(rows.slice(0, 500).map((row) => ({ ...row, onlineMembers: 2, topics: ["events"] })));
const panel = await mountPanel({
topic: undefined,
tenant: "_rocketmq",
namespace: "default",
mqSystemKind: "rocketmq",
supportsPeekMessages: false,
});
await vi.waitFor(() => expect(panel.querySelectorAll("tbody tr")).toHaveLength(1001));
expect(panel.querySelector('[data-testid="subscription-count"]')?.textContent?.replace(/\s+/g, " ").trim()).toBe("1001 / 1001");
const search = panel.querySelector<HTMLInputElement>("input.topic-search");
expect(search).toBeTruthy();
search!.value = "z-target-group";
search!.dispatchEvent(new Event("input", { bubbles: true }));
await vi.waitFor(() => expect(panel.querySelectorAll("tbody tr")).toHaveLength(1));
expect(panel.textContent).toContain("z-target-group");
});
});