GroupManager: Use .setSub() and parallel queries to avoid races

This also simplfies the code.
This commit is contained in:
Richard Hansen 2021-11-12 21:26:55 -05:00
parent 9d63700da0
commit 5b37a56197

View file

@ -43,38 +43,27 @@ exports.deleteGroup = async (groupID) => {
} }
// iterate through all pads of this group and delete them (in parallel) // iterate through all pads of this group and delete them (in parallel)
await Promise.all(Object.keys(group.pads) await Promise.all(Object.keys(group.pads).map(async (padId) => {
.map((padID) => padManager.getPad(padID) const pad = await padManager.getPad(padId);
.then((pad) => pad.remove()) await pad.remove();
)); }));
// iterate through group2sessions and delete all sessions // Delete associated sessions in parallel. This should be done before deleting the group2sessions
const group2sessions = await db.get(`group2sessions:${groupID}`); // record because deleting a session updates the group2sessions record.
const sessions = group2sessions ? group2sessions.sessionIDs : {}; const {sessionIDs = {}} = await db.get(`group2sessions:${groupID}`) || {};
await Promise.all(Object.keys(sessionIDs).map(async (sessionId) => {
await sessionManager.deleteSession(sessionId);
}));
// loop through all sessions and delete them (in parallel) await Promise.all([
await Promise.all(Object.keys(sessions).map((session) => sessionManager.deleteSession(session))); db.remove(`group2sessions:${groupID}`),
// UeberDB's setSub() method atomically reads the record, updates the appropriate property, and
// writes the result. Setting a property to `undefined` deletes that property (JSON.stringify()
// ignores such properties).
db.setSub('groups', [groupID], undefined),
]);
// remove group2sessions entry // Remove the group record after updating the `groups` record so that the state is consistent.
await db.remove(`group2sessions:${groupID}`);
// unlist the group
let groups = await exports.listAllGroups();
groups = groups ? groups.groupIDs : [];
const index = groups.indexOf(groupID);
if (index !== -1) {
// remove from the list
groups.splice(index, 1);
// regenerate group list
const newGroups = {};
groups.forEach((group) => newGroups[group] = 1);
await db.set('groups', newGroups);
}
// remove group entry
await db.remove(`group:${groupID}`); await db.remove(`group:${groupID}`);
}; };
@ -86,22 +75,12 @@ exports.doesGroupExist = async (groupID) => {
}; };
exports.createGroup = async () => { exports.createGroup = async () => {
// search for non existing groupID
const groupID = `g.${randomString(16)}`; const groupID = `g.${randomString(16)}`;
// create the group
await db.set(`group:${groupID}`, {pads: {}}); await db.set(`group:${groupID}`, {pads: {}});
// Add the group to the `groups` record after the group's individual record is created so that
// list the group // the state is consistent. Note: UeberDB's setSub() method atomically reads the record, updates
let groups = await exports.listAllGroups(); // the appropriate property, and writes the result.
groups = groups ? groups.groupIDs : []; await db.setSub('groups', [groupID], 1);
groups.push(groupID);
// regenerate group list
const newGroups = {};
groups.forEach((group) => newGroups[group] = 1);
await db.set('groups', newGroups);
return {groupID}; return {groupID};
}; };