macula_peer_connector (macula v0.14.3)

View Source

Peer Connector - Establishes direct QUIC connections to remote peers (v0.8.0+).

This module enables peer-to-peer communication by establishing outbound QUIC connections to arbitrary peers. Used by DHT for STORE/FIND_VALUE message propagation and by RPC/PubSub for direct delivery.

Overview

Pattern: Connection-pooled utility module with NAT-aware fallback - Uses macula_peer_connection_pool for connection reuse - Falls back to NAT-aware routing (hole punch, relay) on failure - Fire-and-forget message sending

Connection Strategy (v0.12.0+)

1. Try pooled connection (fastest, cached) 2. Try direct QUIC connection (new connection) 3. Fall back to NAT-aware routing via macula_nat_connector: a. Direct connection (if NAT allows) b. Hole punch (coordinated NAT traversal) c. Relay via gateway (guaranteed fallback)

Usage

Used internally by: - macula_pubsub_dht: Direct pub/sub delivery to discovered subscribers - macula_service_registry: DHT STORE propagation to k=20 nodes - Future: Multi-hop RPC routing

   %% Send a DHT STORE message to a peer
   Endpoint = <<"192.168.1.100:9443">>,
   Message = #{
       key => <<"service.calculator.add">>,
       value => <<"192.168.1.50:9443">>,
       ttl => 300
   },
   ok = macula_peer_connector:send_message(Endpoint, dht_store, Message).

Performance Characteristics

v0.8.0: Fire-and-forget pattern (now legacy fallback) - Creates new connection per message - Simple but inefficient for high-frequency messaging

v0.10.0: Connection pooling (current) - Reuses existing connections via macula_peer_connection_pool - 1.5-2x latency improvement for repeated messaging

v0.12.0: NAT-aware routing (current) - Automatic fallback to hole punch and relay - Works across all NAT types

Summary

Functions

Send a message to a remote peer (fire-and-forget). Uses connection pool for efficiency, falls back to direct connection.

Send a message to a remote peer with custom timeout.

Send a message and wait for a reply (request-response pattern). This is used for messages like NAT_PROBE that expect a reply. Returns {ok, ReplyMessage} on success, {error, timeout} if no reply received.

Send a message using NAT-aware routing (hole punch, relay fallback). Use this when sending to peers that may be behind NAT. LocalNodeId is required for hole punch coordination.

Send a message using NAT-aware routing with options. Options: - endpoint: Target endpoint (if known, skips DHT lookup) - relay_endpoint: Specific relay to use - skip_hole_punch: true to skip hole punch attempts

Functions

send_message(Endpoint, MessageType, Message)

-spec send_message(binary(), atom(), map()) -> ok | {error, term()}.

Send a message to a remote peer (fire-and-forget). Uses connection pool for efficiency, falls back to direct connection.

send_message(Endpoint, MessageType, Message, Timeout)

-spec send_message(binary(), atom(), map(), timeout()) -> ok | {error, term()}.

Send a message to a remote peer with custom timeout.

send_message_and_wait(Endpoint, MessageType, Message, Timeout)

-spec send_message_and_wait(binary(), atom(), map(), timeout()) ->
                               {ok, {atom(), map()}} | {error, term()}.

Send a message and wait for a reply (request-response pattern). This is used for messages like NAT_PROBE that expect a reply. Returns {ok, ReplyMessage} on success, {error, timeout} if no reply received.

send_message_nat_aware(LocalNodeId, TargetNodeId, MessageType, Message)

-spec send_message_nat_aware(binary(), binary(), atom(), map()) -> ok | {error, term()}.

Send a message using NAT-aware routing (hole punch, relay fallback). Use this when sending to peers that may be behind NAT. LocalNodeId is required for hole punch coordination.

send_message_nat_aware(LocalNodeId, TargetNodeId, MessageType, Message, Opts)

-spec send_message_nat_aware(binary(), binary(), atom(), map(), map()) -> ok | {error, term()}.

Send a message using NAT-aware routing with options. Options: - endpoint: Target endpoint (if known, skips DHT lookup) - relay_endpoint: Specific relay to use - skip_hole_punch: true to skip hole punch attempts