fix SOME of RAD due to GUN queue growing too deep

This commit is contained in:
Mark Nadal 2019-09-10 14:40:34 -07:00
parent a34e90e868
commit 6af5b6c2ac
9 changed files with 62 additions and 37 deletions

29
gun.js
View File

@ -1235,7 +1235,7 @@
gun = gun.$; gun = gun.$;
} else } else
if(key instanceof Function){ if(key instanceof Function){
if(true === cb){ return soul(this, key, cb, as) } if(true === cb){ return soul(this, key, cb, as), this }
gun = this; gun = this;
var at = gun._, root = at.root, tmp = root.now, ev; var at = gun._, root = at.root, tmp = root.now, ev;
as = cb || {}; as = cb || {};
@ -1292,15 +1292,22 @@
} }
function soul(gun, cb, opt, as){ function soul(gun, cb, opt, as){
var cat = gun._, acks = 0, tmp; var cat = gun._, acks = 0, tmp;
if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat), gun } if(tmp = cat.soul || cat.link || cat.dub){ return cb(tmp, as, cat) }
gun.get(function(msg, ev){ if(cat.jam){ return cat.jam.push([cb, as]) }
cat.jam = [[cb,as]];
gun.get(function(msg, eve){
if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){ if(u === msg.put && (tmp = Object.keys(cat.root.opt.peers).length) && ++acks < tmp){
return; return;
} }
ev.rid(msg); eve.rid(msg);
var at = ((at = msg.$) && at._) || {}; var at = ((at = msg.$) && at._) || {};
tmp = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub; tmp = cat.jam; Gun.obj.del(cat, 'jam');
cb(tmp, as, msg, ev); Gun.obj.map(tmp, function(as, cb){
cb = as[0]; as = as[1];
if(!cb){ return }
var id = at.link || at.soul || rel.is(msg.put) || node_soul(msg.put) || at.dub;
cb(id, as, msg, eve);
});
}, {out: {get: {'.':true}}}); }, {out: {get: {'.':true}}});
return gun; return gun;
} }
@ -1361,9 +1368,9 @@
Gun.chain.put = function(data, cb, as){ Gun.chain.put = function(data, cb, as){
// #soul.has=value>state // #soul.has=value>state
// ~who#where.where=what>when@was // ~who#where.where=what>when@was
// TODO: BUG! Put probably cannot handle plural chains! // TODO: BUG! Put probably cannot handle plural chains! `!as` is quickfix test.
var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp; var gun = this, at = (gun._), root = at.root.$, ctx = root._, M = 100, tmp;
if(!ctx.puta){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K. /*if(!ctx.puta && !as){ if(tmp = ctx.puts){ if(tmp > M){ // without this, when synchronous, writes to a 'not found' pile up, when 'not found' resolves it recursively calls `put` which incrementally resolves each write. Stack overflow limits can be as low as 10K, so this limit is hardcoded to 1% of 10K.
(ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]); (ctx.stack || (ctx.stack = [])).push([gun, data, cb, as]);
if(ctx.puto){ return } if(ctx.puto){ return }
ctx.puto = setTimeout(function drain(){ ctx.puto = setTimeout(function drain(){
@ -1373,7 +1380,7 @@
ctx.stack = ctx.puts = ctx.puto = null; ctx.stack = ctx.puts = ctx.puto = null;
}, 0); }, 0);
return gun; return gun;
} ++ctx.puts } else { ctx.puts = 1 } } } ++ctx.puts } else { ctx.puts = 1 } }*/
as = as || {}; as = as || {};
as.data = data; as.data = data;
as.via = as.$ = as.via || as.$ || gun; as.via = as.$ = as.via || as.$ || gun;
@ -1496,6 +1503,7 @@
ref = ref.get(path[i]); ref = ref.get(path[i]);
} }
if(is){ ref = v } if(is){ ref = v }
//if(as.not){ (ref._).dub = Gun.text.random() } // This might optimize stuff? Maybe not needed anymore. Make sure it doesn't introduce bugs.
var id = (ref._).dub; var id = (ref._).dub;
if(id || (id = Gun.node.soul(at.obj))){ if(id || (id = Gun.node.soul(at.obj))){
ref.back(-1).get(id); ref.back(-1).get(id);
@ -1556,6 +1564,7 @@
if(at.link || at.soul){ return at.link || at.soul } if(at.link || at.soul){ return at.link || at.soul }
as.data = obj_put({}, at.get, as.data); as.data = obj_put({}, at.get, as.data);
}); });
as.not = true; // maybe consider this?
} }
tmp = tmp || at.soul || at.link || at.dub;// || at.get; tmp = tmp || at.soul || at.link || at.dub;// || at.get;
at = tmp? (at.root.$.get(tmp)._) : at; at = tmp? (at.root.$.get(tmp)._) : at;
@ -1815,7 +1824,7 @@
// See the next 'opt' code below for actual saving of data. // See the next 'opt' code below for actual saving of data.
var ev = this.to, opt = root.opt; var ev = this.to, opt = root.opt;
if(root.once){ return ev.next(root) } if(root.once){ return ev.next(root) }
//if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! if(false === opt.localStorage){ return ev.next(root) } // we want offline resynce queue regardless! // actually, this doesn't help, per @go1dfish 's observation. Disabling for now, will need better solution later.
opt.prefix = opt.file || 'gun/'; opt.prefix = opt.file || 'gun/';
var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {}; var gap = Gun.obj.ify(store.getItem('gap/'+opt.prefix)) || {};
var empty = Gun.obj.empty, id, to, go; var empty = Gun.obj.empty, id, to, go;

