Node & Pool¶
The Pool is the heart of RevvLink — it manages all Node connections, handles reconnections, and routes players to the best available node.
Creating Nodes¶
import revvlink
node = revvlink.Node(
uri="http://localhost:2333",
password="youshallnotpass",
# Optional:
identifier="MAIN", # Human-readable name for this node
heartbeat=30.0, # WebSocket ping interval in seconds
retries=3, # Reconnect attempts before giving up
region="us-east", # Geographic region (for load balancing)
inactive_player_timeout=300, # Seconds before auto-disconnecting idle players
)
Connecting the Pool¶
Always connect in setup_hook (not on_ready) to ensure nodes are ready before commands:
class Bot(commands.Bot):
async def setup_hook(self) -> None:
nodes = [
revvlink.Node(uri="http://us.lavalink.example", password="secret", region="us-east"),
revvlink.Node(uri="http://eu.lavalink.example", password="secret", region="eu-west"),
revvlink.Node(uri="http://ap.lavalink.example", password="secret", region="ap-south"),
]
await revvlink.Pool.connect(nodes=nodes, client=self, cache_capacity=200)
Node Selection¶
When a player is created, the Pool automatically selects the best node using a penalty-based algorithm. Penalties are computed from:
- CPU usage — higher load = higher penalty
- Memory pressure — high memory usage increases cost
- Player count — nodes with more players receive higher penalties
- Network latency — higher ping = higher penalty
- Frame deficit/nulls — indicates node audio processing issues
The node with the lowest total penalty is chosen.
Manual Node Assignment¶
# Force a specific node for a player
node = revvlink.Pool.get_node(identifier="EU-WEST")
player = await channel.connect(cls=revvlink.Player)
await player.move_to(node)
Pool Methods¶
# Get the Pool instance
pool = revvlink.Pool
# Get a specific node by identifier
node = revvlink.Pool.get_node(identifier="MAIN")
# Get the best node for a guild
node = revvlink.Pool.get_best_node()
# Get all connected nodes
nodes = revvlink.Pool.nodes # dict[str, Node]
# Close all connections
await revvlink.Pool.close()
Node Statistics¶
node = revvlink.Pool.get_node()
stats = node.stats
if stats:
print(f"Players: {stats.playing_players}")
print(f"CPU System: {stats.cpu.system_load:.1%}")
print(f"CPU Lavalink:{stats.cpu.lavalink_load:.1%}")
print(f"Memory Used: {stats.memory.used / 1024**2:.1f} MB")
print(f"Uptime: {stats.uptime // 1000}s")
Node Events¶
async def on_revvlink_node_ready(payload: revvlink.NodeReadyEventPayload) -> None:
print(f"Node {payload.node.identifier} ready | Resumed: {payload.resumed}")
async def on_revvlink_node_disconnected(payload: revvlink.NodeDisconnectedEventPayload) -> None:
print(f"Node {payload.node.identifier} disconnected | Code: {payload.code}")
Failover¶
When a node disconnects, RevvLink automatically:
- Marks the node as unavailable
- Moves all active players to the next best node
- Resumes playback from where it stopped (if the new node supports session resume)
- Continues attempting to reconnect in the background
Session Resume
RevvLink sends a resumeKey to Lavalink on connection. If the connection drops and reconnects within the timeout window, the session (including all players) is resumed seamlessly.
Cache¶
The cache_capacity parameter enables an LFU (Least Frequently Used) track cache:
Cached tracks avoid re-fetching metadata from Lavalink on repeated plays. Set to None to disable.