From 6b6156b03284f7f447611679af47236c47288452 Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Thu, 18 Apr 2024 09:32:13 -0400 Subject: [PATCH] fix: Reduce size of teamPermanentDeleter transactions for reliability --- server/commands/teamPermanentDeleter.ts | 80 ++++++++++++++----------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/server/commands/teamPermanentDeleter.ts b/server/commands/teamPermanentDeleter.ts index 72f41bfd3..26ef7a043 100644 --- a/server/commands/teamPermanentDeleter.ts +++ b/server/commands/teamPermanentDeleter.ts @@ -1,4 +1,3 @@ -import { Transaction } from "sequelize"; import Logger from "@server/logging/Logger"; import { traceFunction } from "@server/logging/tracing"; import { @@ -20,6 +19,13 @@ import { } from "@server/models"; import { sequelize } from "@server/storage/database"; +/** + * Permanently deletes a team and all related data from the database. Note that this does not happen + * in a single transaction due to the potential size of such a transaction, so it is possible for + * the operation to be interrupted and leave partial data. In which case it can be safely re-run. + * + * @param team - The team to delete. + */ async function teamPermanentDeleter(team: Team) { if (!team.deletedAt) { throw new Error( @@ -32,19 +38,17 @@ async function teamPermanentDeleter(team: Team) { `Permanently destroying team ${team.name} (${team.id})` ); const teamId = team.id; - let transaction!: Transaction; - try { - transaction = await sequelize.transaction(); - await Attachment.findAllInBatches( - { - where: { - teamId, - }, - limit: 100, - offset: 0, + await Attachment.findAllInBatches( + { + where: { + teamId, }, - async (attachments, options) => { + limit: 100, + offset: 0, + }, + async (attachments, options) => { + await sequelize.transaction(async (transaction) => { Logger.info( "commands", `Deleting attachments ${options.offset} – ${ @@ -58,19 +62,22 @@ async function teamPermanentDeleter(team: Team) { }) ) ); - } - ); - // Destroy user-relation models - await User.findAllInBatches( - { - attributes: ["id"], - where: { - teamId, - }, - limit: 100, - offset: 0, + }); + } + ); + + // Destroy user-relation models + await User.findAllInBatches( + { + attributes: ["id"], + where: { + teamId, }, - async (users) => { + limit: 100, + offset: 0, + }, + async (users) => { + await sequelize.transaction(async (transaction) => { const userIds = users.map((user) => user.id); await UserAuthentication.destroy({ where: { @@ -79,6 +86,13 @@ async function teamPermanentDeleter(team: Team) { force: true, transaction, }); + await Attachment.destroy({ + where: { + userId: userIds, + }, + force: true, + transaction, + }); await ApiKey.destroy({ where: { userId: userIds, @@ -93,9 +107,12 @@ async function teamPermanentDeleter(team: Team) { force: true, transaction, }); - } - ); - // Destory team-relation models + }); + } + ); + + // Destory team-relation models + await sequelize.transaction(async (transaction) => { await AuthenticationProvider.destroy({ where: { teamId, @@ -180,14 +197,7 @@ async function teamPermanentDeleter(team: Team) { transaction, } ); - await transaction.commit(); - } catch (err) { - if (transaction) { - await transaction.rollback(); - } - - throw err; - } + }); } export default traceFunction({