From 7124ec790e601f6dce5d44bea48cfe17e9f0ff2b Mon Sep 17 00:00:00 2001 From: VPS 4 Deploy Agent Date: Sat, 27 Jun 2026 18:58:06 +0200 Subject: [PATCH] =?UTF-8?q?perf(whatsapp):=20=C3=ADndices=20em=20FKs=20+?= =?UTF-8?q?=20delete=20de=20inst=C3=A2ncia=20em=20lotes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Adiciona @@index nas FKs que sofrem cascade/SET NULL ao deletar instância: messages.replyToId (self-ref, evitava O(n²) no SET NULL), chats.instanceId, chats.contactId, contacts.instanceId, broadcasts.instanceId. - InstanceRepository.delete agora apaga 'messages' em lotes (5k) antes do cascade, evitando transação/locks gigantes em instâncias com dezenas de milhares de mensagens. O restante (chats/contacts/...) sai pelo cascade, agora rápido com os índices. Co-Authored-By: Claude Opus 4.8 --- .../newwhats.local/backend/prisma/schema.prisma | 9 +++++++++ .../whatsapp/repositories/InstanceRepository.ts | 16 ++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/newwhats.clube67.com/newwhats.local/backend/prisma/schema.prisma b/newwhats.clube67.com/newwhats.local/backend/prisma/schema.prisma index 98b9e01..f9a5c5e 100644 --- a/newwhats.clube67.com/newwhats.local/backend/prisma/schema.prisma +++ b/newwhats.clube67.com/newwhats.local/backend/prisma/schema.prisma @@ -175,6 +175,8 @@ model Contact { @@unique([tenantId, instanceId, jid]) @@index([tenantId, instanceId, lidJid]) + // FK dedicada — acelera o cascade ao deletar a instância + @@index([instanceId]) @@map("contacts") } @@ -207,6 +209,9 @@ model Chat { // WHERE tenantId + instanceId + isArchived (filtro) // ORDER BY isPinned DESC, lastMessageAt DESC (ordenação) @@index([tenantId, instanceId, isArchived, isPinned(sort: Desc), lastMessageAt(sort: Desc)]) + // FKs dedicadas — sem elas o cascade/SET NULL ao deletar instância/contato faz full-scan + @@index([instanceId]) + @@index([contactId]) @@map("chats") } @@ -251,6 +256,8 @@ model Message { @@index([instanceId, fromMe, remoteJid, timestamp(sort: Desc)]) // Índice para obter a última mensagem da instância — usado na verificação de gap de sincronização @@index([instanceId, timestamp(sort: Desc)]) + // FK self-referente — sem índice o cascade roda UPDATE replyToId=NULL com full-scan (O(n²)) + @@index([replyToId]) @@map("messages") } @@ -385,6 +392,8 @@ model Broadcast { instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) recipients BroadcastRecipient[] + // FK dedicada — acelera o cascade ao deletar a instância + @@index([instanceId]) @@map("broadcasts") } diff --git a/newwhats.clube67.com/newwhats.local/backend/src/modules/whatsapp/repositories/InstanceRepository.ts b/newwhats.clube67.com/newwhats.local/backend/src/modules/whatsapp/repositories/InstanceRepository.ts index 0fbb36a..d185f4a 100644 --- a/newwhats.clube67.com/newwhats.local/backend/src/modules/whatsapp/repositories/InstanceRepository.ts +++ b/newwhats.clube67.com/newwhats.local/backend/src/modules/whatsapp/repositories/InstanceRepository.ts @@ -24,6 +24,22 @@ export class InstanceRepository { } async delete(id: string) { + // Delete em lotes da maior tabela (messages) antes do cascade, para evitar + // uma transação única gigante (locks longos + WAL enorme) numa instância com + // dezenas de milhares de mensagens. O índice messages_replyToId_idx evita o + // O(n²) no SET NULL self-referente disparado pelo cascade. + const BATCH = 5000 + for (;;) { + const removed = await prisma.$executeRaw` + DELETE FROM messages + WHERE ctid IN ( + SELECT ctid FROM messages WHERE "instanceId" = ${id} LIMIT ${BATCH} + )` + if (removed < BATCH) break + } + + // O cascade do banco cuida do resto (chats, contacts, protocols, broadcasts…), + // agora rápido graças aos índices nas FKs. return prisma.instance.delete({ where: { id } }) } }