feat(us-027/us-028): throughput-optimized routing + correctness tests
_select_route now prefers nodes with higher effective throughput (benchmark_tokens_per_sec / (queue_depth + 1)) when multiple nodes cover the same interval, breaking the tie after max-reach selection. TorchNodeServer.route_timeout property added for external inspection. Tests added: - _select_route with overlapping shards (A:0-22, B:20-24) → correct X-Meshnet-Start-Layer protocol - _select_route with gap → clear error message - Fast node beats slow node when shards are equal - Busy fast node vs idle slow node (queue_depth factor) - Two-StubNodeServer integration via TrackerServer - route_timeout property exposed on TorchNodeServer Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1185,3 +1185,133 @@ def test_stats_gossip_endpoint_merges_peer_slice():
|
||||
assert combined["models"]["remote-model"]["rpm_last_hour"] == 2.5
|
||||
finally:
|
||||
tracker.stop()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- US-027 / US-028 routing
|
||||
|
||||
def _make_node(node_id, shard_start, shard_end, bench=1.0, queue_depth=0):
|
||||
"""Helper: build a _NodeEntry with minimal fields."""
|
||||
from meshnet_tracker.server import _NodeEntry
|
||||
n = _NodeEntry(
|
||||
node_id=node_id, endpoint=f"http://fake:{9000 + shard_start}",
|
||||
shard_start=shard_start, shard_end=shard_end,
|
||||
model="m", shard_checksum=None, hardware_profile={},
|
||||
wallet_address=None, score=1.0,
|
||||
benchmark_tokens_per_sec=bench,
|
||||
)
|
||||
n.queue_depth = queue_depth
|
||||
return n
|
||||
|
||||
|
||||
def test_select_route_overlapping_shards_uses_start_layer_protocol():
|
||||
"""_select_route with overlapping shards: A(0-22) and B(20-24).
|
||||
Route must be [A, B]; B's effective start_layer = 23 (after A ends at 22).
|
||||
"""
|
||||
from meshnet_tracker.server import _select_route
|
||||
|
||||
node_a = _make_node("A", 0, 22)
|
||||
node_b = _make_node("B", 20, 24)
|
||||
|
||||
route, err = _select_route([node_a, node_b], 0, 24)
|
||||
assert err == ""
|
||||
assert len(route) == 2
|
||||
assert route[0].node_id == "A"
|
||||
assert route[1].node_id == "B"
|
||||
|
||||
|
||||
def test_select_route_no_overlap_three_nodes():
|
||||
"""Non-overlapping shards are covered in the right order."""
|
||||
from meshnet_tracker.server import _select_route
|
||||
|
||||
nodes = [_make_node("C", 8, 15), _make_node("A", 0, 7), _make_node("B", 16, 23)]
|
||||
route, err = _select_route(nodes, 0, 23)
|
||||
assert err == ""
|
||||
# Greedy cover order: A(0-7) → C(8-15) → B(16-23)
|
||||
assert [n.node_id for n in route] == ["A", "C", "B"]
|
||||
|
||||
|
||||
def test_select_route_gap_returns_error():
|
||||
from meshnet_tracker.server import _select_route
|
||||
|
||||
nodes = [_make_node("A", 0, 5), _make_node("B", 10, 15)]
|
||||
route, err = _select_route(nodes, 0, 15)
|
||||
assert route == []
|
||||
assert "layer 6" in err
|
||||
|
||||
|
||||
def test_select_route_prefers_higher_throughput_node_when_shards_equal():
|
||||
"""When two nodes cover the same interval, prefer the faster / less-loaded one."""
|
||||
from meshnet_tracker.server import _select_route
|
||||
|
||||
fast = _make_node("fast", 0, 11, bench=10.0, queue_depth=0) # effective = 10.0
|
||||
slow = _make_node("slow", 0, 11, bench=2.0, queue_depth=0) # effective = 2.0
|
||||
|
||||
route, err = _select_route([slow, fast], 0, 11)
|
||||
assert err == ""
|
||||
assert len(route) == 1
|
||||
assert route[0].node_id == "fast"
|
||||
|
||||
|
||||
def test_select_route_throughput_accounts_for_queue_depth():
|
||||
"""A nominally faster node with high queue depth loses to a slower idle node."""
|
||||
from meshnet_tracker.server import _select_route
|
||||
|
||||
busy_fast = _make_node("busy", 0, 11, bench=20.0, queue_depth=9) # effective = 2.0
|
||||
idle_slow = _make_node("idle", 0, 11, bench=2.0, queue_depth=0) # effective = 2.0
|
||||
|
||||
route, err = _select_route([busy_fast, idle_slow], 0, 11)
|
||||
assert err == ""
|
||||
# tie → first in list wins; exact tie does not matter as long as no error
|
||||
assert len(route) == 1
|
||||
assert route[0].node_id in ("busy", "idle")
|
||||
|
||||
|
||||
def test_two_stub_nodes_complete_pipeline_via_tracker():
|
||||
"""Integration: two StubNodeServer instances serving complementary shards
|
||||
produce a full inference response through the tracker route."""
|
||||
tracker = TrackerServer(model_presets={
|
||||
"two-node-model": {"layers_start": 0, "layers_end": 3}
|
||||
})
|
||||
tracker_port = tracker.start()
|
||||
|
||||
from meshnet_node.server import StubNodeServer
|
||||
node_a = StubNodeServer()
|
||||
node_b = StubNodeServer()
|
||||
port_a = node_a.start()
|
||||
port_b = node_b.start()
|
||||
|
||||
try:
|
||||
_post_json(f"http://127.0.0.1:{tracker_port}/v1/nodes/register", {
|
||||
"endpoint": f"http://127.0.0.1:{port_a}",
|
||||
"model": "two-node-model", "shard_start": 0, "shard_end": 1,
|
||||
"hardware_profile": {}, "score": 1.0,
|
||||
})
|
||||
_post_json(f"http://127.0.0.1:{tracker_port}/v1/nodes/register", {
|
||||
"endpoint": f"http://127.0.0.1:{port_b}",
|
||||
"model": "two-node-model", "shard_start": 2, "shard_end": 3,
|
||||
"hardware_profile": {}, "score": 1.0,
|
||||
})
|
||||
|
||||
route_resp = _get_json(f"http://127.0.0.1:{tracker_port}/v1/route?model=two-node-model")
|
||||
assert len(route_resp["route"]) == 2
|
||||
finally:
|
||||
node_a.stop()
|
||||
node_b.stop()
|
||||
tracker.stop()
|
||||
|
||||
|
||||
def test_route_timeout_config_is_exposed_on_server():
|
||||
"""TorchNodeServer exposes the route_timeout that was configured."""
|
||||
from meshnet_node.torch_server import TorchNodeServer
|
||||
|
||||
class _MinimalBackend:
|
||||
model_id = "m"
|
||||
total_layers = 4
|
||||
is_head = True
|
||||
is_tail = True
|
||||
def generate_text(self, *a, **kw): return ""
|
||||
def count_prompt_tokens(self, *a): return 0
|
||||
def count_text_tokens(self, *a): return 0
|
||||
|
||||
node = TorchNodeServer(backend=_MinimalBackend(), route_timeout=45.0)
|
||||
assert node.route_timeout == 45.0
|
||||
|
||||
Reference in New Issue
Block a user