fix(convex): prevent OOM by filtering large inventory fields
- Add INVENTORY_BLOCKLIST to filter 'software' and 'extended' fields from machine metadata (these fields can be 100KB+ each) - Add compactMachineMetadata migration to clean existing large documents - Preserve essential fields: metrics, postureAlerts, collaborator, inventory.os, cpu, memory, disks, network, services 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
508f915cf9
commit
cf28ad2ee4
2 changed files with 122 additions and 0 deletions
|
|
@ -870,6 +870,122 @@ export const syncMachineCompanyReferences = mutation({
|
|||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Migracao para compactar metadata de machines, removendo dados volumosos
|
||||
* como inventory.software (lista de programas instalados) que podem ter
|
||||
* centenas de KBs por maquina.
|
||||
*
|
||||
* Esta migracao preserva:
|
||||
* - metrics (metricas de sistema)
|
||||
* - postureAlerts (alertas de postura)
|
||||
* - lastPostureAt (timestamp)
|
||||
* - collaborator (email do colaborador)
|
||||
* - inventory.os, inventory.cpu, inventory.memory, inventory.disks, inventory.network
|
||||
* - inventory.services (lista de servicos - usado para alertas de postura)
|
||||
*
|
||||
* Remove:
|
||||
* - inventory.software (lista completa de programas instalados - muito grande)
|
||||
* - inventory.extended (dados estendidos do Linux - muito grande)
|
||||
* - cpuWindow (janela de CPU - pode ser reconstruida)
|
||||
*/
|
||||
export const compactMachineMetadata = mutation({
|
||||
args: {
|
||||
tenantId: v.optional(v.string()),
|
||||
limit: v.optional(v.number()),
|
||||
dryRun: v.optional(v.boolean()),
|
||||
},
|
||||
handler: async (ctx, { tenantId, limit, dryRun }) => {
|
||||
const effectiveDryRun = Boolean(dryRun)
|
||||
const effectiveLimit = limit && limit > 0 ? Math.min(limit, 200) : 200
|
||||
|
||||
// Busca maquinas em lotes pequenos para evitar OOM
|
||||
const machines = tenantId && tenantId.trim().length > 0
|
||||
? await ctx.db
|
||||
.query("machines")
|
||||
.withIndex("by_tenant", (q) => q.eq("tenantId", tenantId))
|
||||
.take(effectiveLimit)
|
||||
: await ctx.db.query("machines").take(effectiveLimit)
|
||||
|
||||
let processed = 0
|
||||
let compacted = 0
|
||||
let bytesSavedEstimate = 0
|
||||
|
||||
for (const machine of machines) {
|
||||
processed += 1
|
||||
const metadata = machine.metadata
|
||||
if (!metadata || typeof metadata !== "object") continue
|
||||
|
||||
const meta = metadata as Record<string, unknown>
|
||||
const inventory = meta["inventory"]
|
||||
const cpuWindow = meta["cpuWindow"]
|
||||
|
||||
// Verificar se precisa compactar
|
||||
let needsCompact = false
|
||||
const sizeBefore = JSON.stringify(meta).length
|
||||
|
||||
const newMeta: Record<string, unknown> = {}
|
||||
|
||||
// Preservar campos essenciais
|
||||
if (meta["metrics"]) newMeta["metrics"] = meta["metrics"]
|
||||
if (meta["postureAlerts"]) newMeta["postureAlerts"] = meta["postureAlerts"]
|
||||
if (meta["lastPostureAt"]) newMeta["lastPostureAt"] = meta["lastPostureAt"]
|
||||
if (meta["collaborator"]) newMeta["collaborator"] = meta["collaborator"]
|
||||
if (meta["remoteAccessSnapshot"]) newMeta["remoteAccessSnapshot"] = meta["remoteAccessSnapshot"]
|
||||
|
||||
// Compactar cpuWindow para apenas ultimas 30 amostras (em vez de 120)
|
||||
if (Array.isArray(cpuWindow) && cpuWindow.length > 30) {
|
||||
newMeta["cpuWindow"] = cpuWindow.slice(-30)
|
||||
needsCompact = true
|
||||
} else if (cpuWindow) {
|
||||
newMeta["cpuWindow"] = cpuWindow
|
||||
}
|
||||
|
||||
// Compactar inventory - remover software e extended
|
||||
if (inventory && typeof inventory === "object") {
|
||||
const inv = inventory as Record<string, unknown>
|
||||
const newInv: Record<string, unknown> = {}
|
||||
|
||||
// Preservar apenas campos essenciais do inventory
|
||||
if (inv["os"]) newInv["os"] = inv["os"]
|
||||
if (inv["cpu"]) newInv["cpu"] = inv["cpu"]
|
||||
if (inv["memory"]) newInv["memory"] = inv["memory"]
|
||||
if (inv["disks"]) newInv["disks"] = inv["disks"]
|
||||
if (inv["network"]) newInv["network"] = inv["network"]
|
||||
if (inv["services"]) newInv["services"] = inv["services"]
|
||||
if (inv["bios"]) newInv["bios"] = inv["bios"]
|
||||
if (inv["motherboard"]) newInv["motherboard"] = inv["motherboard"]
|
||||
|
||||
// Verificar se tinha software ou extended (campos volumosos)
|
||||
if (inv["software"] || inv["extended"]) {
|
||||
needsCompact = true
|
||||
}
|
||||
|
||||
if (Object.keys(newInv).length > 0) {
|
||||
newMeta["inventory"] = newInv
|
||||
}
|
||||
}
|
||||
|
||||
if (!needsCompact) continue
|
||||
|
||||
const sizeAfter = JSON.stringify(newMeta).length
|
||||
bytesSavedEstimate += sizeBefore - sizeAfter
|
||||
|
||||
if (!effectiveDryRun) {
|
||||
await ctx.db.patch(machine._id, { metadata: newMeta, updatedAt: Date.now() })
|
||||
}
|
||||
compacted += 1
|
||||
}
|
||||
|
||||
return {
|
||||
dryRun: effectiveDryRun,
|
||||
processed,
|
||||
compacted,
|
||||
bytesSavedEstimate,
|
||||
bytesSavedMB: Math.round(bytesSavedEstimate / 1024 / 1024 * 100) / 100,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
export const backfillTicketSnapshots = mutation({
|
||||
args: { tenantId: v.string(), limit: v.optional(v.number()) },
|
||||
handler: async (ctx, { tenantId, limit }) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue