From c3e0f5d0991676ee328ef115684b57f09635d7ef Mon Sep 17 00:00:00 2001 From: Ruben Verborgh Date: Mon, 7 Sep 2020 21:07:51 +0200 Subject: [PATCH] Use in-memory lock. --- lib/handlers/patch.js | 2 +- lib/ldp.js | 8 ++++---- lib/lock.js | 33 +++++---------------------------- package-lock.json | 20 +++++--------------- package.json | 2 +- 5 files changed, 16 insertions(+), 49 deletions(-) diff --git a/lib/handlers/patch.js b/lib/handlers/patch.js index fd4e60162..60a0b88ed 100644 --- a/lib/handlers/patch.js +++ b/lib/handlers/patch.js @@ -64,7 +64,7 @@ async function patchHandler (req, res, next) { } // Patch the graph and write it back to the file - const result = await withLock(path, { mustExist: false }, async () => { + const result = await withLock(path, async () => { const graph = await readGraph(resource) await applyPatch(patchObject, graph, url) return writeGraph(graph, resource, ldp.resourceMapper.resolveFilePath(req.hostname), ldp.serverUri) diff --git a/lib/ldp.js b/lib/ldp.js index d67f291a2..08bc7660f 100644 --- a/lib/ldp.js +++ b/lib/ldp.js @@ -251,7 +251,7 @@ class LDP { } // Directory created, now write the file - return withLock(path, { mustExist: false }, () => new Promise((resolve, reject) => { + return withLock(path, () => new Promise((resolve, reject) => { // HACK: the middleware in webid-oidc.js uses body-parser, thus ending the stream of data // for JSON bodies. So, the stream needs to be reset if (contentType.includes('application/json')) { @@ -445,15 +445,15 @@ class LDP { const linkPath = this.resourceMapper._removeDollarExtension(path) try { // first delete file, then links with write permission only (atomic delete) - await withLock(path, { mustExist: false }, () => promisify(fs.unlink)(path)) + await withLock(path, () => promisify(fs.unlink)(path)) const aclPath = `${linkPath}${this.suffixAcl}` if (await promisify(fs.exists)(aclPath)) { - await withLock(aclPath, { mustExist: false }, () => promisify(fs.unlink)(aclPath)) + await withLock(aclPath, () => promisify(fs.unlink)(aclPath)) } const metaPath = `${linkPath}${this.suffixMeta}` if (await promisify(fs.exists)(metaPath)) { - await withLock(metaPath, { mustExist: false }, () => promisify(fs.unlink)(metaPath)) + await withLock(metaPath, () => promisify(fs.unlink)(metaPath)) } } catch (err) { debug.container('DELETE -- unlink() error: ' + err) diff --git a/lib/lock.js b/lib/lock.js index 135229aa7..2cb6c8d6d 100644 --- a/lib/lock.js +++ b/lib/lock.js @@ -1,33 +1,10 @@ -/* eslint-disable no-async-promise-executor */ +const AsyncLock = require('async-lock') -const { lock } = require('proper-lockfile') +const lock = new AsyncLock({ timeout: 30 * 1000 }) -const staleSeconds = 30 - -// Obtains a lock on the path, and maintains it until the callback finishes -function withLock (path, options = {}, callback = options) { - return new Promise(async (resolve, reject) => { - let releaseLock, result - try { - // Obtain the lock - releaseLock = await lock(path, { - retries: 10, - update: 1000, - stale: staleSeconds * 1000, - realpath: !!options.mustExist, - onCompromised: () => - reject(new Error(`The file at ${path} was not updated within ${staleSeconds}s.`)) - }) - // Hold on to the lock until the callback's returned promise resolves - result = await callback() - } catch (error) { - reject(error) - // Ensure the lock is always released - } finally { - await releaseLock() - } - resolve(result) - }) +// Obtains a lock on the path, and maintains it until the task finishes +async function withLock (path, executeTask) { + return await lock.acquire(path, executeTask) } module.exports = withLock diff --git a/package-lock.json b/package-lock.json index 63737a861..25e47d948 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1912,6 +1912,11 @@ "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz", "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw==" }, + "async-lock": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.2.4.tgz", + "integrity": "sha512-UBQJC2pbeyGutIfYmErGc9RaJYnpZ1FHaxuKwb0ahvGiiCkPUf3p67Io+YLPmmv3RHY+mF6JEtNW8FlHsraAaA==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -9082,16 +9087,6 @@ "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", "dev": true }, - "proper-lockfile": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.1.tgz", - "integrity": "sha512-1w6rxXodisVpn7QYvLk706mzprPTAPCYAqxMvctmPN3ekuRk/kuGkGc82pangZiAt4R3lwSuUzheTTn0/Yb7Zg==", - "requires": { - "graceful-fs": "^4.1.11", - "retry": "^0.12.0", - "signal-exit": "^3.0.2" - } - }, "proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -9684,11 +9679,6 @@ "signal-exit": "^3.0.2" } }, - "retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", diff --git a/package.json b/package.json index f6bc384fb..8295dc693 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "dependencies": { "@solid/acl-check": "^0.3.0", "@solid/oidc-auth-manager": "^0.22.0", + "async-lock": "^1.2.4", "body-parser": "^1.19.0", "bootstrap": "^3.4.1", "busboy": "^0.3.1", @@ -92,7 +93,6 @@ "nodemailer": "^6.4.11", "oidc-op-express": "^0.0.3", "owasp-password-strength-test": "^1.3.0", - "proper-lockfile": "^4.1.1", "rdflib": "^1.3.1", "recursive-readdir": "^2.2.2", "rimraf": "^3.0.2",