UNPKG

libp2p

Version:

JavaScript implementation of libp2p, a modular peer to peer network stack

129 lines 4.16 kB
import { KEEP_ALIVE } from '@libp2p/interface'; import { PeerQueue } from '@libp2p/utils/peer-queue'; import pRetry from 'p-retry'; import { MAX_PARALLEL_RECONNECTS } from './constants.js'; /** * When peers tagged with `KEEP_ALIVE` disconnect, this component attempts to * redial them */ export class ReconnectQueue { log; queue; started; peerStore; retries; retryInterval; backoffFactor; connectionManager; events; constructor(components, init = {}) { this.log = components.logger.forComponent('libp2p:reconnect-queue'); this.peerStore = components.peerStore; this.connectionManager = components.connectionManager; this.queue = new PeerQueue({ concurrency: init.maxParallelReconnects ?? MAX_PARALLEL_RECONNECTS, metricName: 'libp2p_reconnect_queue', metrics: components.metrics }); this.started = false; this.retries = init.retries ?? 5; this.backoffFactor = init.backoffFactor; this.retryInterval = init.retryInterval; this.events = components.events; components.events.addEventListener('peer:disconnect', (evt) => { this.maybeReconnect(evt.detail) .catch(err => { this.log.error('failed to maybe reconnect to %p - %e', evt.detail, err); }); }); } async maybeReconnect(peerId) { if (!this.started) { return; } const peer = await this.peerStore.get(peerId); if (!hasKeepAliveTag(peer)) { return; } if (this.queue.has(peerId)) { return; } this.queue.add(async (options) => { await pRetry(async (attempt) => { if (!this.started) { return; } try { await this.connectionManager.openConnection(peerId, { signal: options?.signal }); } catch (err) { this.log('reconnecting to %p attempt %d of %d failed - %e', peerId, attempt, this.retries, err); throw err; } }, { signal: options?.signal, retries: this.retries, factor: this.backoffFactor, minTimeout: this.retryInterval }); }, { peerId }) .catch(async (err) => { this.log.error('failed to reconnect to %p - %e', peerId, err); const tags = {}; [...peer.tags.keys()].forEach(key => { if (key.startsWith(KEEP_ALIVE)) { tags[key] = undefined; } }); await this.peerStore.merge(peerId, { tags }); this.events.safeDispatchEvent('peer:reconnect-failure', { detail: peerId }); }) .catch(async (err) => { this.log.error('failed to remove keep-alive tag from %p - %e', peerId, err); }); } start() { this.started = true; } async afterStart() { // re-connect to any peers with the KEEP_ALIVE tag void Promise.resolve() .then(async () => { const keepAlivePeers = await this.peerStore.all({ filters: [ (peer) => hasKeepAliveTag(peer) ] }); await Promise.all(keepAlivePeers.map(async (peer) => { await this.connectionManager.openConnection(peer.id) .catch(err => { this.log.error(err); }); })); }) .catch(err => { this.log.error(err); }); } stop() { this.started = false; this.queue.abort(); } } function hasKeepAliveTag(peer) { for (const tag of peer.tags.keys()) { if (tag.startsWith(KEEP_ALIVE)) { return true; } } return false; } //# sourceMappingURL=reconnect-queue.js.map