Discard late Fleet history replies after changing selected node
This commit is contained in:
@@ -46,3 +46,25 @@ it('does not rearm timers when requests finish after unmount', async () => {
|
|||||||
expect(intervals).toHaveBeenCalledTimes(intervalCalls)
|
expect(intervals).toHaveBeenCalledTimes(intervalCalls)
|
||||||
intervals.mockRestore(); clear.mockRestore()
|
intervals.mockRestore(); clear.mockRestore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('keeps a late node history response out of the newly selected node', async () => {
|
||||||
|
const history = new Map<string, (value: unknown) => void>()
|
||||||
|
vi.mocked(rpcClient.call).mockImplementation(({ method, params }) => {
|
||||||
|
if (method === 'telemetry.fleet-node-history') return new Promise(done => { history.set((params as { node_id: string }).node_id, done) }) as never
|
||||||
|
return Promise.resolve({ nodes: [], alerts: [] }) as never
|
||||||
|
})
|
||||||
|
let fleet!: ReturnType<typeof useFleetData>
|
||||||
|
const wrapper = mount(defineComponent({ setup() { fleet = useFleetData(); return () => null } }))
|
||||||
|
await flushPromises()
|
||||||
|
fleet.selectNode('first'); await flushPromises()
|
||||||
|
fleet.selectNode('second'); await flushPromises()
|
||||||
|
history.get('first')!({ history: [{ timestamp: 'old', cpu_pct: 99 }] })
|
||||||
|
await flushPromises()
|
||||||
|
expect(fleet.nodeHistory.value).toEqual([])
|
||||||
|
expect(fleet.nodeHistoryLoading.value).toBe(true)
|
||||||
|
history.get('second')!({ history: [{ timestamp: 'current', cpu_pct: 10 }] })
|
||||||
|
await flushPromises()
|
||||||
|
expect(fleet.nodeHistory.value[0]?.timestamp).toBe('current')
|
||||||
|
expect(fleet.nodeHistoryLoading.value).toBe(false)
|
||||||
|
wrapper.unmount()
|
||||||
|
})
|
||||||
|
|||||||
@@ -273,6 +273,7 @@ export function useFleetData() {
|
|||||||
const sortBy = ref<SortOption>(cached.sortBy ?? 'status')
|
const sortBy = ref<SortOption>(cached.sortBy ?? 'status')
|
||||||
const chartWidth = ref(300)
|
const chartWidth = ref(300)
|
||||||
let pollTimer: ReturnType<typeof setInterval> | null = null
|
let pollTimer: ReturnType<typeof setInterval> | null = null
|
||||||
|
let historyRequest = 0
|
||||||
|
|
||||||
// --- Computed ---
|
// --- Computed ---
|
||||||
|
|
||||||
@@ -384,17 +385,20 @@ export function useFleetData() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function fetchNodeHistory(nodeId: string) {
|
async function fetchNodeHistory(nodeId: string) {
|
||||||
|
const request = ++historyRequest
|
||||||
nodeHistoryLoading.value = true
|
nodeHistoryLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await rpcClient.call<{ history?: NodeHistoryEntry[]; entries?: NodeHistoryEntry[] }>({
|
const data = await rpcClient.call<{ history?: NodeHistoryEntry[]; entries?: NodeHistoryEntry[] }>({
|
||||||
method: 'telemetry.fleet-node-history',
|
method: 'telemetry.fleet-node-history',
|
||||||
params: { node_id: nodeId },
|
params: { node_id: nodeId },
|
||||||
})
|
})
|
||||||
nodeHistory.value = normalizeNodeHistoryResponse(data)
|
if (request === historyRequest && selectedNodeId.value === nodeId) {
|
||||||
|
nodeHistory.value = normalizeNodeHistoryResponse(data)
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// Non-critical
|
// Non-critical
|
||||||
} finally {
|
} finally {
|
||||||
nodeHistoryLoading.value = false
|
if (request === historyRequest) nodeHistoryLoading.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -477,6 +481,9 @@ export function useFleetData() {
|
|||||||
|
|
||||||
// Fetch node history when selection changes
|
// Fetch node history when selection changes
|
||||||
watch(selectedNodeId, (newId) => {
|
watch(selectedNodeId, (newId) => {
|
||||||
|
historyRequest++
|
||||||
|
nodeHistory.value = []
|
||||||
|
nodeHistoryLoading.value = false
|
||||||
if (newId) {
|
if (newId) {
|
||||||
fetchNodeHistory(newId)
|
fetchNodeHistory(newId)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user