From fe620488903246a20c5310b46eb2ea20044688db Mon Sep 17 00:00:00 2001 From: Tom Moor Date: Tue, 1 Dec 2020 19:20:20 -0800 Subject: [PATCH] fix: One source of transaction deadlock when invites > pg pool (#1696) * fix: One source of transaction deadlock when invites > pg pool * lint --- server/commands/userInviter.js | 70 ++++++++++++-------------------- server/commands/userSuspender.js | 43 ++++++++++---------- 2 files changed, 48 insertions(+), 65 deletions(-) diff --git a/server/commands/userInviter.js b/server/commands/userInviter.js index 0dcfd64c..69ef5703 100644 --- a/server/commands/userInviter.js +++ b/server/commands/userInviter.js @@ -2,7 +2,6 @@ import { uniqBy } from "lodash"; import mailer from "../mailer"; import { User, Event, Team } from "../models"; -import { sequelize } from "../sequelize"; type Invite = { name: string, email: string }; @@ -47,48 +46,33 @@ export default async function userInviter({ let users = []; // send and record remaining invites - await Promise.all( - filteredInvites.map(async (invite) => { - const transaction = await sequelize.transaction(); - try { - const newUser = await User.create( - { - teamId: user.teamId, - name: invite.name, - email: invite.email, - service: null, - }, - { transaction } - ); - users.push(newUser); - await Event.create( - { - name: "users.invite", - actorId: user.id, - teamId: user.teamId, - data: { - email: invite.email, - name: invite.name, - }, - ip, - }, - { transaction } - ); - await mailer.invite({ - to: invite.email, - name: invite.name, - actorName: user.name, - actorEmail: user.email, - teamName: team.name, - teamUrl: team.url, - }); - await transaction.commit(); - } catch (err) { - await transaction.rollback(); - throw err; - } - }) - ); + for (const invite of filteredInvites) { + const newUser = await User.create({ + teamId: user.teamId, + name: invite.name, + email: invite.email, + service: null, + }); + users.push(newUser); + await Event.create({ + name: "users.invite", + actorId: user.id, + teamId: user.teamId, + data: { + email: invite.email, + name: invite.name, + }, + ip, + }); + await mailer.invite({ + to: invite.email, + name: invite.name, + actorName: user.name, + actorEmail: user.email, + teamName: team.name, + teamUrl: team.url, + }); + } return { sent: filteredInvites, users }; } diff --git a/server/commands/userSuspender.js b/server/commands/userSuspender.js index d143218b..a86b1807 100644 --- a/server/commands/userSuspender.js +++ b/server/commands/userSuspender.js @@ -1,4 +1,5 @@ // @flow +import { Transaction } from "sequelize"; import { ValidationError } from "../errors"; import { User, Event, GroupUser } from "../models"; import { sequelize } from "../sequelize"; @@ -16,29 +17,27 @@ export default async function userSuspender({ throw new ValidationError("Unable to suspend the current user"); } - let transaction; - try { - transaction = await sequelize.transaction(); + await sequelize.transaction(async (transaction: Transaction) => { + await user.update( + { + suspendedById: actorId, + suspendedAt: new Date(), + }, + { transaction } + ); - await user.update({ - suspendedById: actorId, - suspendedAt: new Date(), - }); + await GroupUser.destroy({ where: { userId: user.id }, transaction }); - await GroupUser.destroy({ where: { userId: user.id } }); - } catch (err) { - if (transaction) { - await transaction.rollback(); - } - throw err; - } - - await Event.create({ - name: "users.suspend", - actorId, - userId: user.id, - teamId: user.teamId, - data: { name: user.name }, - ip, + await Event.create( + { + name: "users.suspend", + actorId, + userId: user.id, + teamId: user.teamId, + data: { name: user.name }, + ip, + }, + { transaction } + ); }); }