Add caching to localfilesystem context

This commit is contained in:
Nick O'Leary 2018-07-02 22:32:20 +01:00
parent 7423583508
commit 43d7c8d48c
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
3 changed files with 140 additions and 32 deletions

View File

@ -29,7 +29,6 @@ module.exports = {
var scope = req.params.scope; var scope = req.params.scope;
var id = req.params.id; var id = req.params.id;
var key = req.params[0]; var key = req.params[0];
var result = {};
var ctx; var ctx;
if (scope === 'global') { if (scope === 'global') {
ctx = redNodes.getContext('global'); ctx = redNodes.getContext('global');
@ -43,19 +42,28 @@ module.exports = {
} }
if (ctx) { if (ctx) {
if (key) { if (key) {
result = util.encodeObject({msg:ctx.get(key)}); ctx.get(key,function(err, v) {
console.log(key,v);
res.json(util.encodeObject({msg:v}));
});
return;
} else { } else {
var keys = ctx.keys(); ctx.keys(function(err, keys) {
var result = {};
var i = 0; var c = keys.length;
var l = keys.length; keys.forEach(function(key) {
while(i < l) { ctx.get(key,function(err, v) {
var k = keys[i]; result[key] = util.encodeObject({msg:v});
result[k] = util.encodeObject({msg:ctx.get(k)}); c--;
i++; if (c === 0) {
} res.json(result);
}
});
});
});
} }
} else {
res.json({});
} }
res.json(result);
} }
} }

View File

