From b23af01bee5d5f3b02ad624a0856886c6b3a0640 Mon Sep 17 00:00:00 2001
From: achingbrain <alex@achingbrain.net>
Date: Sat, 2 Sep 2023 16:09:27 +0100
Subject: [PATCH 1/2] fix(libp2p): emit peer:discovered event on internal event
 bus

To notifiy components that a new peer has been discovered, emit the
`peer:discovered` event on the event bus component.

Allows the auto-dialer to choose to dial newly discovered peers
immediately instead of waiting for the next autodial interval.
---
 packages/libp2p/src/connection-manager/auto-dial.ts | 9 +++++++++
 packages/libp2p/src/libp2p.ts                       | 2 +-
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/packages/libp2p/src/connection-manager/auto-dial.ts b/packages/libp2p/src/connection-manager/auto-dial.ts
index 7ba2247a8f..0b75768d16 100644
--- a/packages/libp2p/src/connection-manager/auto-dial.ts
+++ b/packages/libp2p/src/connection-manager/auto-dial.ts
@@ -77,6 +77,15 @@ export class AutoDial implements Startable {
           log.error(err)
         })
     })
+
+    // when new peers are discovered, dial them if we don't have
+    // enough connections
+    components.events.addEventListener('peer:discovery', () => {
+      this.autoDial()
+        .catch(err => {
+          log.error(err)
+        })
+    })
   }
 
   isStarted (): boolean {
diff --git a/packages/libp2p/src/libp2p.ts b/packages/libp2p/src/libp2p.ts
index 5df57f3aaf..29c80f0cc7 100644
--- a/packages/libp2p/src/libp2p.ts
+++ b/packages/libp2p/src/libp2p.ts
@@ -105,7 +105,7 @@ export class Libp2pNode<T extends ServiceMap = Record<string, unknown>> extends
           protocols: evt.detail.peer.protocols
         }
 
-        this.safeDispatchEvent('peer:discovery', { detail: peerInfo })
+        components.events.safeDispatchEvent('peer:discovery', { detail: peerInfo })
       }
     })
 

From 14d74afec7d1f5a867d385311f49c9b01841dcc4 Mon Sep 17 00:00:00 2001
From: achingbrain <alex@achingbrain.net>
Date: Sun, 3 Sep 2023 09:40:06 +0100
Subject: [PATCH 2/2] chore: debounce autodial on peer discovery

---
 .../src/connection-manager/auto-dial.ts       | 23 ++++++++++++++-----
 .../connection-manager/constants.defaults.ts  |  5 ++++
 .../libp2p/src/connection-manager/index.ts    |  8 +++++++
 3 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/packages/libp2p/src/connection-manager/auto-dial.ts b/packages/libp2p/src/connection-manager/auto-dial.ts
index 0b75768d16..49eab00ef3 100644
--- a/packages/libp2p/src/connection-manager/auto-dial.ts
+++ b/packages/libp2p/src/connection-manager/auto-dial.ts
@@ -2,7 +2,7 @@ import { logger } from '@libp2p/logger'
 import { PeerMap, PeerSet } from '@libp2p/peer-collections'
 import { toString as uint8ArrayToString } from 'uint8arrays/to-string'
 import { PeerJobQueue } from '../utils/peer-job-queue.js'
-import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_INTERVAL, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PEER_RETRY_THRESHOLD, AUTO_DIAL_PRIORITY, LAST_DIAL_FAILURE_KEY, MIN_CONNECTIONS } from './constants.js'
+import { AUTO_DIAL_CONCURRENCY, AUTO_DIAL_DISCOVERED_PEERS_DEBOUNCE, AUTO_DIAL_INTERVAL, AUTO_DIAL_MAX_QUEUE_LENGTH, AUTO_DIAL_PEER_RETRY_THRESHOLD, AUTO_DIAL_PRIORITY, LAST_DIAL_FAILURE_KEY, MIN_CONNECTIONS } from './constants.js'
 import type { Libp2pEvents } from '@libp2p/interface'
 import type { EventEmitter } from '@libp2p/interface/events'
 import type { PeerStore } from '@libp2p/interface/peer-store'