2
gun.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -14,12 +14,12 @@
opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB opt.chunk = opt.chunk || (1024 * 1024 * 10); // 10MB
opt.code = opt.code || {}; opt.code = opt.code || {};
opt.code.from = opt.code.from || '!'; opt.code.from = opt.code.from || '!';
//opt.jsonify = true; // TODO: REMOVE!!!! //opt.jsonify = true; if(opt.jsonify){ console.log("JSON RAD!!!") } // TODO: REMOVE!!!!
function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') } function ename(t){ return encodeURIComponent(t).replace(/\*/g, '%2A') }
function atomic(v){ return u !== v && (!v || 'object' != typeof v) } function atomic(v){ return u !== v && (!v || 'object' != typeof v) }
var map = Gun.obj.map; var map = Gun.obj.map;
var LOG = false; var LOG = false;//true;
if(!opt.store){ if(!opt.store){
return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!"); return opt.log("ERROR: Radisk needs `opt.store` interface with `{get: fn, put: fn (, list: fn)}`!");
@ -83,12 +83,13 @@
r.batch = Radix(); r.batch = Radix();
r.batch.acks = []; r.batch.acks = [];
r.batch.ed = 0; r.batch.ed = 0;
//var id = Gun.text.random(2), S = (+new Date); console.log("<<<<<<<<<<<<", id); //console.debug(99); var ID = Gun.text.random(2), S = (+new Date); console.log("[[[[[[[[", ID, batch.acks.length);
r.save(batch, function(err, ok){ r.save(batch, function(err, ok){
if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return } if(++i > 1){ opt.log('RAD ERR: Radisk has callbacked multiple times, please report this as a BUG at github.com/amark/gun/issues ! ' + i); return }
if(err){ opt.log('err', err) } if(err){ opt.log('err', err) }
//console.log(">>>>>>>>>>>>", id, ((+new Date) - S), batch.acks.length); //console.debug(99); var TMP; console.log("]]]]]]]]", ID, batch.acks.length, (TMP = +new Date) - S, 'more?', thrash.more);
map(batch.acks, function(cb){ cb(err, ok) }); map(batch.acks, function(cb){ cb(err, ok) });
//console.log("][", +new Date - TMP, thrash.more);
thrash.at = null; thrash.at = null;
thrash.ing = false; thrash.ing = false;
if(thrash.more){ thrash() } if(thrash.more){ thrash() }
@ -171,9 +172,9 @@
} }
f.write = function(){ f.write = function(){
var tmp = ename(file); var tmp = ename(file);
var start; LOG && (start = (+new Date)); // comment this out! var start; LOG && (start = +new Date); // comment this out!
opt.store.put(tmp, f.text, function(err){ opt.store.put(tmp, f.text, function(err){
LOG && console.log("wrote JSON in", (+new Date) - start); // comment this out! LOG && console.log("wrote to disk in", (+new Date) - start, tmp); // comment this out!
if(err){ return cb(err) } if(err){ return cb(err) }
r.list.add(tmp, cb); r.list.add(tmp, cb);
}); });
@ -202,10 +203,10 @@
r.write.jsonify = function(f, file, rad, cb, o){ r.write.jsonify = function(f, file, rad, cb, o){
var raw; var raw;
var start; LOG && (start = (+new Date)); // comment this out! var start; LOG && (start = +new Date); // comment this out!
try{raw = JSON.stringify(rad.$); try{raw = JSON.stringify(rad.$);
}catch(e){ return cb("Record too big!") } }catch(e){ return cb("Record too big!") }
LOG && console.log("stringified JSON in", (+new Date) - start); // comment this out! LOG && console.log("stringified JSON in", +new Date - start); // comment this out!
if(opt.chunk < raw.length && !o.force){ if(opt.chunk < raw.length && !o.force){
if(Radix.map(rad, f.each, true)){ return } if(Radix.map(rad, f.each, true)){ return }
} }
@ -220,7 +221,7 @@
var sub = Radix(); var sub = Radix();
Radix.map(tree, function(v,k){ Radix.map(tree, function(v,k){
sub(k,v); sub(k,v);
}, o) }, o);
return sub(''); return sub('');
} }
@ -266,7 +267,7 @@
} }
g.ack = function(as){ g.ack = function(as){
if(!as.ack){ return } if(!as.ack){ return }
var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last; var tmp = as.key, o = as.opt, info = g.info, rad = g.disk || noop, data = r.range(rad(tmp), o), last = rad.last || Radix.map(rad, rev, revo);
o.parsed = (o.parsed || 0) + (info.parsed||0); o.parsed = (o.parsed || 0) + (info.parsed||0);
o.chunks = (o.chunks || 0) + 1; o.chunks = (o.chunks || 0) + 1;
if(!o.some){ o.some = (u !== data) } if(!o.some){ o.some = (u !== data) }
@ -283,6 +284,8 @@
if(o.reverse){ g.lex.reverse = true } if(o.reverse){ g.lex.reverse = true }
r.list(g.lex); r.list(g.lex);
} }
function rev(a,b){ return b }
var revo = {reverse: true};
}()); }());
;(function(){ ;(function(){
@ -299,6 +302,7 @@
var p = function Parse(){}, info = {}; var p = function Parse(){}, info = {};
p.disk = Radix(); p.disk = Radix();
p.read = function(err, data){ var tmp; p.read = function(err, data){ var tmp;
LOG && console.log('read disk in', +new Date - start, ename(file)); // keep this commented out in
delete Q[file]; delete Q[file];
if((p.err = err) || (p.not = !data)){ if((p.err = err) || (p.not = !data)){
return map(q, p.ack); return map(q, p.ack);
@ -315,12 +319,12 @@
} }
info.parsed = data.length; info.parsed = data.length;
var start; LOG && (start = (+new Date)); // keep this commented out in production! LOG && (start = +new Date); // keep this commented out in production!
if(opt.jsonify){ // temporary testing idea if(opt.jsonify){ // temporary testing idea
try{ try{
var json = JSON.parse(data); var json = JSON.parse(data);
p.disk.$ = json; p.disk.$ = json;
LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production! LOG && console.log('parsed JSON in', +new Date - start); // keep this commented out in production!
map(q, p.ack); map(q, p.ack);
return; return;
}catch(e){ tmp = e } }catch(e){ tmp = e }
@ -329,7 +333,7 @@
return map(q, p.ack); return map(q, p.ack);
} }
} }
var start; LOG && (start = (+new Date)); // keep this commented out in production! LOG && (start = +new Date); // keep this commented out in production!
var tmp = p.split(data), pre = [], i, k, v; var tmp = p.split(data), pre = [], i, k, v;
if(!tmp || 0 !== tmp[1]){ if(!tmp || 0 !== tmp[1]){
p.err = "File '"+file+"' does not have root radix! "; p.err = "File '"+file+"' does not have root radix! ";
@ -352,7 +356,7 @@
if(u !== k && u !== v){ p.disk(pre.join(''), v) } if(u !== k && u !== v){ p.disk(pre.join(''), v) }
tmp = p.split(tmp[2]); tmp = p.split(tmp[2]);
} }
LOG && console.log('parsed JSON in', (+new Date) - start); // keep this commented out in production! LOG && console.log('parsed RAD in', +new Date - start); // keep this commented out in production!
//cb(err, p.disk); //cb(err, p.disk);
map(q, p.ack); map(q, p.ack);
}; };
@ -372,6 +376,7 @@
if(p.err || p.not){ return cb(p.err, u, info) } if(p.err || p.not){ return cb(p.err, u, info) }
cb(u, p.disk, info); cb(u, p.disk, info);
} }
var start; LOG && (start = +new Date); // keep this commented out in production!
if(raw){ return p.read(null, raw) } if(raw){ return p.read(null, raw) }
opt.store.get(ename(file), p.read); opt.store.get(ename(file), p.read);
} }
@ -504,6 +509,7 @@
} else { } else {
var Gun = require('../gun'); var Gun = require('../gun');
var Radix = require('./radix'); var Radix = require('./radix');
//var Radix = require('./radix2'); Radisk = require('./radisk2');
try{ module.exports = Radisk }catch(e){} try{ module.exports = Radisk }catch(e){}
} }

