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 } }) } }