From 22c9cb93ebcb8780bf02c0ae46b5e3015cb940b0 Mon Sep 17 00:00:00 2001 From: Ray Bellis Date: Fri, 1 Feb 2019 09:57:50 +0000 Subject: [PATCH] allow some operations to proceed in parallel some code chunks previously used `async.parallel` but if you use `await` that forces them to be run serially. Instead, you can initiate the operation (getting a Promise) and then _later_ `await` the result of that Promise. --- src/node/db/Pad.js | 14 ++++++++++---- src/node/db/SecurityManager.js | 24 +++++++++++++++--------- src/node/handler/PadMessageHandler.js | 18 ++++++++++-------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/src/node/db/Pad.js b/src/node/db/Pad.js index fb36bdf5812..6c97fee8dd6 100644 --- a/src/node/db/Pad.js +++ b/src/node/db/Pad.js @@ -169,9 +169,8 @@ Pad.prototype.getInternalRevisionAText = async function getInternalRevisionAText // get all needed data out of the database - // get the atext of the key revision - let _atext = await db.getSub("pad:" + this.id + ":revs:" + keyRev, ["meta", "atext"]); - let atext = Changeset.cloneAText(_atext); + // start to get the atext of the key revision + let p_atext = db.getSub("pad:" + this.id + ":revs:" + keyRev, ["meta", "atext"]); // get all needed changesets let changesets = []; @@ -181,6 +180,10 @@ Pad.prototype.getInternalRevisionAText = async function getInternalRevisionAText }); })); + // we should have the atext by now + let atext = await p_atext; + atext = Changeset.cloneAText(atext); + // apply all changesets to the key changeset let apool = this.apool(); for (let curRev = keyRev; curRev < targetRev; ) { @@ -455,7 +458,10 @@ Pad.prototype.remove = async function remove() { // kick everyone from this pad padMessageHandler.kickSessionsFromPad(padID); - // delete all relations + // delete all relations - the original code used async.parallel but + // none of the operations except getting the group depended on callbacks + // so the database operations here are just started and then left to + // run to completion // is it a group pad? -> delete the entry of this pad in the group if (padID.indexOf("$") >= 0) { diff --git a/src/node/db/SecurityManager.js b/src/node/db/SecurityManager.js index a94b36f60da..8811432b028 100644 --- a/src/node/db/SecurityManager.js +++ b/src/node/db/SecurityManager.js @@ -49,11 +49,11 @@ exports.checkAccess = async function(padID, sessionCookie, token, password) return deny; } - // get author for this token - let tokenAuthor = await authorManager.getAuthor4Token(token); + // start to get author for this token + let p_tokenAuthor = authorManager.getAuthor4Token(token); - // check if pad exists - let padExists = await padManager.doesPadExist(padID); + // start to check if pad exists + let p_padExists = padManager.doesPadExist(padID); // a valid session is required (api-only mode) if (settings.requireSession) { @@ -68,11 +68,13 @@ exports.checkAccess = async function(padID, sessionCookie, token, password) // it's not a group pad, means we can grant access if (padID.indexOf("$") === -1) { // assume user has access - let statusObject = { accessStatus: "grant", authorID: tokenAuthor }; + let authorID = await p_tokenAuthor; + let statusObject = { accessStatus: "grant", authorID }; // user can't create pads if (settings.editOnly) { // pad doesn't exist - user can't have access + let padExists = await p_padExists; if (!padExists) statusObject.accessStatus = "deny"; } @@ -94,10 +96,13 @@ exports.checkAccess = async function(padID, sessionCookie, token, password) let sessionIDs = sessionCookie.split(','); // was previously iterated in parallel using async.forEach - for (let sessionID of sessionIDs) { - try { - let sessionInfo = await sessionManager.getSessionInfo(sessionID); + let sessionInfos = await Promise.all(sessionIDs.map(sessionID => { + return sessionManager.getSessionInfo(sessionID); + })); + // seperated out the iteration of sessioninfos from the (parallel) fetches from the DB + for (let sessionInfo of sessionInfos) { + try { // is it for this group? if (sessionInfo.groupID != groupID) { authLogger.debug("Auth failed: wrong group"); @@ -126,6 +131,7 @@ exports.checkAccess = async function(padID, sessionCookie, token, password) } } + let padExists = await p_padExists; if (padExists) { let pad = await padManager.getPad(padID); @@ -203,7 +209,7 @@ exports.checkAccess = async function(padID, sessionCookie, token, password) if (!validSession && padExists) { // there is no valid session avaiable AND pad exists - let authorID = tokenAuthor; + let authorID = await p_tokenAuthor; let grant = Object.freeze({ accessStatus: "grant", authorID }); if (isPublic && !isPasswordProtected) { diff --git a/src/node/handler/PadMessageHandler.js b/src/node/handler/PadMessageHandler.js index 1c7b44cfc3d..18b08af5bce 100644 --- a/src/node/handler/PadMessageHandler.js +++ b/src/node/handler/PadMessageHandler.js @@ -890,8 +890,6 @@ async function handleClientReady(client, message) return; } - var historicalAuthorData = {}; - hooks.callAll("clientReady", message); // Get ro/rw id:s @@ -915,12 +913,12 @@ async function handleClientReady(client, message) let author = statusObject.authorID; - // get all authordata of this new user, and load the pad-object from the database + // get all authordata of this new user let value = await authorManager.getAuthor(author); let authorColorId = value.colorId; let authorName = value.name; - // get pad + // load the pad-object from the database let pad = await padManager.getPad(padIds.padId); // these db requests all need the pad object (timestamp of latest revision, author data) @@ -930,6 +928,7 @@ async function handleClientReady(client, message) let currentTime = await pad.getRevisionDate(pad.getHeadRevisionNumber()); // get all author data out of the database (in parallel) + let historicalAuthorData = {}; await Promise.all(authors.map(authorId => { return authorManager.getAuthor(authorId).then(author => { if (!author) { @@ -1010,12 +1009,15 @@ async function handleClientReady(client, message) changesets[r] = {}; } - // get changesets, author and timestamp needed for pending revisions + // get changesets, author and timestamp needed for pending revisions (in parallel) + let promises = []; for (let revNum of revisionsNeeded) { - changesets[revNum]['changeset'] = await pad.getRevisionChangeset(revNum); - changesets[revNum]['author'] = await pad.getRevisionAuthor(revNum); - changesets[revNum]['timestamp'] = await pad.getRevisionDate(revNum); + let cs = changesets[revNum]; + promises.push( pad.getRevisionChangeset(revNum).then(result => cs.changeset = result )); + promises.push( pad.getRevisionAuthor(revNum).then(result => cs.author = result )); + promises.push( pad.getRevisionDate(revNum).then(result => cs.timestamp = result )); } + await Promise.all(promises); // return pending changesets for (let r of revisionsNeeded) {