View File

@ -54,10 +54,10 @@
Radix.map = function map(radix, cb, opt, pre){ pre = pre || []; Radix.map = function map(radix, cb, opt, pre){ pre = pre || [];
var t = ('function' == typeof radix)? radix.$ || {} : radix; var t = ('function' == typeof radix)? radix.$ || {} : radix;
if(!t){ return } if(!t){ return }
var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort; var keys = (t[_]||no).sort || (t[_] = function $(){ $.sort = Object.keys(t).sort(); return $ }()).sort, rev;
//var keys = Object.keys(t).sort(); //var keys = Object.keys(t).sort();
opt = (true === opt)? {branch: true} : (opt || {}); opt = (true === opt)? {branch: true} : (opt || {});
if(opt.reverse){ keys = keys.slice().reverse() } if(rev = opt.reverse){ keys = keys.slice().reverse() }
var start = opt.start, end = opt.end; var start = opt.start, end = opt.end;
var i = 0, l = keys.length; var i = 0, l = keys.length;
for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt; for(;i < l; i++){ var key = keys[i], tree = t[key], tmp, p, pt;
@ -66,6 +66,10 @@
pt = p.join(''); pt = p.join('');
if(u !== start && pt < (start||'').slice(0,pt.length)){ continue } if(u !== start && pt < (start||'').slice(0,pt.length)){ continue }
if(u !== end && (end || '\uffff') < pt){ continue } if(u !== end && (end || '\uffff') < pt){ continue }
if(rev){ // children must be checked first when going in reverse.
tmp = map(tree, cb, opt, p);
if(u !== tmp){ return tmp }
}
if(u !== (tmp = tree[''])){ if(u !== (tmp = tree[''])){
tmp = cb(tmp, pt, key, pre); tmp = cb(tmp, pt, key, pre);
if(u !== tmp){ return tmp } if(u !== tmp){ return tmp }
@ -75,8 +79,10 @@
if(u !== tmp){ return tmp } if(u !== tmp){ return tmp }
} }
pre = p; pre = p;
tmp = map(tree, cb, opt, pre); if(!rev){
if(u !== tmp){ return tmp } tmp = map(tree, cb, opt, pre);
if(u !== tmp){ return tmp }
}
pre.pop(); pre.pop();
} }
}; };

