-
Notifications
You must be signed in to change notification settings - Fork 17
feat: compatibility with go-libp2p-mdns #80
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a couple of comments about event listeners, otherwise looks good 👍
Would it make sense to branch of the async/await version of this package?
src/compat/querier.js
Outdated
// Create a querier that queries multicast but gets responses unicast | ||
const querier = MDNS({ multicast: false, interface: '0.0.0.0', port: 0 }) | ||
|
||
querier.on('response', this._onResponse) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think having this event listener will prevent the querier objects from being garbage collected
(looking at the source code for mdns, it doesn't seem like destroy()
removes all event listeners)
} | ||
|
||
stop (callback) { | ||
this._mdns.destroy(callback) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this._mdns.removeListener('query', this._onQuery)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work Alan! 🥇
I think that it is a good call to have this in a separate class!
Regarding @dirkmc 's point for branching out from the async await branch, according to the table in ipfs/js-ipfs#1670 it is a P3+, and should have a lot of dependencies to get merged first. However, the async migration PR will have to be updated with this. Looking at the code, it does not seem a lot of work. What we can also do is changing this PR's codebase (compat) to use async await from the beginning, which would ease the other PR to get merged afterward. What do you guys think?
Also, do not forget to provide documentation for the new classes added.
src/compat/querier.js
Outdated
return { stop: callback => querier.destroy(callback) } | ||
}, this._options.queryPeriod) | ||
|
||
setImmediate(() => callback()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use process.nextTick
instead
src/index.js
Outdated
if (this._goMdns) { | ||
this._goMdns.start(callback) | ||
} else { | ||
setImmediate(() => callback()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
process.nextTick
src/compat/responder.js
Outdated
start (callback) { | ||
this._mdns = MDNS() | ||
this._mdns.on('query', this._onQuery) | ||
setImmediate(() => callback()) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
process.nextTick
data: peerServiceTagLocal | ||
}) | ||
|
||
// Only announce TCP multiaddrs for now |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you have as an action item of merging this PR to create an issue for tracking the announce of other transports?
I don't think we should mix the two styles in the code base:
from libp2p/js-libp2p-switch#310 (comment) So either we merge as is or rebase off of the async/await branch. |
My main concern here relates to #71, in regards to polling mdns. Currently this is configured to run the go query every 5 seconds by default. The normal query is set to run every 10s by default. Running 3 queries every 10 seconds is abusive to mdns and devices using it. I think this might be a good opportunity for us to look at disabling polling by default, and allowing an interval to be set, instead of defaulting to polling. As new devices join, we should discover them when they broadcast. |
Ok, there are some options:
Simply sending a multicast go-ipfs compatible "response" on an interval might work because I think go-ipfs accepts responses multicast as well as unicast. It's similar to (2) above, except that go-ipfs nodes will discover us when we send a response in an interval, not as a response to their query. I'll check it out and report back. |
It works! #81 |
So, #81 doesn't do a query at all which is why we won't be able to get go nodes. It should still do a query, it just shouldn't need to be run on an interval. Anytime a node starts, it should listen for MDNS queries and then immediately query. If we start another node 10 minutes later and it does the same thing. The first node should be made aware of the new node, and vice versa. For every node we have, we should only need that many queries, as long as everyone responds. There is the problem of sleeping nodes or network changes, but since our discovery doesn't work all that consistently right now, I think that could be addressed in a followup release or temporarily just increasing the interval. |
@jacobheun I've altered this PR to increase the interval between MDNS queries to 1 min. When
This follows the implementation of The JS implementation is untouched. Can you please confirm that's cool and I'll get on with adding tests. |
@alanshaw that sounds reasonable to me. I'll look at trying to get libp2p/specs#80 at least moved into Draft so those proper implementations can get unblocked. I believe rust-libp2p is already using that spec. |
This PR adds a compatibility class that allows a js-libp2p node to find a go-libp2p node (and vice versa) over MDNS. It's implemented as a separate class so the two differing implementations do not get confused. I've verified this is working correctly by running a go-ipfs and js-ipfs node with no boostrap nodes (and no other discovery methods) and verifying they find each other. TODO: * [ ] Add tests! Some tips if you want to try this out: * After you've run `ipfs init`, remember to remove the bootstrap nodes from the config file (`~/.ipfs/config`) of each node before you start up * Use `ipfs log level mdns debug` for some go-ipfs mdns logs * You can use the following script (after `npm link`ing this branch) to start a js-ipfs node with no bootstrap nodes and no discovery modules other than MDNS: ```js const IPFS = require('ipfs') const MDNS = require('libp2p-mdns') const TCP = require('libp2p-tcp') const ipfs = new IPFS({ repo: '/tmp/ipfs-mdns', config: { Bootstrap: [] }, libp2p: { modules: { peerDiscovery: [MDNS], transport: [TCP] } } }) ipfs.on('ready', async () => { console.log('ipfs is ready') console.log('My Peer ID:', (await ipfs.id()).id) setInterval(async () => { const peers = await ipfs.swarm.peers() console.log(peers.length, 'peers:') peers.forEach(p => console.log(p.peer.toB58String())) }, 10000) }) ``` License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
Co-Authored-By: alanshaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
License: MIT Signed-off-by: Alan Shaw <[email protected]>
dc964c1
to
37fbfdd
Compare
License: MIT Signed-off-by: Alan Shaw <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Other than adding the compat
option to the readme, this looks good.
@@ -18,10 +21,18 @@ class MulticastDNS extends EventEmitter { | |||
this.port = options.port || 5353 | |||
this.peerInfo = options.peerInfo | |||
this._queryInterval = null | |||
this._onPeer = this._onPeer.bind(this) | |||
|
|||
if (options.compat !== false) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs to be added to the readme.
License: MIT Signed-off-by: Alan Shaw <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. Just FYI, the mdns spec was merged https://github.com/libp2p/specs/blob/master/discovery/mdns.md. We can work with the go team to try and coordinate some future releases to the new version.
This PR adds a compatibility class that allows a js-libp2p node to find a go-libp2p node (and vice versa) over MDNS. I do not know when go-libp2p plans to land the long awaited new MDNS implementation but until then this will allow interop between js and go.
It's implemented as a separate class so the two differing implementations do not get confused.
Fun fact, it uses 2 dgram servers. 1 is the multicast listener, which simply responds to queries. The other is a dgram server that exists for 5 seconds at a time, sends a multicast query, waits for unicast responses, stops and then starts up again on a different random port.
I've verified this is working correctly by running a go-ipfs and js-ipfs node with no boostrap nodes (and no other discovery methods) and verifying they find each other.
TODO:
Some tips if you want to try this out:
ipfs init
, remember to remove the bootstrap nodes from the config file (~/.ipfs/config
) of each node before you start upipfs log level mdns debug
for some go-ipfs mdns logsnpm link
ing this branch) to start a js-ipfs node with no bootstrap nodes and no discovery modules other than MDNS: