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 @@