put sampling, mob, tests

This commit is contained in:
Mark Nadal 2022-06-06 18:08:43 -07:00
parent 525d834784
commit 46d926e831
9 changed files with 305 additions and 1928 deletions

37
gun.js
View File

@ -1086,9 +1086,10 @@
function ran(as){
if(as.err){ ran.end(as.stun, as.root); return } // move log handle here.
if(as.todo.length || as.end || !Object.empty(as.wait)){ return } as.end = 1;
//(as.retry = function(){ as.acks = 0;
var cat = (as.$.back(-1)._), root = cat.root, ask = cat.ask(function(ack){
root.on('ack', ack);
if(ack.err && as.ok){ Gun.log(ack) }
if(ack.err && !ack.lack){ Gun.log(ack) }
if(++acks > (as.acks || 0)){ this.off() } // Adjustable ACKs! Only 1 by default.
if(!as.ack){ return }
as.ack(ack, this);
@ -1099,7 +1100,9 @@
setTimeout.each(Object.keys(stun = stun.add||''), function(cb){ if(cb = stun[cb]){cb()} }); // resume the stunned reads // Any perf reasons to CPU schedule this .keys( ?
}).hatch = tmp; // this is not official yet ^
//console.log(1, "PUT", as.run, as.graph);
if(as.ack && !as.ok){ as.ok = as.acks || 9 } // TODO: In future! Remove this! This is just old API support.
(as.via._).on('out', {put: as.out = as.graph, ok: as.ok && {'@': as.ok+1}, opt: as.opt, '#': ask, _: tmp});
//})();
}; ran.end = function(stun,root){
stun.end = noop; // like with the earlier id, cheaper to make this flag a function so below callbacks do not have to do an extra type check.
if(stun.the.to === stun && stun === stun.the.last){ delete root.stun }
@ -1479,11 +1482,10 @@
if(!(hash = msg['##']) && u !== msg.put && !meta.via && ack){ mesh.hash(msg, peer); return } // TODO: Should broadcasts be hashed?
if(!peer && ack){ peer = ((tmp = dup.s[ack]) && (tmp.via || ((tmp = tmp.it) && (tmp = tmp._) && tmp.via))) || ((tmp = mesh.last) && ack === tmp['#'] && mesh.leap) } // warning! mesh.leap could be buggy! mesh last check reduces this.
if(!peer && ack){ // still no peer, then ack daisy chain 'tunnel' got lost.
if(dup.s[ack]){ return } // in dups but no peer hints that this was ack to self, ignore.
console.STAT && console.STAT(+new Date, ++SMIA, 'total no peer to ack to');
if(dup.s[ack]){ return } // in dups but no peer hints that this was ack to ourself, ignore.
console.STAT && console.STAT(+new Date, ++SMIA, 'total no peer to ack to'); // TODO: Delete this now. Dropping lost ACKs is protocol fine now.
return false;
} // TODO: Temporary? If ack via trace has been lost, acks will go to all peers, which trashes browser bandwidth. Not relaying the ack will force sender to ask for ack again. Note, this is technically wrong for mesh behavior.
if(msg.put && (tmp = msg.ok)){ msg.ok = {'@':(tmp['@']||1)-1, '/': (tmp['/']==msg._.near)? mesh.near : tmp['/']}; }
if(!peer && mesh.way){ return mesh.way(msg) }
DBG && (DBG.yh = +new Date);
if(!(raw = meta.raw)){ mesh.raw(msg, peer); return }
@ -1557,6 +1559,7 @@
}
if(i > 1){ msg['><'] = to.join() } // TODO: BUG! This gets set regardless of peers sent to! Detect?
}
if(msg.put && (tmp = msg.ok)){ msg.ok = {'@':(tmp['@']||1)-1, '/': (tmp['/']==msg._.near)? mesh.near : tmp['/']}; }
if(put = meta.$put){
tmp = {}; Object.keys(msg).forEach(function(k){ tmp[k] = msg[k] });
tmp.put = ':])([:';
@ -1603,7 +1606,7 @@
(peer.queue = peer.queue || []).push(raw);
}}
mesh.near = 0; // TODO: BUG! Mesh.near buggy, FIX!
mesh.near = 0;
mesh.hi = function(peer){
var wire = peer.wire, tmp;
if(!wire){ mesh.wire((peer.length && {url: peer, id: peer}) || peer); return }
@ -1614,8 +1617,11 @@
mesh.say({dam: '?', pid: root.opt.pid}, opt.peers[tmp] = peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
peer.met = peer.met || +(new Date);
if(!wire.hied){ root.on(wire.hied = 'hi', peer) }
if(!peer.met){
mesh.near++;
peer.met = +(new Date);
root.on('hi', peer)
}
// @rogowski I need this here by default for now to fix go1dfish's bug
tmp = peer.queue; peer.queue = [];
setTimeout.each(tmp||[],function(msg){
@ -1624,7 +1630,8 @@
//Type.obj.native && Type.obj.native(); // dirty place to check if other JS polluted.
}
mesh.bye = function(peer){
--mesh.near;
peer.met && --mesh.near;
delete peer.met;
root.on('bye', peer);
var tmp = +(new Date); tmp = (tmp - (peer.met||tmp));
mesh.bye.time = ((mesh.bye.time || tmp) + tmp) / 2;
@ -1638,6 +1645,13 @@
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
mesh.hear['mob'] = function(msg, peer){ // NOTE: AXE will overload this with better logic.
if(!msg.peers){ return }
var peers = Object.keys(msg.peers), one = peers[Math.floor(Math.random()*peers.length)];
if(!one){ return }
mesh.bye(peer);
mesh.hi(one);
}
root.on('create', function(root){
root.opt.pid = root.opt.pid || String.random(9);
@ -1776,10 +1790,11 @@
root.on('put', function(msg){
this.to.next(msg); // remember to call next middleware adapter
var put = msg.put, soul = put['#'], key = put['.'], id = msg['#'], tmp; // pull data off wire envelope
var put = msg.put, soul = put['#'], key = put['.'], id = msg['#'], ok = msg.ok||'', tmp; // pull data off wire envelope
disk[soul] = Gun.state.ify(disk[soul], key, put['>'], put[':'], soul); // merge into disk object
if(stop && size > (4999880)){ root.on('in', {'@': id, err: "localStorage max!"}); return; }
if(!msg['@']){ acks.push(id) } // then ack any non-ack write. // TODO: use batch id.
//if(!msg['@']){ acks.push(id) } // then ack any non-ack write. // TODO: use batch id.
if(!msg['@'] && (!msg._.via || Math.random() < (ok['@'] / ok['/']))){ acks.push(id) } // then ack any non-ack write. // TODO: use batch id.
if(to){ return }
to = setTimeout(flush, 9+(size / 333)); // 0.1MB = 0.3s, 5MB = 15s
});
@ -1795,7 +1810,7 @@
}
size = tmp.length;
if(!err && !Object.empty(opt.peers)){ return } // only ack if there are no peers. // Switch this to probabilistic mode
//if(!err && !Object.empty(opt.peers)){ return } // only ack if there are no peers. // Switch this to probabilistic mode
setTimeout.each(ack, function(id){
root.on('in', {'@': id, err: err, ok: 0}); // localStorage isn't reliable, so make its `ok` code be a low number.
},0,99);

View File

@ -4,9 +4,7 @@
// but for now... I gotta rush this out!
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun'), u;
Gun.on('opt', function(at){ start(at); this.to.next(at) }); // make sure to call the "next" middleware adapter.
// For Future WebRTC notes: Chrome 500 max limit, however 256 likely - FF "none", webtorrent does 55 per torrent (thanks Feross! How'd you come to this number?).
// TODO: BUG: panic test/panic/1 & test/panic/3 fail when AXE is on.
function start(root){
if(root.axe){ return }
var opt = root.opt, peers = opt.peers;
@ -85,8 +83,8 @@ function start(root){
var ref = root.$.get(soul)._, route = (ref||'').route;
//'test' === soul && console.log(Object.port, ''+msg['#'], has, val, route && route.keys());
if(!route){ return }
q = Q[tmp = soul+has] || (Q[tmp] = {});
if(!Q[soul+has]){ setTimeout(function(){ id = Q[soul+has]; delete Q[soul+has]; // TODO: add debounce here!? hmm, scope would need sub. // Q is a quick hack!
if(ref.skip){ ref.skip.now = msg['#']; return }
(ref.skip = {now: msg['#']}).to = setTimeout(function(){
setTimeout.each(Object.maps(route), function(pid){ var peer, tmp;
if(!(peer = route.get(pid))){ return }
if(!peer.wire){ route.delete(pid); return } // bye!
@ -101,16 +99,15 @@ function start(root){
}
put[soul] = state_ify(put[soul], has, state, val, soul);
tmp = dup.track(peer.next = peer.next || String.random(9));
(tmp.back || (tmp.back = {}))[''+id] = 1;
(tmp.back || (tmp.back = {}))[''+ref.skip.now] = 1;
if(peer.to){ return }
peer.to = setTimeout(function(){ flush(peer) }, opt.gap);
}) }, 9) }
Q[soul+has] = msg['#'];
}) }, 9);
});
});
function flush(peer){
var msg = {'#': peer.next, put: peer.put, ok: {'@': 4, '/': mesh.near}}; // BUG: TODO: sub count!
var msg = {'#': peer.next, put: peer.put, ok: {'@': 3, '/': mesh.near}}; // BUG: TODO: sub count!
// TODO: what about DAM's >< dedup? Current thinking is, don't use it, however, you could store first msg# & latest msg#, and if here... latest === first then likely it is the same >< thing, so if(firstMsg['><'][peer.id]){ return } don't send.
peer.next = peer.put = peer.to = null;
mesh.say(msg, peer);
@ -141,7 +138,7 @@ function start(root){
}());
;(function(){ // THIS IS THE MOB MODULE;
return; // WORK IN PROGRESS, TEST FINALIZED, NEED TO MAKE STABLE.
//return; // WORK IN PROGRESS, TEST FINALIZED, NEED TO MAKE STABLE.
/*
AXE should have a couple of threshold items...
let's pretend there is a variable max peers connected
@ -160,7 +157,8 @@ function start(root){
root.on('hi', function(peer){
this.to.next(peer);
if(peer.url){ return } // I am assuming that if we are wanting to make an outbound connection to them, that we don't ever want to drop them unless our actual config settings change.
var count = Object.keys(opt.peers).length || mesh.near; // TODO: BUG! This is slow, use .near, but near is buggy right now, fix in DAM.
var count = /*Object.keys(opt.peers).length ||*/ mesh.near; // TODO: BUG! This is slow, use .near, but near is buggy right now, fix in DAM.
//console.log("are we mobbed?", opt.mob, Object.keys(opt.peers).length, mesh.near);
if(opt.mob >= count){ return } // TODO: Make dynamic based on RAM/CPU also. Or possibly even weird stuff like opt.mob / axe.up length?
var peers = {};Object.keys(axe.up).forEach(function(p){ p = axe.up[p]; p.url && (peers[p.url]={}) });
// TODO: BUG!!! Infinite reconnection loop happens if not enough relays, or if some are missing. For instance, :8766 says to connect to :8767 which then says to connect to :8766. To not DDoS when system overload, figure clever way to tell peers to retry later, that network does not have enough capacity?

View File

@ -1,6 +1,5 @@
;(function(){
var Gun = (typeof window !== "undefined")? window.Gun : require('../gun');
Gun.on('opt', function(root){
this.to.next(root);
var opt = root.opt;
@ -33,7 +32,7 @@
// The above change corrects at least firefox RTC Peer handler where it **throws** on over 6 ice servers, and updates url: to urls: removing deprecation warning
opt.rtc.dataChannel = opt.rtc.dataChannel || {ordered: false, maxRetransmits: 2};
opt.rtc.sdp = opt.rtc.sdp || {mandatory: {OfferToReceiveAudio: false, OfferToReceiveVideo: false}};
opt.rtc.max = opt.rtc.max || 55; // is this a magic number?
opt.rtc.max = opt.rtc.max || 55; // is this a magic number? // For Future WebRTC notes: Chrome 500 max limit, however 256 likely - FF "none", webtorrent does 55 per torrent.
opt.rtc.room = opt.rtc.room || Gun.window && (location.hash.slice(1) || location.pathname.slice(1));
opt.announce = function(to){
opt.rtc.start = +new Date; // handle room logic:
@ -57,7 +56,7 @@
if(!msg.ok){ return }
var rtc = msg.ok.rtc, peer, tmp;
if(!rtc || !rtc.id || rtc.id === opt.pid){ return }
//console.log("webrtc:", rtc);
//console.log("webrtc:", JSON.stringify(msg));
if(tmp = rtc.answer){
if(!(peer = opt.peers[rtc.id] || open[rtc.id]) || peer.remoteSet){ return }
tmp.sdp = tmp.sdp.replace(/\\r\\n/g, '\r\n');
@ -78,12 +77,10 @@
wire.onopen = function(e){
delete open[rtc.id];
mesh.hi(peer);
//clearTimeout(wire.to);
//delete open[rtc.id];
}
wire.onmessage = function(msg){
if(!msg){ return }
console.log(opt.pid, "HEARD FROM WEBRTC:");
//console.log('via rtc');
mesh.hear(msg.data || msg, peer);
};
peer.onicecandidate = function(e){ // source: EasyRTC!
@ -112,5 +109,4 @@
return peer;
}
});
var noop = function(){};
}());

1884
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -63,13 +63,13 @@
"ws": "^7.2.1"
},
"optionalDependencies": {
"@peculiar/webcrypto": "^1.1.1",
"emailjs": "^2.2.0"
"@peculiar/webcrypto": "^1.1.1"
},
"devDependencies": {
"aws-sdk": "^2.528.0",
"ip": "^1.1.5",
"mocha": "^6.2.0",
"ip": "^1.1.5",
"aws-sdk": "^2.528.0",
"emailjs": "^2.2.0",
"uglify-js": "^3.6.0"
}
}

View File

@ -3,8 +3,8 @@ This is the first in a series of basic networking correctness tests.
Each test itself might be dumb and simple, but built up together,
they prove desired end goals for behavior at scale.
1. (this file) Makes sure that a browser receives daisy chain acks that data was saved.
2. (this file) Makes sure the browser receives a deduplicated ACK when data is requested across the daisy chains.
1. (this file) When Alice saves data, acks should daisy chain back to her.
2. (this file) When Dave asks for data, Bob & Alice do not need to reply because it would be the same as Carl's reply.
Assume we have a 4 peer federated-like topology,
@ -86,7 +86,7 @@ describe("Put ACK", function(){
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server}); // Note: test with AXE on & off.
var gun = Gun({file: env.i+'data', peers: peers, web: server, axe: false}); // Note: test with AXE on & off.
server.listen(port, function(){
test.done();
});
@ -107,8 +107,10 @@ describe("Put ACK", function(){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
window.ref = gun.get('a');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
@ -122,34 +124,34 @@ describe("Put ACK", function(){
var c = test.props.acks, acks = {}, tmp;
c = c < 2? 2 : c; // at least 2 acks.
ref.put({hello: 'world'}, function(ack){
//console.log("ack:", ack['#']);
acks[ack['#']] = 1; // uniquely list all the ack IDs.
tmp = Object.keys(acks).length;
console.log(tmp, "save");
if(tmp >= c){ // when there are enough
test.done(); // confirm test passes
wire(); // start sniffing for future tests
setTimeout(function(){
test.done(); // confirm test passes
wire(); // start sniffing for future tests
}, 1000 * 2);
return;
}
}, {acks: c});
}, {acks: c, ok: c*10}); // TODO: Upon breaking change (> 2020) update this test with the deprecated `acks` behavior.
function wire(){ // for the future tests, track how many wire messages are heard/sent.
ref.hear = ref.hear || [];
var dam = ref.back('opt.mesh');
var hear = dam.hear;
dam.hear = function(raw, peer){ // hijack the listener
var msg; try{msg = JSON.parse(raw);
}catch(e){ console.log("Note: This test not support RAD serialization format yet, use JSON.") }
dam.hear = function(raw, peer){
hear(raw, peer);
ref.hear.push(msg || raw); // add to count
}
var say = dam.say;
dam.say = function(raw, peer){
var yes = say(raw, peer);
if(yes === false){ return }
console.log(msg);
(ref.say || (ref.say = [])).push(JSON.parse(msg)); // add to count.
ref.hear.push(raw);
}
var peers = ref.back('opt.peers');
Object.keys(peers).forEach(function(peer){
peer = peers[peer];
peer.say = function(raw){
(ref.say || (ref.say = [])).push(raw); // add to count.
peer.wire.send(raw);
}
});
}
}, {acks: config.relays});
});
@ -176,7 +178,7 @@ describe("Put ACK", function(){
var hear = dam.hear;
dam.hear = function(raw, peer){ // hijack listener
var msg = JSON.parse(raw);
console.log('hear:', msg);
//console.log('hear:', msg);
hear(raw, peer);
ref.hear.push(msg);

225
test/panic/4putackdedup.js Normal file
View File

@ -0,0 +1,225 @@
/*
Assume we have 5000 peers in a star topology,
..______r1______..
./.../..|...\...\.
b1..b2..b3..b4..b5
They are all subscribed to the same data and Alice makes a 1 byte change to it. Even if a save acknowledgement is 1 byte, that would be 5K times the change. This does not scale. So instead, Alice states a sample size of 3/X and then the peers saving data use that ratio to decide whether they ack.
Each time the message gets relayed we modify X to be how many peers this hop sent it to. We also should decrease the sample size by 1.
This test checks that we get fewer acks than (half) the peers connected.
This also tests that custom ACKs get sent back thru AXE to one of the sending peers.
*/
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: ip || 'localhost',
port: 8765,
relays: 1,
browsers: 9,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic; try{ panic = require('panic-server') } catch(e){ console.log("PANIC not installed! `npm install panic-server panic-manager panic-client`") }
panic.server().on('request', function(req, res){
config.route[req.url] && require('fs').createReadStream(config.route[req.url]).pipe(res);
}).listen(config.port);
var clients = panic.clients;
var manager = require('panic-manager')();
manager.start({
clients: Array(config.relays).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var relays = clients.filter('Node.js');
var r1 = relays.pluck(1);
var browsers = clients.excluding(relays);
var b1 = browsers.pluck(1);
var others = browsers.excluding(b1);
var b2 = relays.excluding(b1).pluck(1);
var b3 = relays.excluding([b1,b2]).pluck(1);
var b4 = relays.excluding([b1,b2]).pluck(1);
var b5 = relays.excluding([b1,b2]).pluck(1);
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("Dedup load balancing GETs", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Relays have joined!", function(){
return relays.atLeast(config.relays);
});
it("GUN started!", function(){
var tests = [], i = 0;
relays.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
try{ require('fs').unlinkSync(env.i+'data') }catch(e){}
try{ require('gun/lib/fsrm')(env.i+'data') }catch(e){}
var server = require('http').createServer(function(req, res){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun; try{ Gun = require('gun') }catch(e){ console.log("GUN not found! You need to link GUN to PANIC. Nesting the `gun` repo inside a `node_modules` parent folder often fixes this.") }
var peers = [], i = env.config.relays;
while(i--){
var tmp = (env.config.port + (i + 1));
if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
}
var gun = Gun({peers: peers, web: server, rad: false, radisk: false, file: false, localStorage: false, axe: false});
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it(config.browsers +" browser(s) have joined!", function(){
require('./util/open').web(config.browsers, "http://"+ config.IP +":"+ config.port);
return browsers.atLeast(config.browsers);
});
// end PANIC template -->
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
browsers.each(function(browser, id){
tests.push(browser.run(function(test){
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
// start with the first peer:
var env = test.props;
var gun = Gun({peers: 'http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun', localStorage: false});
window.gun = gun;
window.ref = gun.get('test');
ref.on(function(data){ });
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("ACK", function(){
var tests = [], i = 0;
others.each(function(browser, id){
tests.push(browser.run(function(test){
var id = test.props.i;
// these lines are for debugging...
/*var dam = ref.back('opt.mesh');
var say = dam.say;
dam.say = function(raw, peer){
say(raw, peer);
//console.log("said:", JSON.stringify(raw));
}
var hear = dam.hear;
dam.hear = function(raw, peer){
//console.log("heard:", raw);
hear(raw, peer);
}*/
gun.on('put', function(msg){
var ok = msg.ok;
if(ok['@'] > 2){
console.log("Relay did not decrement!")
test.fail("Relay did not decrement!");
_relay_did_not_decrement;
return;
}
if(Math.random() > (ok['@'] / ok['/'])){ return }
console.log('ack?', JSON.stringify(msg));
//console.log("WAS THE SPECIAL ONE TO ACK!", JSON.stringify(msg));
gun.on('out', {'@': msg['#'], ok: {yay: 1}});
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("wait...", function(done){
setTimeout(function(){
done();
},2000);
});
it("Alice saves data", function(){
return b1.run(function(test){
test.async();
// these lines are for debugging...
/*var dam = ref.back('opt.mesh');
var say = dam.say;
dam.say = function(raw, peer){
say(raw, peer);
//console.log("said:", JSON.stringify(raw), dam.near, Object.keys(gun._.opt.peers).join(','));
}
var hear = dam.hear;
dam.hear = function(raw, peer){
hear(raw, peer);
//console.log("heard:", raw);
}*/
var many = test.props.config.browsers;
console.log("Alice is saving...");
test.c = 0;
ref.put({hello: 'world'}, function(ack){
if(!ack.ok.yay){
test.fail("ERROR: No custom ack!");
console.log("ERROR: No custom ack!");
return no_custom_ack;
}
//console.log("I saved data, this is the ACK", JSON.stringify(ack));
test.c++;
clearTimeout(test.to);
test.to = setTimeout(function(){
if(test.c >= (many/2)){
test.fail("ERROR: Too many acks!");
console.log("ERROR: Too many acks!");
return too_many_acks;
}
test.done();
}, 999);
}, {ok: 3, acks: 9999});
}, {config: config});
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
console.log("REMINDER: RUN THIS TEST WITH AXE ON & OFF!");
require('./util/open').cleanup();
return relays.run(function(){
process.exit();
});
});
});

View File

@ -131,17 +131,18 @@ describe("Mob test.", function(){
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
// NOTE: This "mob" logic will be moved into axe.js (or maybe gun.js itself), but while we're building the logic it is easier to quickly hack/iterate by prototyping here in the test itself until it passes. But it needs to be refactored into the actual code or else you might have false positives of this test overloading "mob" logic.
// ^^^^^^^^^ THIS HAS BEEN MOVED TO GUN CORE, HOWEVER,
// ^^^^^^^^^ EXPERIMENT WITH MORE ADVANCED LOGIC THAT AXE OVERLOADS CORE.
var mesh = gun.back('opt.mesh'); // overload...
mesh.hear['mob'] = function(msg, peer){
/*mesh.hear['mob'] = function(msg, peer){
// TODO: NOTE, code AXE DHT to aggressively drop new peers AFTER superpeer sends this rebalance/disconnect message that contains some other superpeers.
clearTimeout(gun.TO); gun.TO = setTimeout(end, 2000);
console.log("getting mobbed", msg);
if(!msg.peers){ return }
var peers = Object.keys(msg.peers), one = peers[Math.floor(Math.random()*peers.length)];
console.log('Browser', env.i, 'chooses', one, 'from', JSON.stringify(peers), 'that', peer.url, 'suggested, because it is mobbed.');//, 'from', msg.peers+'');
mesh.bye(peer); // Idea: Should keep track of failed ones to reduce repeats. For another feature/module that deserves its own separate test.
mesh.hi(one);
}
}*/
//console.log('Browser', env.i, "started with:", Object.keys(gun._.opt.peers)+'');
window.gun = gun;