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,48 +46,33 @@ 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(); teamId: user.teamId,
try { name: invite.name,
const newUser = await User.create( email: invite.email,
{ service: null,
teamId: user.teamId, });
name: invite.name, users.push(newUser);
email: invite.email, await Event.create({
service: null, name: "users.invite",
}, actorId: user.id,
{ transaction } teamId: user.teamId,
); data: {
users.push(newUser); email: invite.email,
await Event.create( name: invite.name,
{ },
name: "users.invite", ip,
actorId: user.id, });
teamId: user.teamId, await mailer.invite({
data: { to: invite.email,
email: invite.email, name: invite.name,
name: invite.name, actorName: user.name,
}, actorEmail: user.email,
ip, teamName: team.name,
}, teamUrl: team.url,
{ 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;
}
})
);
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(); {
suspendedById: actorId,
suspendedAt: new Date(),
},
{ transaction }
);
await user.update({ await GroupUser.destroy({ where: { userId: user.id }, transaction });
suspendedById: actorId,
suspendedAt: new Date(),
});
await GroupUser.destroy({ where: { userId: user.id } }); await Event.create(
} catch (err) { {
if (transaction) { name: "users.suspend",
await transaction.rollback(); actorId,
} userId: user.id,
throw err; teamId: user.teamId,
} data: { name: user.name },
ip,
await Event.create({ },
name: "users.suspend", { transaction }
actorId, );
userId: user.id,
teamId: user.teamId,
data: { name: user.name },
ip,
}); });
} }