View File

@ -67,14 +67,12 @@ Gun.on('create', function(root){
o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1; o.limit = (tmp <= (o.pack || (1000 * 100)))? tmp : 1;
} }
if(has['-'] || (soul||{})['-']){ o.reverse = true } if(has['-'] || (soul||{})['-']){ o.reverse = true }
//console.log("RAD get:", key, o);
var start = (+new Date); // STATS! // console.log("GET!", id, JSON.stringify(key)); var start = (+new Date); // STATS! // console.log("GET!", id, JSON.stringify(key));
rad(key||'', function(err, data, o){ rad(key||'', function(err, data, o){
try{opt.store.stats.get.time[statg % 50] = (+new Date) - start; ++statg; try{opt.store.stats.get.time[statg % 50] = (+new Date) - start; ++statg;
opt.store.stats.get.count++; opt.store.stats.get.count++;
if(err){ opt.store.stats.get.err = err } if(err){ opt.store.stats.get.err = err }
}catch(e){} // STATS! }catch(e){} // STATS!
//console.log("RAD gat:", err, data, o);
if(data){ if(data){
if(typeof data !== 'string'){ if(typeof data !== 'string'){
if(o.atom){ if(o.atom){
@ -85,7 +83,6 @@ Gun.on('create', function(root){
} }
if(!graph && data){ each(data, '') } if(!graph && data){ each(data, '') }
} }
//console.log("GOT!", id, JSON.stringify(key), ((+new Date) - start));
root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix}); root.on('in', {'@': id, put: graph, err: err? err : u, rad: Radix});
}, o); }, o);
function each(val, has, a,b){ function each(val, has, a,b){

View File

@ -127,7 +127,6 @@ function batch(){ var as = this;
}, as); }, as);
if(as.res){ as.res() } if(as.res){ as.res() }
} function no(v,k){ if(v){ return true } } } function no(v,k){ if(v){ return true } }
//console.debug(999,1); var C = 0; setInterval(function(){ try{ debug.innerHTML = C }catch(e){console.log(e)} }, 500);
function map(v,k,n, at){ var as = this; function map(v,k,n, at){ var as = this;
var is = Gun.is(v); var is = Gun.is(v);
@ -157,7 +156,7 @@ function soul(id, as, msg, eve){
id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous? id = at.dub = at.dub || id || Gun.node.soul(cat.obj) || Gun.node.soul(msg.put || at.put) || Gun.val.link.is(msg.put || at.put) || (as.via.back('opt.uuid') || Gun.text.random)(); // TODO: BUG!? Do we really want the soul of the object given to us? Could that be dangerous?
if(eve){ eve.stun = true } if(eve){ eve.stun = true }
if(!id){ // polyfill async uuid for SEA if(!id){ // polyfill async uuid for SEA
at.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback as.via.back('opt.uuid')(function(err, id){ // TODO: improve perf without anonymous callback
if(err){ return Gun.log(err) } // TODO: Handle error. if(err){ return Gun.log(err) } // TODO: Handle error.
solve(at, at.dub = at.dub || id, cat, as); solve(at, at.dub = at.dub || id, cat, as);
}); });

View File

@ -153,6 +153,7 @@ Gun.dup = require('./dup');
Gun.on.get = function(msg, gun){ Gun.on.get = function(msg, gun){
var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp; var root = gun._, get = msg.get, soul = get[_soul], node = root.graph[soul], has = get[_has], tmp;
var next = root.next || (root.next = {}), at = next[soul]; var next = root.next || (root.next = {}), at = next[soul];
// queue concurrent GETs?
if(!node){ return root.on('get', msg) } if(!node){ return root.on('get', msg) }
if(has){ if(has){
if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) } if('string' != typeof has || !obj_has(node, has)){ return root.on('get', msg) }
@ -193,6 +194,7 @@ Gun.dup = require('./dup');
at.opt.peers = at.opt.peers || {}; at.opt.peers = at.opt.peers || {};
obj_map(opt, function each(v,k){ obj_map(opt, function each(v,k){
if(!obj_has(this, k) || text.is(v) || obj.empty(v)){ this[k] = v ; return } if(!obj_has(this, k) || text.is(v) || obj.empty(v)){ this[k] = v ; return }
if(v && v.constructor !== Object && !list_is(v)){ return }
obj_map(v, each, this[k]); obj_map(v, each, this[k]);
}, at.opt); }, at.opt);
Gun.on('opt', at); Gun.on('opt', at);

View File

@ -3692,7 +3692,6 @@ describe('Gun', function(){
expect(gone[index]).to.not.be.ok(); expect(gone[index]).to.not.be.ok();
gone[index] = diff; gone[index] = diff;
largest = (largest < diff)? diff : largest; largest = (largest < diff)? diff : largest;
//console.log(diff, '<', max);
expect(diff > max).to.not.be.ok(); expect(diff > max).to.not.be.ok();
}); });
var turns = 0; var turns = 0;

View File

@ -113,15 +113,22 @@ var names = ["Adalard","Adora","Aia","Albertina","Alfie","Allyn","Amabil","Ammam
it('radix reverse', function(done){ it('radix reverse', function(done){
var r = Radix(), tmp; var r = Radix(), tmp;
r('alice', 1);r('bob', 2);r('carl', 3);r('dave', 4); r('alice', 1);r('bob', 2);r('carl', 3);r('carlo',4);
r('dave', 5);r('zach',6);r('zachary',7);
var by = ['alice','bob','carl','carlo','dave','zach','zachary'];
Gun.obj.map(by, function(k,i){
r(k,i);
});
Radix.map(r, function(v,k, a,b){ Radix.map(r, function(v,k, a,b){
expect(by.pop()).to.be(k);
tmp = v; tmp = v;
}, {reverse: 1}); }, {reverse: 1});
expect(tmp).to.be(1); expect(tmp).to.be(1);
expect(by.length).to.be(0);
Radix.map(r, function(v,k, a,b){ Radix.map(r, function(v,k, a,b){
tmp = v; tmp = v;
}); });
expect(tmp).to.be(4); expect(tmp).to.be(7);
done(); done();
}); });
}); });