fix: One source of transaction deadlock when invites > pg pool (#1696)

* fix: One source of transaction deadlock when invites > pg pool

* lint
This commit is contained in:
Tom Moor
2020-12-01 19:20:20 -08:00
committed by GitHub
parent 1851477290
commit fe62048890
2 changed files with 48 additions and 65 deletions

View File

@ -2,7 +2,6 @@
import { uniqBy } from "lodash"; import { uniqBy } from "lodash";
import mailer from "../mailer"; import mailer from "../mailer";
import { User, Event, Team } from "../models"; import { User, Event, Team } from "../models";
import { sequelize } from "../sequelize";
type Invite = { name: string, email: string }; type Invite = { name: string, email: string };
@ -47,22 +46,15 @@ export default async function userInviter({
let users = []; let users = [];
// send and record remaining invites // send and record remaining invites
await Promise.all( for (const invite of filteredInvites) {
filteredInvites.map(async (invite) => { const newUser = await User.create({
const transaction = await sequelize.transaction();
try {
const newUser = await User.create(
{
teamId: user.teamId, teamId: user.teamId,
name: invite.name, name: invite.name,
email: invite.email, email: invite.email,
service: null, service: null,
}, });
{ transaction }
);
users.push(newUser); users.push(newUser);
await Event.create( await Event.create({
{
name: "users.invite", name: "users.invite",
actorId: user.id, actorId: user.id,
teamId: user.teamId, teamId: user.teamId,
@ -71,9 +63,7 @@ export default async function userInviter({
name: invite.name, name: invite.name,
}, },
ip, ip,
}, });
{ transaction }
);
await mailer.invite({ await mailer.invite({
to: invite.email, to: invite.email,
name: invite.name, name: invite.name,
@ -82,13 +72,7 @@ export default async function userInviter({
teamName: team.name, teamName: team.name,
teamUrl: team.url, teamUrl: team.url,
}); });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
} }
})
);
return { sent: filteredInvites, users }; return { sent: filteredInvites, users };
} }

View File

@ -1,4 +1,5 @@
// @flow // @flow
import { Transaction } from "sequelize";
import { ValidationError } from "../errors"; import { ValidationError } from "../errors";
import { User, Event, GroupUser } from "../models"; import { User, Event, GroupUser } from "../models";
import { sequelize } from "../sequelize"; import { sequelize } from "../sequelize";
@ -16,29 +17,27 @@ export default async function userSuspender({
throw new ValidationError("Unable to suspend the current user"); throw new ValidationError("Unable to suspend the current user");
} }
let transaction; await sequelize.transaction(async (transaction: Transaction) => {
try { await user.update(
transaction = await sequelize.transaction(); {
await user.update({
suspendedById: actorId, suspendedById: actorId,
suspendedAt: new Date(), suspendedAt: new Date(),
}); },
{ transaction }
);
await GroupUser.destroy({ where: { userId: user.id } }); await GroupUser.destroy({ where: { userId: user.id }, transaction });
} catch (err) {
if (transaction) {
await transaction.rollback();
}
throw err;
}
await Event.create({ await Event.create(
{
name: "users.suspend", name: "users.suspend",
actorId, actorId,
userId: user.id, userId: user.id,
teamId: user.teamId, teamId: user.teamId,
data: { name: user.name }, data: { name: user.name },
ip, ip,
},
{ transaction }
);
}); });
} }