From fd86035865b0da7bf92fee2e9872a4039cc6054e Mon Sep 17 00:00:00 2001 From: Hiroki Uchikawa <31908137+HirokiUchikawa@users.noreply.github.com> Date: Mon, 17 Sep 2018 05:15:23 +0900 Subject: [PATCH] Prevent race condition (#1889) * Make pending Flag to be deleted after write process complete. * Prevent executing write process until the previous process is completed * Fix to prevent file write race condition when closing file context * Make flushing rerun if pendingWrites was added --- red/runtime/nodes/context/localfilesystem.js | 35 ++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/red/runtime/nodes/context/localfilesystem.js b/red/runtime/nodes/context/localfilesystem.js index 548c98c60..ee07dd26b 100644 --- a/red/runtime/nodes/context/localfilesystem.js +++ b/red/runtime/nodes/context/localfilesystem.js @@ -142,6 +142,7 @@ function LocalFileSystem(config){ this.storageBaseDir = getBasePath(this.config); if (config.hasOwnProperty('cache')?config.cache:true) { this.cache = MemoryStore({}); + this.writePromise = Promise.resolve(); } this.pendingWrites = {}; this.knownCircularRefs = {}; @@ -203,8 +204,22 @@ LocalFileSystem.prototype.open = function(){ log.debug("Flushing localfilesystem context scope "+scope); promises.push(fs.outputFile(storagePath + ".json", stringifiedContext.json, "utf8")); }); - delete self._pendingWriteTimeout; - return Promise.all(promises); + return Promise.all(promises).then(function(){ + if(Object.keys(self.pendingWrites).length > 0){ + // Rerun flushing if pendingWrites was added when the promise was running + return new Promise(function(resolve, reject) { + setTimeout(function() { + self._flushPendingWrites.call(self).then(function(){ + resolve(); + }).catch(function(err) { + reject(err); + }); + }, self.flushInterval); + }); + } else { + delete self._pendingWriteTimeout; + } + }); } }); } else { @@ -213,10 +228,16 @@ LocalFileSystem.prototype.open = function(){ } LocalFileSystem.prototype.close = function(){ - if (this.cache && this._flushPendingWrites) { + var self = this; + if (this.cache && this._pendingWriteTimeout) { clearTimeout(this._pendingWriteTimeout); delete this._pendingWriteTimeout; - return this._flushPendingWrites(); + this.flushInterval = 0; + return this.writePromise.then(function(){ + if(Object.keys(self.pendingWrites).length > 0) { + return self._flushPendingWrites(); + } + }); } return Promise.resolve(); } @@ -277,8 +298,10 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) { return; } else { this._pendingWriteTimeout = setTimeout(function() { - self._flushPendingWrites.call(self).catch(function(err) { - log.error(log._("context.localfilesystem.error-write",{message:err.toString()})) + self.writePromise = self.writePromise.then(function(){ + return self._flushPendingWrites.call(self).catch(function(err) { + log.error(log._("context.localfilesystem.error-write",{message:err.toString()})); + }); }); }, this.flushInterval); }