From c8eeb7c0b9cf8476a3fe86699e4de9870710c023 Mon Sep 17 00:00:00 2001 From: Jokul Date: Sun, 12 Jul 2026 10:01:30 +0800 Subject: [PATCH] fix: memory analysis dark mode stripe, scan speed, and NaN total size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Dark mode: add --el-fill-color-lighter and --el-table-current-row-bg-color to .el-table overrides so striped rows render with proper contrast - Scan speed: add batchKeyMemoryUsage pipeline (TYPE + MEMORY USAGE in one Redis round-trip instead of 2×N sequential IPC calls) - Scan speed: decouple scan loop from UI via 300ms flush buffer, keeping el-table re-render frequency capped at ~3/s - Scan speed: use shallowRef for entries to skip deep reactive proxies - NaN fix: handle ioredis stringNumbers:true by converting via Number() instead of typeof === 'number' - formatSize: guard against non-finite input, add TB/PB units --- src/main/ipc-handlers.ts | 3 + src/main/redis/keys.ts | 35 ++++++++++++ src/preload/index.d.ts | 1 + src/preload/index.ts | 2 + .../src/components/views/MemoryAnalysis.vue | 56 +++++++++++-------- src/renderer/src/styles/global.css | 2 + 6 files changed, 75 insertions(+), 24 deletions(-) diff --git a/src/main/ipc-handlers.ts b/src/main/ipc-handlers.ts index e6fe8cb..d88cf9d 100644 --- a/src/main/ipc-handlers.ts +++ b/src/main/ipc-handlers.ts @@ -149,6 +149,9 @@ export function registerIpcHandlers(): void { ipcMain.handle('redis:memoryUsage', async (_e, id: string, key: string) => { return redis.memoryUsage(id, key) }) + ipcMain.handle('redis:batchMemoryUsage', async (_e, id: string, keys: string[]) => { + return redis.batchKeyMemoryUsage(id, keys) + }) // Redis - String ipcMain.handle('redis:getString', async (_e, id: string, key: string) => { diff --git a/src/main/redis/keys.ts b/src/main/redis/keys.ts index 0bc8a95..4379cff 100644 --- a/src/main/redis/keys.ts +++ b/src/main/redis/keys.ts @@ -51,6 +51,41 @@ export async function memoryUsage(id: string, key: string): Promise } +export interface KeyMemoryInfo { + key: string + type: string + size: number +} + +/** + * Batch-fetch TYPE + MEMORY USAGE for multiple keys via a single ioredis pipeline. + * One Redis round-trip instead of 2×N sequential commands. + */ +export async function batchKeyMemoryUsage( + id: string, + keys: string[] +): Promise { + const client = requireClient(id) + if (keys.length === 0) return [] + const pipeline = client.pipeline() + for (const key of keys) { + pipeline.type(key) + pipeline.memory('USAGE', key) + } + const results = await pipeline.exec() + const entries: KeyMemoryInfo[] = [] + for (let i = 0; i < keys.length; i++) { + const typeRes = results?.[i * 2] + const sizeRes = results?.[i * 2 + 1] + const type = typeRes?.[0] ? 'none' : String(typeRes?.[1] ?? 'none') + const rawSize = sizeRes?.[0] ? null : sizeRes?.[1] + // ioredis stringNumbers:true may return strings like "528" instead of 528 + const size = Number.isFinite(Number(rawSize)) ? Number(rawSize) : 0 + entries.push({ key: keys[i], type, size }) + } + return entries +} + export async function scanKeys( id: string, cursor: string, diff --git a/src/preload/index.d.ts b/src/preload/index.d.ts index 007793d..c1b14ce 100644 --- a/src/preload/index.d.ts +++ b/src/preload/index.d.ts @@ -55,6 +55,7 @@ export interface ElectronAPI { setTTL: (id: string, key: string, ttl: number) => Promise batchDelete: (id: string, keys: string[]) => Promise memoryUsage: (id: string, key: string) => Promise + batchMemoryUsage: (id: string, keys: string[]) => Promise<{ key: string; type: string; size: number }[]> // String getString: (id: string, key: string) => Promise setString: (id: string, key: string, value: string) => Promise diff --git a/src/preload/index.ts b/src/preload/index.ts index a21612f..ccaabbb 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -96,6 +96,8 @@ const electronAPI = { // Memory memoryUsage: (id: string, key: string): Promise => ipcRenderer.invoke('redis:memoryUsage', id, key), + batchMemoryUsage: (id: string, keys: string[]): Promise<{ key: string; type: string; size: number }[]> => + ipcRenderer.invoke('redis:batchMemoryUsage', id, keys), batchDelete: (id: string, keys: string[]): Promise => ipcRenderer.invoke('redis:batchDelete', id, keys), // Stream diff --git a/src/renderer/src/components/views/MemoryAnalysis.vue b/src/renderer/src/components/views/MemoryAnalysis.vue index 39e196a..4c27db0 100644 --- a/src/renderer/src/components/views/MemoryAnalysis.vue +++ b/src/renderer/src/components/views/MemoryAnalysis.vue @@ -1,5 +1,5 @@