Skip to content

Commit

Permalink
GroupManager: Use .setSub() and parallel queries to avoid races
Browse files Browse the repository at this point in the history
This also simplfies the code.
  • Loading branch information
rhansen committed Nov 28, 2021
1 parent 9d63700 commit 5b37a56
Showing 1 changed file with 25 additions and 46 deletions.
71 changes: 25 additions & 46 deletions src/node/db/GroupManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,38 +43,27 @@ exports.deleteGroup = async (groupID) => {
}

// iterate through all pads of this group and delete them (in parallel)
await Promise.all(Object.keys(group.pads)
.map((padID) => padManager.getPad(padID)
.then((pad) => pad.remove())
));

// iterate through group2sessions and delete all sessions
const group2sessions = await db.get(`group2sessions:${groupID}`);
const sessions = group2sessions ? group2sessions.sessionIDs : {};

// loop through all sessions and delete them (in parallel)
await Promise.all(Object.keys(sessions).map((session) => sessionManager.deleteSession(session)));

// remove group2sessions entry
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 Promise.all(Object.keys(group.pads).map(async (padId) => {
const pad = await padManager.getPad(padId);
await pad.remove();
}));

// Delete associated sessions in parallel. This should be done before deleting the group2sessions
// record because deleting a session updates the group2sessions record.
const {sessionIDs = {}} = await db.get(`group2sessions:${groupID}`) || {};
await Promise.all(Object.keys(sessionIDs).map(async (sessionId) => {
await sessionManager.deleteSession(sessionId);
}));

await Promise.all([
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 the group record after updating the `groups` record so that the state is consistent.
await db.remove(`group:${groupID}`);
};

Expand All @@ -86,22 +75,12 @@ exports.doesGroupExist = async (groupID) => {
};

exports.createGroup = async () => {
// search for non existing groupID
const groupID = `g.${randomString(16)}`;

// create the group
await db.set(`group:${groupID}`, {pads: {}});

// list the group
let groups = await exports.listAllGroups();
groups = groups ? groups.groupIDs : [];
groups.push(groupID);

// regenerate group list
const newGroups = {};
groups.forEach((group) => newGroups[group] = 1);
await db.set('groups', newGroups);

// Add the group to the `groups` record after the group's individual record is created so that
// the state is consistent. Note: UeberDB's setSub() method atomically reads the record, updates
// the appropriate property, and writes the result.
await db.setSub('groups', [groupID], 1);
return {groupID};
};

Expand Down

0 comments on commit 5b37a56

Please sign in to comment.