mirror of
https://github.com/fluencelabs/js-libp2p
synced 2025-06-14 17:51:22 +00:00
fix: store multiaddrs during content and peer routing queries (#865)
* fix: store provider multiaddrs during find providers Changes the behaviour of `libp2p.contentRouting.findProviders` to store the multiaddrs reported by the routers before yielding results to the caller, so when they try to dial the provider, the multiaddrs are already in the peer store's address book. Also dedupes providers reported by routers but keeps all of the addresses reported, even for duplicates. Also, also fixes a performance bug where the previous implementation would wait for any router to completely finish finding providers before sending any results to the caller. It'll now yield results as they come in which makes it much, much faster.
This commit is contained in:
135
src/content-routing/index.js
Normal file
135
src/content-routing/index.js
Normal file
@ -0,0 +1,135 @@
|
||||
'use strict'
|
||||
|
||||
const errCode = require('err-code')
|
||||
const { messages, codes } = require('../errors')
|
||||
const {
|
||||
storeAddresses,
|
||||
uniquePeers,
|
||||
requirePeers,
|
||||
maybeLimitSource
|
||||
} = require('./utils')
|
||||
|
||||
const merge = require('it-merge')
|
||||
const { pipe } = require('it-pipe')
|
||||
|
||||
/**
|
||||
* @typedef {import('peer-id')} PeerId
|
||||
* @typedef {import('multiaddr')} Multiaddr
|
||||
* @typedef {import('cids')} CID
|
||||
*/
|
||||
|
||||
/**
|
||||
* @typedef {Object} GetData
|
||||
* @property {PeerId} from
|
||||
* @property {Uint8Array} val
|
||||
*/
|
||||
|
||||
class ContentRouting {
|
||||
/**
|
||||
* @class
|
||||
* @param {import('..')} libp2p
|
||||
*/
|
||||
constructor (libp2p) {
|
||||
this.libp2p = libp2p
|
||||
this.routers = libp2p._modules.contentRouting || []
|
||||
this.dht = libp2p._dht
|
||||
|
||||
// If we have the dht, add it to the available content routers
|
||||
if (this.dht) {
|
||||
this.routers.push(this.dht)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all content routers in parallel to find providers of the given key.
|
||||
*
|
||||
* @param {CID} key - The CID key of the content to find
|
||||
* @param {object} [options]
|
||||
* @param {number} [options.timeout] - How long the query should run
|
||||
* @param {number} [options.maxNumProviders] - maximum number of providers to find
|
||||
* @returns {AsyncIterable<{ id: PeerId, multiaddrs: Multiaddr[] }>}
|
||||
*/
|
||||
async * findProviders (key, options = {}) {
|
||||
if (!this.routers.length) {
|
||||
throw errCode(new Error('No content this.routers available'), 'NO_ROUTERS_AVAILABLE')
|
||||
}
|
||||
|
||||
yield * pipe(
|
||||
merge(
|
||||
...this.routers.map(router => router.findProviders(key, options))
|
||||
),
|
||||
(source) => storeAddresses(source, this.libp2p.peerStore),
|
||||
(source) => uniquePeers(source),
|
||||
(source) => maybeLimitSource(source, options.maxNumProviders),
|
||||
(source) => requirePeers(source)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates over all content routers in parallel to notify it is
|
||||
* a provider of the given key.
|
||||
*
|
||||
* @param {CID} key - The CID key of the content to find
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
async provide (key) {
|
||||
if (!this.routers.length) {
|
||||
throw errCode(new Error('No content routers available'), 'NO_ROUTERS_AVAILABLE')
|
||||
}
|
||||
|
||||
await Promise.all(this.routers.map((router) => router.provide(key)))
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the given key/value pair in the DHT.
|
||||
*
|
||||
* @param {Uint8Array} key
|
||||
* @param {Uint8Array} value
|
||||
* @param {Object} [options] - put options
|
||||
* @param {number} [options.minPeers] - minimum number of peers required to successfully put
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
put (key, value, options) {
|
||||
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
||||
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
||||
}
|
||||
|
||||
return this.dht.put(key, value, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value to the given key.
|
||||
* Times out after 1 minute by default.
|
||||
*
|
||||
* @param {Uint8Array} key
|
||||
* @param {Object} [options] - get options
|
||||
* @param {number} [options.timeout] - optional timeout (default: 60000)
|
||||
* @returns {Promise<GetData>}
|
||||
*/
|
||||
get (key, options) {
|
||||
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
||||
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
||||
}
|
||||
|
||||
return this.dht.get(key, options)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the `n` values to the given key without sorting.
|
||||
*
|
||||
* @param {Uint8Array} key
|
||||
* @param {number} nVals
|
||||
* @param {Object} [options] - get options
|
||||
* @param {number} [options.timeout] - optional timeout (default: 60000)
|
||||
* @returns {Promise<GetData[]>}
|
||||
*/
|
||||
async getMany (key, nVals, options) { // eslint-disable-line require-await
|
||||
if (!this.libp2p.isStarted() || !this.dht.isStarted) {
|
||||
throw errCode(new Error(messages.NOT_STARTED_YET), codes.DHT_NOT_STARTED)
|
||||
}
|
||||
|
||||
return this.dht.getMany(key, nVals, options)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ContentRouting
|
Reference in New Issue
Block a user