Commit 36cd0eeba48657072d1b4544e72acf7e5eead61f
fix prioritizing connection to pubs that are friends
Matt McKegg committed on 11/3/2016, 9:53:34 PMParent: 68976cde98459b3f066c8c3c0fab7c054a4d848c
Files changed
lib/persistent-gossip/index.js | changed |
lib/persistent-gossip/init.js | changed |
lib/persistent-gossip/schedule.js | changed |
lib/friends-with-gossip-priority.js | added |
package.json | changed |
server-process.js | changed |
lib/persistent-gossip/index.js | ||
---|---|---|
@@ -21,12 +21,8 @@ | ||
21 | 21 | function stringify(peer) { |
22 | 22 | return [peer.host, peer.port, peer.key].join(':') |
23 | 23 | } |
24 | 24 | |
25 | -function isFriends (friends, a, b) { | |
26 | - return friends[a] && friends[b] && friends[a][b] && friends[b][a] | |
27 | -} | |
28 | - | |
29 | 25 | /* |
30 | 26 | Peers : [{ |
31 | 27 | key: id, |
32 | 28 | host: ip, |
@@ -67,18 +63,8 @@ | ||
67 | 63 | wakeup: 0, |
68 | 64 | peers: function () { |
69 | 65 | return peers |
70 | 66 | }, |
71 | - connectToFriends: function () { | |
72 | - server.friends.all((err, friends) => { | |
73 | - if (err) return | |
74 | - peers.forEach(function(peer) { | |
75 | - if (!Schedule.isConnect(peer) && isFriends(friends, server.id, peer.key)) { | |
76 | - gossip.connect(peer, function() {}) | |
77 | - } | |
78 | - }) | |
79 | - }) | |
80 | - }, | |
81 | 67 | get: function (addr) { |
82 | 68 | addr = ref.parseAddress(addr) |
83 | 69 | return u.find(peers, function (a) { |
84 | 70 | return ( |
@@ -154,11 +140,11 @@ | ||
154 | 140 | addr.duration = addr.duration || null |
155 | 141 | peers.push(addr) |
156 | 142 | notify({ type: 'discover', peer: addr, source: source || 'manual' }) |
157 | 143 | return addr |
158 | - } else if (source === 'local') { | |
159 | - // this peer is local to us, override old source | |
160 | - addr.source = 'local' | |
144 | + } else if (source === 'friends' || source === 'local') { | |
145 | + // this peer is a friend or local, override old source to prioritize gossip | |
146 | + f.source = source | |
161 | 147 | } |
162 | 148 | //don't count local over and over |
163 | 149 | else if(f.source != 'local') |
164 | 150 | f.announcers ++ |
lib/persistent-gossip/init.js | ||
---|---|---|
@@ -19,16 +19,13 @@ | ||
19 | 19 | if(msg.sync) return |
20 | 20 | if(!msg.content.address) return |
21 | 21 | if(ref.isAddress(msg.content.address)) |
22 | 22 | gossip.add(msg.content.address, 'pub') |
23 | - }, gossip.connectToFriends) | |
23 | + }) | |
24 | 24 | ) |
25 | 25 | |
26 | 26 | // populate peertable with announcements on the LAN multicast |
27 | 27 | server.on('local', function (_peer) { |
28 | 28 | gossip.add(_peer, 'local') |
29 | 29 | }) |
30 | 30 | |
31 | 31 | } |
32 | - | |
33 | - | |
34 | - |
lib/persistent-gossip/schedule.js | ||
---|---|---|
@@ -46,8 +46,12 @@ | ||
46 | 46 | // cjdns creates fake private ip addresses. |
47 | 47 | return ip.isPrivate(e.host) && e.source === 'local' |
48 | 48 | } |
49 | 49 | |
50 | +function isFriend (e) { | |
51 | + return e.source === 'friends' | |
52 | +} | |
53 | + | |
50 | 54 | function isUnattempted (e) { |
51 | 55 | return !e.stateChange |
52 | 56 | } |
53 | 57 | |
@@ -108,8 +112,9 @@ | ||
108 | 112 | return (value === undefined || value === '') ? def : value |
109 | 113 | } |
110 | 114 | |
111 | 115 | function connect (peers, ts, name, filter, opts) { |
116 | + opts.group = name | |
112 | 117 | var connected = peers.filter(isConnect).filter(filter) |
113 | 118 | |
114 | 119 | //disconnect if over quota |
115 | 120 | if(connected.length > opts.quota) { |
@@ -136,21 +141,44 @@ | ||
136 | 141 | connecting = false |
137 | 142 | var ts = Date.now() |
138 | 143 | var peers = gossip.peers() |
139 | 144 | |
140 | - var connected = peers.filter(and(isConnect, not(isLocal))).length | |
145 | + var connected = peers.filter(and(isConnect, not(isLocal), not(isFriend))).length | |
146 | + var connectedFriends = peers.filter(and(isConnect, isFriend)).length | |
141 | 147 | |
142 | - connect(peers, ts, 'longterm', exports.isLongterm, { | |
143 | - quota: 3, factor: 10e3, max: 10*min, groupMin: 5e3, | |
144 | - disable: !conf('global', true) | |
145 | - }) | |
146 | - | |
147 | 148 | connect(peers, ts, 'local', exports.isLocal, { |
148 | 149 | quota: 3, factor: 2e3, max: 10*min, groupMin: 1e3, |
149 | 150 | disable: !conf('local', true) |
150 | 151 | }) |
151 | 152 | |
152 | - if(connected < 3) | |
153 | + // prioritize friends | |
154 | + connect(peers, ts, 'friends', and(exports.isFriend, exports.isLongterm), { | |
155 | + quota: 2, factor: 10e3, max: 10*min, groupMin: 5e3, | |
156 | + disable: !conf('local', true) | |
157 | + }) | |
158 | + | |
159 | + if (connectedFriends < 2) | |
160 | + connect(peers, ts, 'attemptFriend', and(exports.isFriend, exports.isUnattempted), { | |
161 | + min: 0, quota: 1, factor: 0, max: 0, groupMin: 0, | |
162 | + disable: !conf('global', true) | |
163 | + }) | |
164 | + | |
165 | + connect(peers, ts, 'retryFriends', and(exports.isFriend, exports.isInactive), { | |
166 | + min: 0, | |
167 | + quota: 3, factor: 60e3, max: 3*60*60e3, groupMin: 5*60e3 | |
168 | + }) | |
169 | + | |
170 | + // standard longterm peers | |
171 | + connect(peers, ts, 'longterm', and( | |
172 | + exports.isLongterm, | |
173 | + not(exports.isFriend), | |
174 | + not(exports.isLocal) | |
175 | + ), { | |
176 | + quota: 2, factor: 10e3, max: 10*min, groupMin: 5e3, | |
177 | + disable: !conf('global', true) | |
178 | + }) | |
179 | + | |
180 | + if(!connected) | |
153 | 181 | connect(peers, ts, 'attempt', exports.isUnattempted, { |
154 | 182 | min: 0, quota: 1, factor: 0, max: 0, groupMin: 0, |
155 | 183 | disable: !conf('global', true) |
156 | 184 | }) |
@@ -199,6 +227,7 @@ | ||
199 | 227 | exports.isInactive = isInactive |
200 | 228 | exports.isLongterm = isLongterm |
201 | 229 | exports.isLegacy = isLegacy |
202 | 230 | exports.isLocal = isLocal |
231 | +exports.isFriend = isFriend | |
203 | 232 | exports.isConnectedOrConnecting = isConnect |
204 | 233 | exports.select = select |
lib/friends-with-gossip-priority.js | ||
---|---|---|
@@ -1,0 +1,178 @@ | ||
1 | +var Graphmitter = require('graphmitter') | |
2 | +var pull = require('pull-stream') | |
3 | +var mlib = require('ssb-msgs') | |
4 | +var memview = require('level-memview') | |
5 | +var pushable = require('pull-pushable') | |
6 | +var mdm = require('mdmanifest') | |
7 | +var valid = require('scuttlebot/lib/validators') | |
8 | +var apidoc = require('scuttlebot/lib/apidocs').friends | |
9 | + | |
10 | +// friends plugin | |
11 | +// methods to analyze the social graph | |
12 | +// maintains a 'follow' and 'flag' graph | |
13 | + | |
14 | +function isFunction (f) { | |
15 | + return 'function' === typeof f | |
16 | +} | |
17 | + | |
18 | +function isString (s) { | |
19 | + return 'string' === typeof s | |
20 | +} | |
21 | + | |
22 | +function isFriend (friends, a, b) { | |
23 | + return friends[a] && friends[b] && friends[a][b] && friends[b][a] | |
24 | +} | |
25 | + | |
26 | +exports.name = 'friends' | |
27 | +exports.version = '1.0.0' | |
28 | +exports.manifest = mdm.manifest(apidoc) | |
29 | + | |
30 | +exports.init = function (sbot, config) { | |
31 | + | |
32 | + var graphs = { | |
33 | + follow: new Graphmitter(), | |
34 | + flag: new Graphmitter() | |
35 | + } | |
36 | + | |
37 | + // view processor | |
38 | + var syncCbs = [] | |
39 | + function awaitSync (cb) { | |
40 | + if (syncCbs) syncCbs.push(cb) | |
41 | + else cb() | |
42 | + } | |
43 | + | |
44 | + // read/watch the log for changes to the social graph | |
45 | + pull(sbot.createLogStream({ live: true }), pull.drain(function (msg) { | |
46 | + | |
47 | + if (msg.sync) { | |
48 | + syncCbs.forEach(function (cb) { cb() }) | |
49 | + syncCbs = null | |
50 | + | |
51 | + if (sbot.gossip) { | |
52 | + // prioritize friends | |
53 | + var friends = graphs['follow'].toJSON() | |
54 | + sbot.gossip.peers().forEach(function(peer) { | |
55 | + if (isFriend(friends, sbot.id, peer.key)) { | |
56 | + sbot.gossip.add(peer, 'friends') | |
57 | + } | |
58 | + }) | |
59 | + } | |
60 | + | |
61 | + return | |
62 | + } | |
63 | + | |
64 | + var c = msg.value.content | |
65 | + if (c.type == 'contact') { | |
66 | + mlib.asLinks(c.contact, 'feed').forEach(function (link) { | |
67 | + if ('following' in c) { | |
68 | + if (c.following) | |
69 | + graphs.follow.edge(msg.value.author, link.link, true) | |
70 | + else | |
71 | + graphs.follow.del(msg.value.author, link.link) | |
72 | + | |
73 | + } | |
74 | + if ('flagged' in c) { | |
75 | + if (c.flagged) | |
76 | + graphs.flag.edge(msg.value.author, link.link, c.flagged) | |
77 | + else | |
78 | + graphs.flag.del(msg.value.author, link.link) | |
79 | + } | |
80 | + }) | |
81 | + } | |
82 | + })) | |
83 | + | |
84 | + return { | |
85 | + | |
86 | + get: valid.sync(function (opts) { | |
87 | + var g = graphs[opts.graph || 'follow'] | |
88 | + if(!g) throw new Error('opts.graph must be provided') | |
89 | + return g.get(opts.source, opts.dest) | |
90 | + }, 'object?'), | |
91 | + | |
92 | + all: valid.async(function (graph, cb) { | |
93 | + if (typeof graph == 'function') { | |
94 | + cb = graph | |
95 | + graph = null | |
96 | + } | |
97 | + if (!graph) | |
98 | + graph = 'follow' | |
99 | + awaitSync(function () { | |
100 | + cb(null, graphs[graph] ? graphs[graph].toJSON() : null) | |
101 | + }) | |
102 | + }, 'string?'), | |
103 | + | |
104 | + path: valid.sync(function (opts) { | |
105 | + if(isString(opts)) | |
106 | + opts = {source: sbot.id, dest: opts} | |
107 | + return graphs.follow.path(opts) | |
108 | + | |
109 | + }, 'string|object'), | |
110 | + | |
111 | + createFriendStream: valid.source(function (opts) { | |
112 | + opts = opts || {} | |
113 | + var live = opts.live === true | |
114 | + var meta = opts.meta === true | |
115 | + var start = opts.start || sbot.id | |
116 | + var graph = graphs[opts.graph || 'follow'] | |
117 | + if(!graph) | |
118 | + return pull.error(new Error('unknown graph:' + opts.graph)) | |
119 | + var cancel, ps = pushable(function () { | |
120 | + cancel && cancel() | |
121 | + }) | |
122 | + | |
123 | + function push (to, hops) { | |
124 | + return ps.push(meta ? {id: to, hops: hops} : to) | |
125 | + } | |
126 | + | |
127 | + //by default, also emit your own key. | |
128 | + if(opts.self !== false) | |
129 | + push(start, 0) | |
130 | + | |
131 | + var conf = config.friends || {} | |
132 | + cancel = graph.traverse({ | |
133 | + start: start, | |
134 | + hops: opts.hops || conf.hops || 3, | |
135 | + max: opts.dunbar || conf.dunbar || 150, | |
136 | + each: function (_, to, hops) { | |
137 | + if(to !== start) push(to, hops) | |
138 | + } | |
139 | + }) | |
140 | + | |
141 | + if(!live) { cancel(); ps.end() } | |
142 | + | |
143 | + return ps | |
144 | + }, 'createFriendStreamOpts?'), | |
145 | + | |
146 | + hops: valid.async(function (start, graph, opts, cb) { | |
147 | + if (typeof opts == 'function') { // (start|opts, graph, cb) | |
148 | + cb = opts | |
149 | + opts = null | |
150 | + } else if (typeof graph == 'function') { // (start|opts, cb) | |
151 | + cb = graph | |
152 | + opts = graph = null | |
153 | + } | |
154 | + opts = opts || {} | |
155 | + if(isString(start)) { // (start, ...) | |
156 | + // first arg is id string | |
157 | + opts.start = start | |
158 | + } else if (start && typeof start == 'object') { // (opts, ...) | |
159 | + // first arg is opts | |
160 | + for (var k in start) | |
161 | + opts[k] = start[k] | |
162 | + } | |
163 | + | |
164 | + var conf = config.friends || {} | |
165 | + opts.start = opts.start || sbot.id | |
166 | + opts.dunbar = opts.dunbar || conf.dunbar || 150 | |
167 | + opts.hops = opts.hops || conf.hops || 3 | |
168 | + | |
169 | + var g = graphs[graph || 'follow'] | |
170 | + if (!g) | |
171 | + return cb(new Error('Invalid graph type: '+graph)) | |
172 | + | |
173 | + awaitSync(function () { | |
174 | + cb(null, g.traverse(opts)) | |
175 | + }) | |
176 | + }, ['feedId', 'string?', 'object?'], ['createFriendStreamOpts']) | |
177 | + } | |
178 | +} |
package.json | ||
---|---|---|
@@ -17,11 +17,13 @@ | ||
17 | 17 | "data-uri-to-buffer": "0.0.4", |
18 | 18 | "deep-equal": "^1.0.1", |
19 | 19 | "electron": "~1.4.4", |
20 | 20 | "electron-default-menu": "~1.0.0", |
21 | + "graphmitter": "^1.6.3", | |
21 | 22 | "has-network": "0.0.0", |
22 | 23 | "insert-css": "~1.0.0", |
23 | 24 | "is-visible": "^2.1.1", |
25 | + "level-memview": "0.0.0", | |
24 | 26 | "micro-css": "~0.6.2", |
25 | 27 | "non-private-ip": "^1.4.1", |
26 | 28 | "on-change-network": "0.0.2", |
27 | 29 | "on-wakeup": "^1.0.1", |
server-process.js | ||
---|---|---|
@@ -5,9 +5,9 @@ | ||
5 | 5 | |
6 | 6 | var createSbot = require('scuttlebot') |
7 | 7 | .use(require('scuttlebot/plugins/master')) |
8 | 8 | .use(require('./lib/persistent-gossip')) // override |
9 | - .use(require('scuttlebot/plugins/friends')) | |
9 | + .use(require('./lib/friends-with-gossip-priority')) | |
10 | 10 | .use(require('scuttlebot/plugins/replicate')) |
11 | 11 | .use(require('ssb-blobs')) |
12 | 12 | .use(require('scuttlebot/plugins/invite')) |
13 | 13 | .use(require('scuttlebot/plugins/block')) |
Built with git-ssb-web