@rococtz fix + @dreader AXE tests

This commit is contained in:
Mark Nadal 2022-04-26 23:12:52 -07:00
parent 4f19440262
commit ebe6f0cf3b
12 changed files with 444 additions and 254 deletions

21
gun.js
View File

@ -63,8 +63,9 @@
}
;(function(){ // max ~1ms or before stack overflow
var u, sT = setTimeout, l = 0, c = 0, sI = (typeof setImmediate !== ''+u && setImmediate) || sT; // queueMicrotask faster but blocks UI
sT.hold = sT.hold || 9;
sT.poll = sT.poll || function(f){ //f(); return; // for testing
if((1 >= (+new Date - l)) && c++ < 3333){ f(); return }
if((sT.hold >= (+new Date - l)) && c++ < 3333){ f(); return }
sI(function(){ l = +new Date; f() },c=0)
}
}());
@ -490,19 +491,20 @@
if(!Object.plain(opt)){ opt = {} }
if(!Object.plain(at.opt)){ at.opt = opt }
if('string' == typeof tmp){ tmp = [tmp] }
if(!Object.plain(at.opt.peers)){ at.opt.peers = {}}
if(tmp instanceof Array){
if(!Object.plain(at.opt.peers)){ at.opt.peers = {}}
opt.peers = {};
tmp.forEach(function(url){
var p = {}; p.id = p.url = url;
at.opt.peers[url] = at.opt.peers[url] || p;
opt.peers[url] = at.opt.peers[url] = at.opt.peers[url] || p;
})
}
at.opt.peers = at.opt.peers || {};
obj_each(opt, function each(k){ var v = this[k];
if((this && this.hasOwnProperty(k)) || 'string' == typeof v || Object.empty(v)){ this[k] = v; return }
if(v && v.constructor !== Object && !(v instanceof Array)){ return }
obj_each(v, each);
});
at.opt.from = opt;
Gun.on('opt', at);
at.opt.uuid = at.opt.uuid || function uuid(l){ return Gun.state().toString(36).replace('.','') + String.random(l||12) }
return gun;
@ -1580,7 +1582,6 @@
}
// for now - find better place later.
function send(raw, peer){ try{
//console.log('SAY:', peer.id, (raw||'').slice(0,250), ((raw||'').length / 1024 / 1024).toFixed(4));
var wire = peer.wire;
if(peer.say){
peer.say(raw);
@ -1594,7 +1595,8 @@
}}
mesh.hi = function(peer){
var tmp = peer.wire || {};
var wire = peer.wire, tmp;
if(!wire){ mesh.wire((peer.length && {url: peer}) || peer); return }
if(peer.id){
opt.peers[peer.url || peer.id] = peer;
} else {
@ -1603,7 +1605,7 @@
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
}
peer.met = peer.met || +(new Date);
if(!tmp.hied){ root.on(tmp.hied = 'hi', peer) }
if(!wire.hied){ root.on(wire.hied = '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){
@ -1621,7 +1623,6 @@
if(msg.pid){
if(!peer.pid){ peer.pid = msg.pid }
if(msg['@']){ return }
if(msg.pid === opt.pid){ mesh.bye(peer) }
}
mesh.say({dam: '?', pid: opt.pid, '@': msg['#']}, peer);
delete dup.s[peer.last]; // IMPORTANT: see https://gun.eco/docs/DAM#self
@ -1744,9 +1745,9 @@
var opt = root.opt, graph = root.graph, acks = [], disk, to, size, stop;
if(false === opt.localStorage){ return }
opt.prefix = opt.file || 'gun/';
try{ disk = lg[opt.prefix] = lg[opt.prefix] || JSON.parse(to = store.getItem(opt.prefix)) || {}; // TODO: Perf! This will block, should we care, since limited to 5MB anyways?
try{ disk = lg[opt.prefix] = lg[opt.prefix] || JSON.parse(size = store.getItem(opt.prefix)) || {}; // TODO: Perf! This will block, should we care, since limited to 5MB anyways?
}catch(e){ disk = lg[opt.prefix] = {}; }
size = (to||'').length;
size = (size||'').length;
root.on('get', function(msg){
this.to.next(msg);

View File

@ -18,7 +18,7 @@ function start(root){
mesh.way = function(msg){
if(!msg){ return }
if(msg.get){ return GET(msg) }
if(msg.put){ return } // relaying handled by HAM aggregation, no message forwarding!
if(msg.put){ return }
fall(msg);
}
@ -37,7 +37,7 @@ function start(root){
}
GET.turn = function(msg, route, turn){
var tmp = msg['#'], tag = dup.s[tmp], next;
if(!tmp){ return }
if(!tmp || !tag){ return } // message timed out, GUN may require us to relay, tho AXE does not like that. Rethink?
// Ideas: Save a random seed that sorts the route, store it and the index. // Or indexing on lowest latency is probably better.
clearTimeout(tag.lack);
if(tag.ack && (tmp = tag['##']) && msg['##'] === tmp){ return } // hashes match, stop asking other peers!
@ -74,6 +74,7 @@ function start(root){
if(msg['@']){ return } // acks send existing data, not updates, so no need to resend to others.
if(!soul || !has){ return }
var ref = root.$.get(soul)._, route = (ref||'').route;
//'test' === soul && console.log(Object.port, ''+msg['#'], has, val, route && route.keys());
if(!route){ return }
if(Q[soul+has]){ return; } (Q[soul+has] = setTimeout(function(){ delete Q[soul+has]; // TODO: add debounce here!? hmm, scope would need sub. // Q is a quick hack!
setTimeout.each(Object.maps(route), function(id){ var peer, tmp;
@ -103,17 +104,26 @@ function start(root){
var state_ify = Gun.state.ify, state_is = Gun.state.is;
;(function(){ // THIS IS THE UP MODULE;
return; // unfinished.
axe.up = {};
root.on('hi', function(peer){
this.to.next(peer);
var hi = mesh.hear['?']; // lower-level integration with DAM! This is abnormal but helps performance.
mesh.hear['?'] = function(msg, peer){ var p; // deduplicate unnecessary connections:
hi(msg, peer);
if(!peer.pid){ return }
if(peer.pid === opt.pid){ mesh.bye(peer); return } // if I connected to myself, drop.
if(p = axe.up[peer.pid]){ // if we both connected to each other...
if(p === peer){ return } // do nothing if no conflict,
if(opt.pid > peer.pid){ // else deterministically sort
p = peer; // so we will wind up choosing the same to keep
peer = axe.up[p.pid]; // and the same to drop.
}
p.url = p.url || peer.url; // copy if not
mesh.bye(peer); // drop
axe.up[p.pid] = p; // update same to be same.
return;
}
if(!peer.url){ return }
axe.up[peer.id] = peer;
});
root.on('bye', function(peer){ this.to.next(peer);
// this needs to be modified. TODO!
return;
});
axe.up[peer.pid] = peer;
};
}());
;(function(){ // THIS IS THE MOB MODULE;
@ -130,7 +140,7 @@ function start(root){
The mob threshold might be determined by other factors,
like how much RAM or CPU stress we have.
*/
opt.mob = opt.mob || 8000; // default to 80% of lot of free cloud deploy's socket limit which is 10K.
opt.mob = opt.mob || 9900; // should be based on ulimit, some clouds as low as 10K.
// handle rebalancing a mob of peers:
root.on('hi', function(peer){
@ -138,7 +148,8 @@ function start(root){
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;
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);
var peers = [];Object.keys(axe.up).forEach(function(p){ p = axe.up[p]; p.url && peers.push(p.url) });
//console.log(Object.port, 'mobbed?', peer.pid, opt.mob, count, Object.keys(opt.peers)+'', 'bye', peer.pid || peer.id);
if(!peers.length){ return }
mesh.say({dam: 'mob', mob: count, peers: peers}, peer);
//setTimeout(function(){ mesh.bye(peer) }, 9); // something with better perf? // UNCOMMENT WHEN WE ACTIVATE THIS FEATURE

View File

@ -1,6 +1,6 @@
{
"name": "gun",
"version": "0.2020.1236",
"version": "0.2020.1237",
"description": "A realtime, decentralized, offline-first, graph data synchronization engine.",
"types": "index.d.ts",
"main": "index.js",

View File

@ -17,8 +17,9 @@ Dave asking for the data should not flood him with more than necessary responses
*/
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: require('ip').address(),
IP: ip || 'localhost',
port: 8765,
servers: 2,
browsers: 2,
@ -29,7 +30,8 @@ var config = {
}
}
var panic = require('panic-server');
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);
@ -75,7 +77,7 @@ describe("Put ACK", function(){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
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.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
@ -111,7 +113,6 @@ describe("Put ACK", function(){
});
return Promise.all(tests);
});
// end PANIC template -->
it("Put", function(){

View File

@ -7,8 +7,9 @@ This test is almost the opposite of the first test.
*/
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: require('ip').address(),
IP: ip || 'localhost',
port: 8765,
servers: 1,
browsers: 4,
@ -19,7 +20,8 @@ var config = {
}
}
var panic = require('panic-server');
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);
@ -67,7 +69,7 @@ describe("GET GET", function(){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
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.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
@ -113,7 +115,6 @@ describe("GET GET", function(){
});
return Promise.all(tests);
});
// end PANIC template -->
it("connect", function(){

View File

@ -1,13 +1,13 @@
/*
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) Is a browser write is confirmed as save by multiple peers even if by daisy chain.
2.
Make sure that our writes are all saved by the other browser.
// Note: This test has many manual adjustments: Many features disabled, etc. consider some variations.
// Bug: Failing due to lS ack disabled if peered, need to fix with advanced probabilistic mode.
*/
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: require('ip').address(),
IP: ip || 'localhost',
port: 8765,
servers: 1,
browsers: 2,
@ -19,7 +19,8 @@ var config = {
}
}
var panic = require('panic-server');
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);
@ -43,6 +44,7 @@ var browsers = clients.excluding(servers);
var alice = browsers.pluck(1);
var carl = browsers.excluding(alice).pluck(1);
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
@ -58,12 +60,12 @@ describe("Put ACK", function(){
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){}
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 = require('gun');
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.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
@ -72,7 +74,7 @@ describe("Put ACK", function(){
}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server});
var gun = Gun({file: false, rad: false, localStorage: false, file: env.i+'data', peers: peers, web: server, axe: false});
server.listen(port, function(){
test.done();
});
@ -85,6 +87,13 @@ describe("Put ACK", function(){
require('./util/open').web(config.browsers, "http://"+ config.IP +":"+ config.port);
return browsers.atLeast(config.browsers);
});
// end PANIC template -->
it("Alice", function(){
return alice.run(function(test){
window.ALICE = 1;
}, {puts: config.puts});
});
it("Browsers initialized gun!", function(){
var tests = [], i = 0;
@ -93,8 +102,8 @@ 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('test');
var gun = Gun({peers: 'http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun', localStorage: window.ALICE? false : true});
window.ref = gun.get('test').on(function(){ });
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
@ -108,6 +117,7 @@ describe("Put ACK", function(){
while(i--){ go(i) }
function go(i){
ref.get(i).put({hello: 'world'}, function(ack){
//console.log("acked:", JSON.stringify(ack.ok) || ('err:'+ack.err));
if(ack.err){ put_failed }
if(++d !== test.props.puts){ return }
console.log("all success", d);

View File

@ -1,187 +0,0 @@
/*
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.
Alice: [Bob]
Bob: [Carl]
Carl: [Bob]
Dave: [Carl]
Ed: [?]
100,000 browsers
1 relay peer
50,000 browsers on Bob
50,000 browsers on Carl
//var gun = Gun(['https://gunjs.herokuapp.com/gun', 'https://guntest.herokuapp.com/gun']);
pretend we have 3TB of data.
300K browsers.
suppose we have 3 nodejs peers that are shards
var superpeer1 = Gun(AXE({shard: 'a~m'}));
var superpeer2 = Gun(AXE({shard: 'n~r'}));
var superpeer3 = Gun(AXE({shard: 's~z'}));
300K browsers join a popular app and they have to do this
via the browser, so they all go to superpeer1.com
then 2/3 of them should get sharded to superpeer2 & superpeer3
first all connect to:
..s1-----s2--s3
./\.\.
b1.b2.b3
then be load balanced to:
..s1--s2--s3
./....|....\.
b1....b2....b3
*/
var config = {
IP: require('ip').address(),
port: 8765,
servers: 3,
browsers: 3,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
var panic = require('panic-server');
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.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var s1 = servers.pluck(1);
var s2 = servers.excluding(s1).pluck(1);
var s3 = servers.excluding([s1,s2]).pluck(1);
var browsers = clients.excluding(servers);
var b1 = browsers.pluck(1);
var b2 = servers.excluding(b1).pluck(1);
var b3 = servers.excluding([b1,b2]).pluck(1);
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.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 = require('gun');
var peers = [], i = env.config.servers;
while(i--){
var tmp = (env.config.port + (i + 1));
//if(port != tmp){ // ignore ourselves
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
//}
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server, mob: 4});
global.gun = gun;
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);
});
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){}
var env = test.props;
var gun = Gun('http://'+ env.config.IP + ':' + (env.config.port + 1) + '/gun');
var mesh = gun.back('opt.mesh');
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.
if(!msg.peers){ return }
var one = msg.peers[Math.floor(Math.random()*msg.peers.length)];
console.log("CONNECT TO THIS PEER:", one);
gun.opt(one);
mesh.bye(peer);
if(test.c){ return }
test.c = 1;
test.done();
}
console.log("connected to who superpeer(s)?", gun._.opt.peers);
window.gun = gun;
window.ref = gun.get('test');
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("Got Load Balanced to Different Peer", function(){
var tests = [], i = 0;
browsers.each(function(browser, id){
tests.push(browser.run(function(test){
console.log("...", gun._.opt.peers);
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
//},1000);
},1000 * 60);
});
after("Everything shut down.", function(){
require('./util/open').cleanup();
return servers.run(function(){
process.exit();
});
});
});

View File

@ -1,14 +1,17 @@
/*
It is possible to connect to yourself, which just wastes bandwidth, so this test checks that we disconnect from ourselves.
*/
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: require('ip').address(),
IP: ip || 'localhost',
port: 8765,
servers: 1
}
var panic = require('panic-server');
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);
@ -29,7 +32,7 @@ manager.start({
var servers = clients.filter('Node.js');
var alice = servers.pluck(1);
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("Do not connect to self", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
@ -50,10 +53,10 @@ describe("Do not connect to self", function(){
res.end("I am "+ env.i +"!");
});
var port = env.config.port + env.i;
var Gun = require('gun');
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.servers;
global.self_url = 'http://'+ env.config.IP + ':' + port + '/gun';
// make sure to connect to self/same.
peers.push(self_url);
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server, multicast: false});
@ -65,6 +68,7 @@ describe("Do not connect to self", function(){
});
return Promise.all(tests);
});
// end PANIC template -->
it("Drop self", function(){
var tests = [], i = 0;

121
test/panic/axe/2no_dup.js Normal file
View File

@ -0,0 +1,121 @@
/*
If Alice calls Bob and Bob calls Alice, 2 connections will be formed, which wastes bandwidth. This test checks for that and deduplicates the connections.
*/
// <-- 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,
servers: 3,
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.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.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.servers;
while(i--){ // make sure to connect to self/same.
var tmp = (env.config.port + (i + 1));
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
global.peerID = String.fromCharCode(64 + env.i);
console.log(env.i, port, " connect to ", peers);
var gun = Gun({file: env.i+'data', pid: peerID, peers: peers, web: server});
global.gun = gun;
server.listen(port, function(){
test.done();
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
// end PANIC template -->
it("Drop duplicates", function(){
var tests = [], i = 0;
servers.each(function(client){
tests.push(client.run(function(test){
var env = test.props;
test.async();
var peers = gun.back('opt.peers');
gun.get('test').on(function(a){ }); // connections are lazy, so trigger a read. A feature, tho also a bug in this case, should probably have its own tests to determine if this ought be intended or not.
setTimeout(function(){
var p = [], o = {}, err; Object.keys(peers).forEach(function(id){
id = peers[id];
p.push(id.pid);
err = err || (o[id.pid] = (o[id.pid] || 0) + 1) - 1;
});
console.log(peerID, 'connected to:', p);
if(p.length > 2 || err){
console.log("FAIL: too_many_connections");
too_many_connections;
return;
}
test.done();
},2000);
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1);
});
after("Everything shut down.", function(){
require('../util/open').cleanup();
return servers.run(function(){
process.exit();
});
});
});

223
test/panic/axe/3mob.js Normal file
View File

@ -0,0 +1,223 @@
/*
If WebRTC fails browsers will fallback to using a relay.
A relay may be running on a Raspberry Pi or some low-end cloud VM.
There is a limit to how many peers it can help before it crashes.
It can only handle a "mob" of a certain size.
So what happens then?
It would be useful if it could tell newly connecting peers to connect to someone else.
This will rebalance the load on the network.
Imagine there are 3 relays that can handle 100K peers,
But then 300K peers connect to the 1st one.
We want to then see the peers move to the other relays, such that the 3 have 100K peers each.
(1) To simulate this, we will have 3 peers connect starting like this:
..s1-----s2--s3
./\.\..........
b1.b2.b3.......
(2) By the end of the test, the "mob" logic should rebalance the load to look like this:
..s1--s2--s3..
./....|.....\.
b1....b2....b3
If it does, the test passes.
(Note: At the end of this test, it uses GUN to sync data about what peers are connected to whom. While this is useful in that it also verifies that sync between b1 <-> b3 works regardless of whether direct or indirect connections, as such it could result in errors: If GUN has a bug, the AXE test may fail even if it is not the fault of AXE, and likewise - the usage of GUN in this test is contrived, it passing has 0 correlation that GUN is correctly handling the sync logic. In fact, assume it is not, make sure you use another test to verify that.)
*/
// <-- 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,
servers: 3,
browsers: 3,
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.servers).fill().map(function(u, i){
return {
type: 'node',
port: config.port + (i + 1)
}
}),
panic: 'http://' + config.IP + ':' + config.port
});
var servers = clients.filter('Node.js');
var s1 = servers.pluck(1);
var s2 = servers.excluding(s1).pluck(1);
var s3 = servers.excluding([s1,s2]).pluck(1);
var browsers = clients.excluding(servers);
var b1 = browsers.pluck(1);
var b2 = servers.excluding(b1).pluck(1);
var b3 = servers.excluding([b1,b2]).pluck(1);
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("Put ACK", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
it("Servers have joined!", function(){
return servers.atLeast(config.servers);
});
it("GUN started!", function(){
var tests = [], i = 0;
servers.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.servers;
while(i--){ // make sure to connect to self/same.
var tmp = (env.config.port + (i + 1));
peers.push('http://'+ env.config.IP + ':' + tmp + '/gun');
}
console.log(port, " connect to ", peers);
var gun = Gun({file: env.i+'data', peers: peers, web: server, mob: 3, multicast: false});
global.gun = gun;
Object.port = port;
server.listen(port, function(){
console.log(port, 'DONE');
test.done();
});
gun.get('a').on(function(){ }); // TODO: Wrong! This is an example of the test using GUN in weird ways to work around bugs at the time of writing. This line should not be necessary, AXE should still pass even if this line is commented out, however if it fails then that is a bug in GUN's logic, not AXE.
}, {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){
test.async();
try{ localStorage.clear() }catch(e){}
try{ indexedDB.deleteDatabase('radata') }catch(e){}
Object.tid = test.props.i;
// start with the first peer:
var env = test.props;
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.
var mesh = gun.back('opt.mesh'); // overload...
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.
gun.CHANGED = 1;
clearTimeout(gun.TO); gun.TO = setTimeout(end, 2000);
console.log(1111, 'Browser', env.i, 'choose', one, 'of', JSON.stringify(msg.peers), 'got from', peer.url);//, 'from', msg.peers+'');
if(!msg.peers){ return }
var one = msg.peers[Math.floor(Math.random()*msg.peers.length)];
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;
window.ref = gun.get('test');
function end(){
test.done();
}
gun.TO = setTimeout(end, 3000);
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("wait...", function(done){
setTimeout(function(){
done();
},3000);
});
it("Got Load Balanced to Different Peer", function(){
var tests = [], i = 0;
browsers.each(function(browser, id){
tests.push(browser.run(function(test){
test.async();
//console.log(2222222, "I don't understand", test.props.i, 'how can I have non URL peers?', ''+Object.keys(gun.back('opt.peers')));
ref.get('b'+test.props.i).put(''+Object.keys(gun.back('opt.peers')));
// NOTE: Above line was put here as a workaround. Even if this line was in the prior step, this test should still pass. Make sure there is another test that correctly checks for reconnect logic properly restoring sync.
ref.on(function(data){
if(!data.b1 || !data.b2 || !data.b3){ return }
clearTimeout(test.to);
test.to = setTimeout(function(){
//var d = {...data}; delete d._; console.log(test.props.i, "update:", JSON.stringify(d));
var now = Object.keys(gun.back('opt.peers'));
if(now.length > 1){
console.log("FAIL: too_many_connections");
too_many_connections;
return;
}
var not = {};
Object.keys(data).forEach(function(k,v){ v = data[k];
if('_' === k){ return }
if(v.split(',').length > 1){
console.log("FAIL: too_many");
too_many;
return;
}
if(not[v]){
console.log("FAIL: already");
already_;
return;
}
not[v] = 1;
});
test.done();
}, 2000);
});
}, {i: i += 1, config: config}));
});
return Promise.all(tests);
});
it("All finished!", function(done){
console.log("Done! Cleaning things up...");
setTimeout(function(){
done();
},1000);
});
after("Everything shut down.", function(){
require('../util/open').cleanup();
return servers.run(function(){
process.exit();
});
});
});

View File

@ -1,19 +1,3 @@
var ip; try{ foo; ip = require('ip').address() }catch(e){}
var config = {
IP: ip || 'localhost',
port: 8765,
servers: 1,
browsers: 2, //3,
each: 100000,
size: 1,
wait: 1,
route: {
'/': __dirname + '/index.html',
'/gun.js': __dirname + '/../../gun.js',
'/jquery.js': __dirname + '/../../examples/jquery.js'
}
}
/*
Assume we have 3 peers in a star topology,
@ -31,6 +15,23 @@ Using the WebRTC module, C <-> A directly, no need for a relay!
But we're wanting to test the performance of the whole network.
*/
// <-- 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,
servers: 1,
browsers: 2, //3,
each: 100000,
size: 1,
wait: 1,
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){ // Static server

View File

@ -1,5 +1,7 @@
// <-- PANIC template, copy & paste, tweak a few settings if needed...
var ip; try{ ip = require('ip').address() }catch(e){}
var config = {
IP: require('ip').address(),
IP: ip || 'localhost',
port: 8765,
servers: 2,
browsers: 2,
@ -37,6 +39,7 @@ var alice = browsers.pluck(1);
var bob = browsers.excluding(alice).pluck(1);
var again = {};
// continue boiler plate, tweak a few defaults if needed, but give descriptive test names...
describe("The Holy Grail Test!", function(){
//this.timeout(5 * 60 * 1000);
this.timeout(10 * 60 * 1000);
@ -80,6 +83,7 @@ describe("The Holy Grail Test!", function(){
});
return Promise.all(tests);
});
// end PANIC template -->
it("Write initial value", function(){
return alice.run(function(test){