Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion libp2p/relay/circuit_v2/dcutr.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,9 @@ async def _have_direct_connection(self, peer_id: ID) -> bool:

# Handle both single connection and list of connections
connections: list[INetConn] = (
[conn_or_conns] if not isinstance(conn_or_conns, list) else conn_or_conns
list(conn_or_conns)
if not isinstance(conn_or_conns, list)
else conn_or_conns
)

# Check if any connection is direct (not relayed)
Expand Down
33 changes: 30 additions & 3 deletions libp2p/relay/circuit_v2/transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ def __init__(
stream_timeout=config.timeouts.discovery_stream_timeout,
peer_protocol_timeout=config.timeouts.peer_protocol_timeout,
)
self.relay_counter = 0 # for round robin load balancing
# A lock to protect ``relay_counter`` from concurrent access since
# ``_select_relay`` may be invoked from multiple tasks concurrently.
self._relay_counter_lock = trio.Lock()

async def dial(
self,
Expand Down Expand Up @@ -221,9 +225,32 @@ async def _select_relay(self, peer_info: PeerInfo) -> ID | None:
# Get a relay from the list of discovered relays
relays = self.discovery.get_relays()
if relays:
# TODO: Implement more sophisticated relay selection
# For now, just return the first available relay
return relays[0]
# Prioritize relays with active reservations
relays_with_reservations = []
other_relays = []

for relay_id in relays:
relay_info = self.discovery.get_relay_info(relay_id)
if relay_info and relay_info.has_reservation:
relays_with_reservations.append(relay_id)
else:
other_relays.append(relay_id)

# Return first available relay with reservation, or fallback to others
candidate_list = (
relays_with_reservations
if relays_with_reservations
else other_relays
)
if candidate_list:
# Round-robin load-balancing protected by a lock to avoid race
# conditions when multiple coroutines attempt relay selection
# simultaneously.
async with self._relay_counter_lock:
index = self.relay_counter % len(candidate_list)
relay_peer_id = candidate_list[index]
self.relay_counter += 1
return relay_peer_id

# Wait and try discovery
await trio.sleep(1)
Expand Down
1 change: 1 addition & 0 deletions newsfragments/972.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Implemented round-robin load balancing for CircuitV2 relay selection, prioritizing relays with active reservations for more reliable and evenly distributed relay usage.
Loading
Loading