mirror of
https://github.com/node-red/node-red.git
synced 2023-10-10 13:36:53 +02:00
Handle persisting objects with circular refs in context
This commit is contained in:
parent
36e3bfffb4
commit
ef8b936069
@ -48,9 +48,12 @@
|
|||||||
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 log = require("../../log");
|
||||||
|
|
||||||
|
var safeJSONStringify = require("json-stringify-safe");
|
||||||
var MemoryStore = require("./memory");
|
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"){
|
||||||
@ -118,6 +121,12 @@ function listFiles(storagePath) {
|
|||||||
}).then(dirs => dirs.reduce((acc, val) => acc.concat(val), []));
|
}).then(dirs => dirs.reduce((acc, val) => acc.concat(val), []));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function stringify(value) {
|
||||||
|
var hasCircular;
|
||||||
|
var result = safeJSONStringify(value,null,4,function(k,v){hasCircular = true})
|
||||||
|
return { json: result, circular: hasCircular };
|
||||||
|
}
|
||||||
|
|
||||||
function LocalFileSystem(config){
|
function LocalFileSystem(config){
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.storageBaseDir = getBasePath(this.config);
|
this.storageBaseDir = getBasePath(this.config);
|
||||||
@ -125,6 +134,8 @@ function LocalFileSystem(config){
|
|||||||
this.cache = MemoryStore({});
|
this.cache = MemoryStore({});
|
||||||
}
|
}
|
||||||
this.pendingWrites = {};
|
this.pendingWrites = {};
|
||||||
|
this.knownCircularRefs = {};
|
||||||
|
|
||||||
if (config.hasOwnProperty('flushInterval')) {
|
if (config.hasOwnProperty('flushInterval')) {
|
||||||
this.flushInterval = Math.max(0,config.flushInterval) * 1000;
|
this.flushInterval = Math.max(0,config.flushInterval) * 1000;
|
||||||
} else {
|
} else {
|
||||||
@ -172,7 +183,15 @@ LocalFileSystem.prototype.open = function(){
|
|||||||
scopes.forEach(function(scope) {
|
scopes.forEach(function(scope) {
|
||||||
var storagePath = getStoragePath(self.storageBaseDir,scope);
|
var storagePath = getStoragePath(self.storageBaseDir,scope);
|
||||||
var context = newContext[scope];
|
var context = newContext[scope];
|
||||||
promises.push(fs.outputFile(storagePath + ".json", JSON.stringify(context, undefined, 4), "utf8"));
|
var stringifiedContext = stringify(context);
|
||||||
|
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
|
||||||
|
log.warn("Context "+scope+" contains a circular reference that cannot be persisted");
|
||||||
|
self.knownCircularRefs[scope] = true;
|
||||||
|
} else {
|
||||||
|
delete self.knownCircularRefs[scope];
|
||||||
|
}
|
||||||
|
log.debug("Flushing localfilesystem context scope "+scope);
|
||||||
|
promises.push(fs.outputFile(storagePath + ".json", stringifiedContext.json, "utf8"));
|
||||||
});
|
});
|
||||||
delete self._pendingWriteTimeout;
|
delete self._pendingWriteTimeout;
|
||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
@ -221,6 +240,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 self = this;
|
||||||
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
var storagePath = getStoragePath(this.storageBaseDir ,scope);
|
||||||
if (this.cache) {
|
if (this.cache) {
|
||||||
this.cache.set(scope,key,value,callback);
|
this.cache.set(scope,key,value,callback);
|
||||||
@ -229,7 +249,6 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
|
|||||||
// there's a pending write which will handle this
|
// there's a pending write which will handle this
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
var self = this;
|
|
||||||
this._pendingWriteTimeout = setTimeout(function() { self._flushPendingWrites.call(self)}, this.flushInterval);
|
this._pendingWriteTimeout = setTimeout(function() { self._flushPendingWrites.call(self)}, this.flushInterval);
|
||||||
}
|
}
|
||||||
} else if (callback && typeof callback !== 'function') {
|
} else if (callback && typeof callback !== 'function') {
|
||||||
@ -251,7 +270,14 @@ LocalFileSystem.prototype.set = function(scope, key, value, callback) {
|
|||||||
}
|
}
|
||||||
util.setObjectProperty(obj,key[i],v);
|
util.setObjectProperty(obj,key[i],v);
|
||||||
}
|
}
|
||||||
return fs.outputFile(storagePath + ".json", JSON.stringify(obj, undefined, 4), "utf8");
|
var stringifiedContext = stringify(obj);
|
||||||
|
if (stringifiedContext.circular && !self.knownCircularRefs[scope]) {
|
||||||
|
log.warn("Context "+scope+" contains a circular reference that cannot be persisted");
|
||||||
|
self.knownCircularRefs[scope] = true;
|
||||||
|
} else {
|
||||||
|
delete self.knownCircularRefs[scope];
|
||||||
|
}
|
||||||
|
return fs.outputFile(storagePath + ".json", stringifiedContext.json, "utf8");
|
||||||
}).then(function(){
|
}).then(function(){
|
||||||
if(typeof callback === "function"){
|
if(typeof callback === "function"){
|
||||||
callback(null);
|
callback(null);
|
||||||
@ -308,6 +334,7 @@ LocalFileSystem.prototype.clean = function(_activeNodes) {
|
|||||||
} else {
|
} else {
|
||||||
cachePromise = Promise.resolve();
|
cachePromise = Promise.resolve();
|
||||||
}
|
}
|
||||||
|
this.knownCircularRefs = {};
|
||||||
return cachePromise.then(() => listFiles(self.storageBaseDir)).then(function(files) {
|
return cachePromise.then(() => listFiles(self.storageBaseDir)).then(function(files) {
|
||||||
var promises = [];
|
var promises = [];
|
||||||
files.forEach(function(file) {
|
files.forEach(function(file) {
|
||||||
|
@ -46,9 +46,12 @@ describe('localfilesystem',function() {
|
|||||||
|
|
||||||
it('should store property',function(done) {
|
it('should store property',function(done) {
|
||||||
context.get("nodeX","foo",function(err, value){
|
context.get("nodeX","foo",function(err, value){
|
||||||
|
if (err) { return done(err); }
|
||||||
should.not.exist(value);
|
should.not.exist(value);
|
||||||
context.set("nodeX","foo","test",function(err){
|
context.set("nodeX","foo","test",function(err){
|
||||||
|
if (err) { return done(err); }
|
||||||
context.get("nodeX","foo",function(err, value){
|
context.get("nodeX","foo",function(err, value){
|
||||||
|
if (err) { return done(err); }
|
||||||
value.should.be.equal("test");
|
value.should.be.equal("test");
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user