From fd90538fcc63fa55be516fa29c53ca82c5c98a23 Mon Sep 17 00:00:00 2001 From: Tom Smeding Date: Thu, 18 Jun 2020 22:15:29 +0200 Subject: blog: Fix rendering race condition with multiple clients --- modules/blog/blog.js | 43 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/modules/blog/blog.js b/modules/blog/blog.js index 0b7785b..b6085d9 100644 --- a/modules/blog/blog.js +++ b/modules/blog/blog.js @@ -9,7 +9,23 @@ let moddir = null; let repodir = null; const repoRemote = "https://git.tomsmeding.com/blog"; +// Cache for rendered posts; null if currently being rendered let templateCache = new Map(); +// Clients that requested a post that is currently being rendered; values are +// callbacks that take (http status code, rendered html [= null if status != 200]) +let renderWatchers = new Map(); + +function triggerRenderWatchers(path, statusCode, rendered) { + const list = renderWatchers.get(path); + if (list !== undefined) { + try { + for (const callback of list) callback(statusCode, rendered); + } catch(e) { + console.error("While triggering render watchers:", e); + } + renderWatchers.delete(path); + } +} function fetch(url) { return new Promise((resolve, reject) => { @@ -111,21 +127,38 @@ module.exports = (app, io, _moddir) => { return; } - const path = req.path.slice(6).replace(/\.html$/, ""); - - if (templateCache.has(path)) { - res.send(templateCache.get(path)); + const path = req.path + .slice(6) + .replace(/\/[\/]*/g, "/") + .replace(/\.html$/, ""); + + const fromCache = templateCache.get(path); + + if (fromCache != null) { // neither null nor undefined + res.send(fromCache); + } else if (fromCache !== undefined) { + // Is currently being renderered for another client + if (!renderWatchers.has(path)) renderWatchers.set(path, []); + renderWatchers.get(path).push((statusCode, rendered) => { + if (statusCode == 200) res.send(rendered); + else res.sendStatus(statusCode); + }); } else { + // Indicate that this path is currently being rendered + templateCache.set(path, null); + generateTemplate(repodir, path ? path + ".html" : undefined) .then(rendered => { - // TODO: fix rendering race condition templateCache.set(path, rendered); + triggerRenderWatchers(path, 200, rendered); res.send(rendered); }) .catch(err => { if (err.code && err.code == "ENOENT") { + triggerRenderWatchers(path, 400, null); res.sendStatus(404); } else { + triggerRenderWatchers(path, 500, null); console.error(err); res.sendStatus(500); } -- cgit v1.2.3