Connecting Nodes¶
The Pool¶
Pool is the central orchestrator in Voltricx. It manages all Lavalink node connections, handles failover, and performs load balancing automatically.
import voltricx
# Single node
await voltricx.Pool.connect(
client=bot,
nodes=[
voltricx.NodeConfig(identifier="Main", uri="http://localhost:2333", password="pass")
]
)
# Multiple nodes with regions
await voltricx.Pool.connect(
client=bot,
nodes=[
voltricx.NodeConfig(identifier="US", uri="http://us.node:2333", password="pass", region="us"),
voltricx.NodeConfig(identifier="EU", uri="http://eu.node:2333", password="pass", region="eu")
]
)
After connection, Pool prints a status report to the console:
[Lavalink] Node connection report (3/3 connected)
[CONNECTED] US http://us.node:2333 region=us
[CONNECTED] EU http://eu.node:2333 region=eu
[CONNECTED] Asia http://asia.node:2333 region=asia
Node Status¶
A Node can be in one of three states:
| Status | Enum | Meaning |
|---|---|---|
| Connected | NodeStatus.connected | Node is online and accepting requests |
| Connecting | NodeStatus.connecting | Handshake in progress |
| Disconnected | NodeStatus.disconnected | Node is offline or unreachable |
from voltricx import NodeStatus
node = voltricx.Pool.get_node("Main")
if node.status == NodeStatus.connected:
print("Node is healthy!")
Node Selection & Load Balancing¶
Pool.get_node() uses a penalty scoring system to select the healthiest node:
# Automatic selection (lowest penalty score)
node = voltricx.Pool.get_node()
# By identifier
node = voltricx.Pool.get_node("EU")
# By region (closest healthy node in that region)
node = voltricx.Pool.get_node(region="eu")
Penalty Formula¶
The penalty score is calculated from three components:
| Component | Formula |
|---|---|
| CPU | (1.05 ^ (100 × system_load) × 10) − 10 |
| Frame deficit | (1.03 ^ (500 × deficit / 3000) × 300) − 300 |
| Frame nulled | (1.03 ^ (500 × nulled / 3000) × 300) − 300) × 2 |
| Players | playing_count × 1.5 |
A node that is disconnected gets a penalty of 9e30 (effectively infinite), so it's never selected.
Node Events¶
Listen to node lifecycle events with discord.py's event system:
@bot.event
async def on_voltricx_node_ready(node: voltricx.Node):
print(f"✅ Node {node.identifier} connected")
@bot.event
async def on_voltricx_node_disconnected(node: voltricx.Node):
print(f"❌ Node {node.identifier} disconnected")
Automatic Failover¶
When a node disconnects, Voltricx automatically migrates all its players to the next healthiest node:
@bot.event
async def on_voltricx_failover(
old_node: voltricx.Node,
new_node: voltricx.Node,
players: list[voltricx.Player]
):
print(f"Migrated {len(players)} players from {old_node.identifier} → {new_node.identifier}")
for player in players:
if player.home:
await player.home.send(
f"⚠️ Server failover — migrated to `{new_node.identifier}`"
)
The failover logic:
- Waits up to 5 seconds for a healthy node to appear
- Selects the node with the lowest penalty score
- Migrates each player individually, accounting for region preferences
- Resumes playback from the current track position
Fetching Node Info¶
node = voltricx.Pool.get_node("Main")
# Fetch Lavalink server info
info = await node.fetch_info()
print(info.version.semver) # e.g. "4.0.8"
print(info.source_managers) # ["youtube", "soundcloud", ...]
# Fetch stats
stats = await node.fetch_stats()
# Fetch version string
version = await node.fetch_version()
Route Planner¶
The Lavalink RoutePlanner is used with rotating IP addresses for YouTube API compliance:
# Check status
status = await node.fetch_routeplanner_status()
if status:
print(f"Type: {status.cls}")
# Unmark a specific failing IP
await node.free_address("1.2.3.4")
# Reset all failing IPs
await node.free_all_addresses()
Listing All Nodes¶
all_nodes = voltricx.Pool.nodes()
for identifier, node in all_nodes.items():
print(f"{identifier}: {node.status.value} ({len(node.players)} players)")
Shutting Down¶
Always call Pool.close() before your bot exits to cleanly destroy all players and close WebSocket connections: