libp2p-hs-0.1.0.0: Haskell implementation of the libp2p networking stack
Safe HaskellNone
LanguageGHC2021

LibP2P.Switch.Dial

Description

Dial logic for the Switch.

Implements connection reuse, exponential backoff, dial deduplication, and parallel staggered dialing (Happy Eyeballs, RFC 8305).

Dial flow: 1. Check connection pool for existing Open connection 2. Check per-peer backoff (reject if recently failed) 3. Deduplication: join pending dial if another thread is already dialing 4. Select transport per address, staggered parallel dial 5. Upgrade first successful raw connection 6. Add to pool / record backoff on failure

Synopsis

Main entry point

dial :: Switch -> PeerId -> [Multiaddr] -> IO (Either DialError Connection) Source #

Dial a peer, reusing existing connections or establishing new ones.

Implements the full dial flow: 1. Pool reuse: return existing Open connection if available 2. Backoff check: reject if peer recently failed 3. Deduplication: coalesce concurrent dials to same peer via TMVar 4. Staggered parallel dial with 250ms delay (Happy Eyeballs) 5. First success: upgrade, add to pool, return 6. All fail: record backoff, return error

Dial options

data DialOpts Source #

Per-dial options.

Mirrors the two orthogonal context values go-libp2p threads through a dial: network.WithForceDirectDial and network.WithSimultaneousConnect, which its hole puncher sets together.

Constructors

DialOpts 

Fields

  • doForceDirect :: !Bool

    Bypass connection reuse, dial backoff and dial deduplication, and always establish a new transport connection. Hole punching needs this: reusing a pooled connection emits no packet at all, so the TCP simultaneous connect the DCUtR spec relies on cannot happen. go-libp2p likewise consults backoff only when the dial is not force-direct.

  • doUpgradeAsClient :: !Bool

    Whether to run the client side of the security handshake and the muxer. False upgrades as the responder over a connection we dialled, which specsrelayDCUtR requires of peer B: "For the purpose of all protocols run on top of this TCP connection, A is assumed to be the client and B the server."

defaultDialOpts :: DialOpts Source #

Ordinary dial: reuse pooled connections, honour backoff, act as client.

dialWith :: Switch -> DialOpts -> PeerId -> [Multiaddr] -> IO (Either DialError Connection) Source #

Dial a peer under explicit options.

A force-direct dial skips steps 1-3 entirely. Skipping deduplication is required, not incidental: DCUtR calls its dialer once per address so that every address is attempted at the same moment, and a shared pending-dial TMVar carries one result for all waiters, so joining it would collapse those attempts into a single address. Backoff is still *recorded* on failure, as go-libp2p does.

Backoff management

checkBackoff :: TVar (Map PeerId BackoffEntry) -> PeerId -> IO (Either DialError ()) Source #

Check if a peer is currently in dial backoff. Returns Right () if no backoff is active or the backoff has expired. Expired entries are cleaned up atomically.

recordBackoff :: TVar (Map PeerId BackoffEntry) -> PeerId -> IO () Source #

Record a backoff after a failed dial. First failure: 5s. Each subsequent: duration * 2, capped at 300s. Backoff formula: min(initialBackoff * 2^(attempts-1), maxBackoff)

clearBackoff :: TVar (Map PeerId BackoffEntry) -> PeerId -> IO () Source #

Clear backoff for a peer (called on successful connection).

Constants (exported for testing)

initialBackoffSeconds :: NominalDiffTime Source #

Initial backoff duration after first failure: 5 seconds.

maxBackoffSeconds :: NominalDiffTime Source #

Maximum backoff duration: 300 seconds (5 minutes).

staggerDelayUs :: Int Source #

Stagger delay between parallel dial attempts: 250ms (RFC 8305).