@@ -18,6 +18,7 @@ interface AutoDialInit {
   autoDialPriority?: number
   autoDialInterval?: number
   autoDialPeerRetryThreshold?: number
+  autoDialDiscoveredPeersDebounce?: number
 }
 
 interface AutoDialComponents {
@@ -32,7 +33,8 @@ const defaultOptions = {
   autoDialConcurrency: AUTO_DIAL_CONCURRENCY,
   autoDialPriority: AUTO_DIAL_PRIORITY,
   autoDialInterval: AUTO_DIAL_INTERVAL,
-  autoDialPeerRetryThreshold: AUTO_DIAL_PEER_RETRY_THRESHOLD
+  autoDialPeerRetryThreshold: AUTO_DIAL_PEER_RETRY_THRESHOLD,
+  autoDialDiscoveredPeersDebounce: AUTO_DIAL_DISCOVERED_PEERS_DEBOUNCE
 }
 
 export class AutoDial implements Startable {
@@ -44,6 +46,7 @@ export class AutoDial implements Startable {
   private readonly autoDialIntervalMs: number
   private readonly autoDialMaxQueueLength: number
   private readonly autoDialPeerRetryThresholdMs: number
+  private readonly autoDialDiscoveredPeersDebounce: number
   private autoDialInterval?: ReturnType<typeof setInterval>
   private started: boolean
   private running: boolean
@@ -61,6 +64,7 @@ export class AutoDial implements Startable {
     this.autoDialIntervalMs = init.autoDialInterval ?? defaultOptions.autoDialInterval
     this.autoDialMaxQueueLength = init.maxQueueLength ?? defaultOptions.maxQueueLength
     this.autoDialPeerRetryThresholdMs = init.autoDialPeerRetryThreshold ?? defaultOptions.autoDialPeerRetryThreshold
+    this.autoDialDiscoveredPeersDebounce = init.autoDialDiscoveredPeersDebounce ?? defaultOptions.autoDialDiscoveredPeersDebounce
     this.started = false
     this.running = false
     this.queue = new PeerJobQueue({
@@ -78,13 +82,20 @@ export class AutoDial implements Startable {
         })
     })
 
+    // sometimes peers are discovered in quick succession so add a small
+    // debounce to ensure all eligible peers are autodialed
+    let debounce: ReturnType<typeof setTimeout>
+
     // when new peers are discovered, dial them if we don't have
     // enough connections
     components.events.addEventListener('peer:discovery', () => {
-      this.autoDial()
-        .catch(err => {
-          log.error(err)
-        })
+      clearTimeout(debounce)
+      debounce = setTimeout(() => {
+        this.autoDial()
+          .catch(err => {
+            log.error(err)
+          })
+      }, this.autoDialDiscoveredPeersDebounce)
     })
   }
 
diff --git a/packages/libp2p/src/connection-manager/constants.defaults.ts b/packages/libp2p/src/connection-manager/constants.defaults.ts
index 60e7e5c430..a07de40eec 100644
--- a/packages/libp2p/src/connection-manager/constants.defaults.ts
+++ b/packages/libp2p/src/connection-manager/constants.defaults.ts
@@ -38,6 +38,11 @@ export const AUTO_DIAL_MAX_QUEUE_LENGTH = 100
  */
 export const AUTO_DIAL_PEER_RETRY_THRESHOLD = 1000 * 60
 
+/**
+ * @see https://libp2p.github.io/js-libp2p/interfaces/libp2p.index.unknown.ConnectionManagerInit.html#autoDialDiscoveredPeersDebounce
+ */
+export const AUTO_DIAL_DISCOVERED_PEERS_DEBOUNCE = 10
+
 /**
  * @see https://libp2p.github.io/js-libp2p/interfaces/index._internal_.ConnectionManagerConfig.html#inboundConnectionThreshold
  */
diff --git a/packages/libp2p/src/connection-manager/index.ts b/packages/libp2p/src/connection-manager/index.ts
index 4258bd4c87..c371a5b38c 100644
--- a/packages/libp2p/src/connection-manager/index.ts
+++ b/packages/libp2p/src/connection-manager/index.ts
@@ -72,6 +72,14 @@ export interface ConnectionManagerInit {
    */
   autoDialPeerRetryThreshold?: number
 
+  /**
+   * Newly discovered peers may be auto-dialed to increase the number of open
+   * connections, but they can be discovered in quick succession so add a small
+   * delay before attempting to dial them in case more peers have been
+   * discovered. (default: 10ms)
+   */
+  autoDialDiscoveredPeersDebounce?: number
+
   /**
    * Sort the known addresses of a peer before trying to dial, By default public
    * addresses will be dialled before private (e.g. loopback or LAN) addresses.