From b6f69d70469ace6250d08cb748dd6bc28ce87bb5 Mon Sep 17 00:00:00 2001 From: rever-tecnologia Date: Wed, 10 Dec 2025 10:21:35 -0300 Subject: [PATCH] fix(heartbeat): evitar versoes desnecessarias comparando dados antes de atualizar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Compara inventory/metrics com valores atuais antes de incluir no patch - Usa JSON.stringify para comparacao eficiente de objetos - Filtra campos de metadata que realmente mudaram - Evita criacao de versoes quando heartbeat envia dados identicos Isso previne o acumulo de versoes no Convex que causava OOM. Reducao de memoria apos limpeza manual: - DB: 450MB -> 16MB (96%) - RAM: 7GB -> 189MB (97%) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- convex/machines.ts | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/convex/machines.ts b/convex/machines.ts index 8f3be5e..694d094 100644 --- a/convex/machines.ts +++ b/convex/machines.ts @@ -854,20 +854,44 @@ export const heartbeat = mutation({ await ctx.db.insert("machineHeartbeats", { machineId: machine._id, lastHeartbeatAt: now }) } - // 2. Preparar patch de metadata (se houver mudancas) + // 2. Preparar patch de metadata (se houver mudancas REAIS) + // IMPORTANTE: So incluimos no patch se os dados realmente mudaram + // Isso evita criar versoes desnecessarias do documento machines const metadataPatch: Record = {} + const currentMetadata = (machine.metadata ?? {}) as Record + if (args.metadata && typeof args.metadata === "object") { - Object.assign(metadataPatch, args.metadata as Record) - } - const remoteAccessSnapshot = metadataPatch["remoteAccessSnapshot"] - if (remoteAccessSnapshot !== undefined) { - delete metadataPatch["remoteAccessSnapshot"] + // Filtrar apenas campos que realmente mudaram + const incomingMeta = args.metadata as Record + for (const key of Object.keys(incomingMeta)) { + if (key !== "inventory" && key !== "metrics" && key !== "remoteAccessSnapshot") { + if (JSON.stringify(incomingMeta[key]) !== JSON.stringify(currentMetadata[key])) { + metadataPatch[key] = incomingMeta[key] + } + } + } } + + const remoteAccessSnapshot = (args.metadata as Record | undefined)?.["remoteAccessSnapshot"] + + // Inventory: so incluir se realmente mudou if (args.inventory && typeof args.inventory === "object") { - metadataPatch.inventory = mergeInventory(metadataPatch.inventory, args.inventory as Record) + const currentInventory = currentMetadata.inventory as Record | undefined + const newInventoryStr = JSON.stringify(args.inventory) + const currentInventoryStr = JSON.stringify(currentInventory ?? {}) + if (newInventoryStr !== currentInventoryStr) { + metadataPatch.inventory = mergeInventory(currentInventory, args.inventory as Record) + } } + + // Metrics: so incluir se realmente mudou if (args.metrics && typeof args.metrics === "object") { - metadataPatch.metrics = args.metrics as Record + const currentMetrics = currentMetadata.metrics as Record | undefined + const newMetricsStr = JSON.stringify(args.metrics) + const currentMetricsStr = JSON.stringify(currentMetrics ?? {}) + if (newMetricsStr !== currentMetricsStr) { + metadataPatch.metrics = args.metrics as Record + } } // 3. Verificar se ha mudancas reais nos dados que justifiquem atualizar o documento machines