@ -14,10 +14,39 @@
* limitations under the License. * limitations under the License.
**/ **/
/**
* Local file-system based context storage
*
* Configuration options:
* {
* base: "contexts", // the base directory to use
* // default: "contexts"
* dir: "/path/to/storage", // the directory to create the base directory in
* // default: settings.userDir
* cache: true // whether to cache contents in memory
* // default: true
* }
*
*
* $HOME/.node-red/contexts
* global
* global_context.json
* <id of Flow 1>
* flow_context.json
* <id of Node a>.json
* <id of Node b>.json
* <id of Flow 2>
* flow_context.json
* <id of Node x>.json
* <id of Node y>.json
*/
var fs = require('fs-extra'); var fs = require('fs-extra');
var path = require("path"); var path = require("path");
var util = require("../../util"); var util = require("../../util");
var MemoryStore = require("./memory");
function getStoragePath(storageBaseDir, scope) { function getStoragePath(storageBaseDir, scope) {
if(scope.indexOf(":") === -1){ if(scope.indexOf(":") === -1){
if(scope === "global"){ if(scope === "global"){
@ -76,10 +105,48 @@ function loadFile(storagePath){
function LocalFileSystem(config){ function LocalFileSystem(config){
this.config = config; this.config = config;
this.storageBaseDir = getBasePath(this.config); this.storageBaseDir = getBasePath(this.config);
if (config.hasOwnProperty('cache')?config.cache:true) {
this.cache = MemoryStore({});
}
} }
LocalFileSystem.prototype.open = function(){ LocalFileSystem.prototype.open = function(){
return Promise.resolve(); var self = this;
if (this.cache) {
var scopes = [];
var promises = [];
var subdirs = [];
var subdirPromises = [];
return fs.readdir(self.storageBaseDir).then(function(dirs){
dirs.forEach(function(fn) {
var p = getStoragePath(self.storageBaseDir ,fn)+".json";
scopes.push(fn);
promises.push(loadFile(p));
subdirs.push(path.join(self.storageBaseDir,fn));
subdirPromises.push(fs.readdir(path.join(self.storageBaseDir,fn)));
})
return Promise.all(subdirPromises);
}).then(function(dirs) {
dirs.forEach(function(files,i) {
files.forEach(function(fn) {
if (fn !== 'flow.json' && fn !== 'global.json') {
scopes.push(fn.substring(0,fn.length-5)+":"+scopes[i]);
promises.push(loadFile(path.join(subdirs[i],fn)))
}
});
})
return Promise.all(promises);
}).then(function(res) {
scopes.forEach(function(scope,i) {
var data = res[i]?JSON.parse(res[i]):{};
Object.keys(data).forEach(function(key) {
self.cache.set(scope,key,data[key]);
})
});
})
} else {
return Promise.resolve();
}
} }
LocalFileSystem.prototype.close = function(){ LocalFileSystem.prototype.close = function(){
@ -87,6 +154,9 @@ LocalFileSystem.prototype.close = function(){
} }
LocalFileSystem.prototype.get = function(scope, key, callback) { LocalFileSystem.prototype.get = function(scope, key, callback) {
if (this.cache) {
return this.cache.get(scope,key,callback);
}
if(typeof callback !== "function"){ if(typeof callback !== "function"){
throw new Error("Callback must be a function"); throw new Error("Callback must be a function");
} }
@ -102,7 +172,7 @@ LocalFileSystem.prototype.get = function(scope, key, callback) {
}); });
}; };
LocalFileSystem.prototype.set =function(scope, key, value, callback) { LocalFileSystem.prototype._set = function(scope, key, value, callback) {
var storagePath = getStoragePath(this.storageBaseDir ,scope); var storagePath = getStoragePath(this.storageBaseDir ,scope);
loadFile(storagePath + ".json").then(function(data){ loadFile(storagePath + ".json").then(function(data){
var obj = data ? JSON.parse(data) : {} var obj = data ? JSON.parse(data) : {}
@ -117,9 +187,23 @@ LocalFileSystem.prototype.set =function(scope, key, value, callback) {
callback(err); callback(err);
} }
}); });
}
LocalFileSystem.prototype.set = function(scope, key, value, callback) {
if (this.cache) {
this.cache.set(scope,key,value,callback);
this._set(scope,key,value, function(err) {
// TODO: log any errors
});
} else {
this._set(scope,key,value,callback);
}
}; };
LocalFileSystem.prototype.keys = function(scope, callback){ LocalFileSystem.prototype.keys = function(scope, callback){
if (this.cache) {
return this.cache.keys(scope,callback);
}
if(typeof callback !== "function"){ if(typeof callback !== "function"){
throw new Error("Callback must be a function"); throw new Error("Callback must be a function");
} }
@ -136,29 +220,45 @@ LocalFileSystem.prototype.keys = function(scope, callback){
}; };
LocalFileSystem.prototype.delete = function(scope){ LocalFileSystem.prototype.delete = function(scope){
var storagePath = getStoragePath(this.storageBaseDir ,scope); var cachePromise;
return fs.remove(storagePath + ".json"); if (this.cache) {
cachePromise = this.cache.delete(scope);
} else {
cachePromise = Promise.resolve();
}
var that = this;
return cachePromise.then(function() {
var storagePath = getStoragePath(that.storageBaseDir,scope);
return fs.remove(storagePath + ".json");
});
} }
LocalFileSystem.prototype.clean = function(activeNodes){ LocalFileSystem.prototype.clean = function(activeNodes){
var self = this; var self = this;
return fs.readdir(self.storageBaseDir).then(function(dirs){ var cachePromise;
return Promise.all(dirs.reduce(function(result, item){ if (this.cache) {
if(item !== "global" && activeNodes.indexOf(item) === -1){ cachePromise = this.cache.clean(activeNodes);
result.push(fs.remove(path.join(self.storageBaseDir,item))); } else {
cachePromise = Promise.resolve();
}
return cachePromise.then(function() {
return fs.readdir(self.storageBaseDir).then(function(dirs){
return Promise.all(dirs.reduce(function(result, item){
if(item !== "global" && activeNodes.indexOf(item) === -1){
result.push(fs.remove(path.join(self.storageBaseDir,item)));
}
return result;
},[]));
}).catch(function(err){
if(err.code == 'ENOENT') {
return Promise.resolve();
}else{
return Promise.reject(err);
} }
return result; });
},[]));
}).catch(function(err){
if(err.code == 'ENOENT') {
return Promise.resolve();
}else{
return Promise.reject(err);
}
}); });
} }
module.exports = function(config){ module.exports = function(config){
return new LocalFileSystem(config); return new LocalFileSystem(config);
}; };

View File

@ -29,7 +29,7 @@ describe('localfilesystem',function() {
}); });
beforeEach(function() { beforeEach(function() {
context = LocalFileSystem({dir: resourcesDir}); context = LocalFileSystem({dir: resourcesDir, cache: false});
return context.open(); return context.open();
}); });
@ -308,7 +308,7 @@ describe('localfilesystem',function() {
done(); done();
}); });
}); });
}); }).catch(done);
}); });
}); });
}); });
@ -375,4 +375,4 @@ describe('localfilesystem',function() {
}); });
}); });
}); });
}); });