fix(rocketmq): search consumer groups beyond first page
This commit is contained in:
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");
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user