Skip to content

Commit 0dc784e

Browse files
committed
Merge remote-tracking branch 'origin/develop' into ucm-vllm-connector-metrics
# Conflicts: # ucm/integration/vllm/hma_connector.py
2 parents 680d3a5 + ea4c9f0 commit 0dc784e

32 files changed

Lines changed: 1141 additions & 461 deletions

File tree

examples/metrics/metrics_configs.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,22 @@ counter:
3838
documentation: "Total shards dispatched by Cache dump (mirror of cache_load_shards_total)"
3939
- name: "cache_dump_backend_shards_total"
4040
documentation: "Shards actually pushed to backend on dump (excludes !handle.Owner() skips in shared-buffer scenario)"
41+
- name: "cache_load_queue_full_total"
42+
documentation: "Number of Cache load submissions rejected because the waiting queue was full"
43+
- name: "cache_dump_queue_full_total"
44+
documentation: "Number of Cache dump submissions rejected because the waiting queue was full"
45+
- name: "cache_backend_load_submit_errors_total"
46+
documentation: "Number of Cache load backend submit failures"
47+
- name: "cache_backend_load_wait_errors_total"
48+
documentation: "Number of Cache load backend wait failures"
49+
- name: "cache_backend_dump_submit_errors_total"
50+
documentation: "Number of Cache dump backend submit failures"
51+
- name: "cache_backend_dump_wait_errors_total"
52+
documentation: "Number of Cache dump backend wait failures"
53+
- name: "cache_h2d_errors_total"
54+
documentation: "Number of Cache host-to-device transfer or sync failures"
55+
- name: "cache_d2h_errors_total"
56+
documentation: "Number of Cache device-to-host transfer, event wait, or sync failures"
4157

4258
# -- Pipeline Cache stage: per-worker real throughput counters ------------
4359
# Accumulated per completed Cache task with size = shardSize * num_shards.
@@ -58,6 +74,14 @@ counter:
5874
documentation: "Total bytes transferred from posix storage to host buffer (load path, summed per completed task)"
5975
- name: "posix_h2s_bytes_total"
6076
documentation: "Total bytes transferred from host buffer to posix storage (dump path, summed per completed task)"
77+
- name: "posix_aio_timeout_total"
78+
documentation: "Number of Posix AIO task or submit timeouts"
79+
- name: "posix_io_timeout_total"
80+
documentation: "Number of Posix synchronous worker task timeouts"
81+
- name: "posix_open_errors_total"
82+
documentation: "Number of Posix open failures"
83+
- name: "posix_io_errors_total"
84+
documentation: "Number of Posix read, write, or AIO completion failures"
6185

6286
# -- Connector layer: per-worker real throughput counters ----------------
6387
# The connector load_speed / save_speed histograms sample per-call speed
@@ -68,6 +92,20 @@ counter:
6892
documentation: "Total bytes loaded through the UCM connector (summed across all start_load_kv calls)"
6993
- name: "save_bytes_total"
7094
documentation: "Total bytes saved through the UCM connector (summed across all wait_for_save calls)"
95+
- name: "connector_lookup_errors_total"
96+
documentation: "Number of connector lookup errors treated as cache misses"
97+
- name: "connector_load_submit_errors_total"
98+
documentation: "Number of connector load submit failures"
99+
- name: "connector_load_wait_errors_total"
100+
documentation: "Number of connector load wait failures"
101+
- name: "connector_load_invalid_requests_total"
102+
documentation: "Number of connector load failure events that invalidated request blocks"
103+
- name: "connector_load_invalid_blocks_total"
104+
documentation: "Number of newly invalidated vLLM block ids caused by connector load failures"
105+
- name: "connector_dump_submit_errors_total"
106+
documentation: "Number of connector dump submit failures"
107+
- name: "connector_dump_wait_errors_total"
108+
documentation: "Number of connector dump wait failures"
71109

72110
# Gauge metrics configuration
73111
gauge:

test/test_ucm_connector_metrics.py

Lines changed: 0 additions & 251 deletions
Original file line numberDiff line numberDiff line change
@@ -370,13 +370,7 @@ def _install_stubs():
370370

371371
_install_stubs()
372372

373-
import ucm.integration.vllm.hma_connector as hma_connector_module
374373
import ucm.integration.vllm.ucm_connector as ucm_connector_module
375-
from ucm.integration.vllm.hma_connector import (
376-
FAWARequestDispatchMeta,
377-
UCMFAWAConnector,
378-
UCMFAWAConnectorMetadata,
379-
)
380374
from ucm.integration.vllm.metrics import UCMConnectorStats, UCMPromMetrics
381375
from ucm.integration.vllm.ucm_connector import (
382376
PendingDumpTask,
@@ -482,38 +476,6 @@ def _reset_fakes():
482476
dispatcher_module._DISPATCHER = None
483477

484478

485-
class FakeHMAStore:
486-
def __init__(self, label, fail_wait=False):
487-
self.label = label
488-
self.fail_wait = fail_wait
489-
self.loaded = []
490-
self.dumped = []
491-
self.waited = []
492-
493-
def load_data(self, keys, shard_indices, ptrs):
494-
task = f"{self.label}-load-{len(keys)}-{len(self.loaded)}"
495-
self.loaded.append((list(keys), list(shard_indices), ptrs, task))
496-
return task
497-
498-
def dump_data(self, keys, shard_indices, ptrs, event_handle):
499-
task = f"{self.label}-dump-{len(keys)}-{len(self.dumped)}"
500-
self.dumped.append((list(keys), list(shard_indices), ptrs, event_handle, task))
501-
return task
502-
503-
def wait(self, task):
504-
self.waited.append(task)
505-
if self.fail_wait:
506-
raise RuntimeError(f"{self.label} wait failed")
507-
508-
509-
class FakeHMADevice:
510-
def __init__(self):
511-
self.destroyed = []
512-
513-
def destroy_event_handle(self, event_handle):
514-
self.destroyed.append(event_handle)
515-
516-
517479
def test_config_definitions_register_enable_list_and_metric_names():
518480
_reset_fakes()
519481
config = _metrics_config()
@@ -1023,219 +985,6 @@ def destroy_event_handle(self, event_handle):
1023985
]
1024986

1025987

1026-
def _fawa_request(load_keys=None, dump_keys=None):
1027-
return FAWARequestDispatchMeta(
1028-
load_keys=list(load_keys or []),
1029-
load_hash_start=0,
1030-
load_hash_end=len(load_keys or []),
1031-
load_vllm_block_ids=([1, 2],),
1032-
dump_keys=list(dump_keys or []),
1033-
dump_hash_start=0,
1034-
dump_hash_end=len(dump_keys or []),
1035-
dump_vllm_block_ids=([1, 2],),
1036-
)
1037-
1038-
1039-
def _new_hma_connector(metadata, fa_store=None, wa_store=None):
1040-
connector = object.__new__(UCMFAWAConnector)
1041-
connector.fa_store = fa_store or FakeHMAStore("FA")
1042-
connector.wa_store = wa_store or FakeHMAStore("WA")
1043-
connector._fawa_store_block_sizes = {"FA": 100, "WA": 20}
1044-
connector._invalid_block_ids = set()
1045-
connector.tp_size = 1
1046-
connector.tp_rank = 0
1047-
connector.wa_dump_block_wise = True
1048-
connector.fa_group_ids = set()
1049-
connector.window_group_ids = set()
1050-
connector.group_metas = []
1051-
connector.tp_dump_tasks = {}
1052-
connector._direct_task_has_load = False
1053-
connector._direct_task_load_duration_ms = 0.0
1054-
connector._direct_task_compute_start_ms = None
1055-
connector.device = FakeHMADevice()
1056-
connector._get_connector_metadata = lambda: metadata
1057-
connector._extract_fa_ptr = lambda *args, **kwargs: [[0], [0]]
1058-
connector._extract_wa_ptr = lambda *args, **kwargs: [[0], [0]]
1059-
return connector
1060-
1061-
1062-
def test_hma_connector_records_load_metrics(monkeypatch):
1063-
_reset_fakes()
1064-
times = iter([1.0, 1.1, 1.11])
1065-
monkeypatch.setattr(
1066-
hma_connector_module.time,
1067-
"perf_counter",
1068-
lambda: next(times),
1069-
)
1070-
metadata = UCMFAWAConnectorMetadata(
1071-
request_meta={"req-1": _fawa_request(load_keys=[b"a", b"b"])}
1072-
)
1073-
connector = _new_hma_connector(metadata)
1074-
1075-
connector.start_load_kv(None)
1076-
1077-
stats = fake_ucmmetrics.updated[-1]
1078-
assert stats["load_requests_num"] == 1
1079-
assert stats["load_blocks_num"] == 3
1080-
assert stats["load_duration"] == pytest.approx(100.0)
1081-
assert stats["load_bytes_total"] == 220
1082-
assert stats["load_speed"] == pytest.approx(220 / 100 / 1024 / 1024)
1083-
assert connector._direct_task_has_load is True
1084-
assert connector._direct_task_load_duration_ms == pytest.approx(100.0)
1085-
assert connector._direct_task_compute_start_ms == pytest.approx(1110.0)
1086-
1087-
1088-
def test_hma_connector_load_metrics_ignore_wait_failure_for_counting(monkeypatch):
1089-
_reset_fakes()
1090-
times = iter([1.0, 1.1, 1.11])
1091-
monkeypatch.setattr(
1092-
hma_connector_module.time,
1093-
"perf_counter",
1094-
lambda: next(times),
1095-
)
1096-
metadata = UCMFAWAConnectorMetadata(
1097-
request_meta={"req-1": _fawa_request(load_keys=[b"a", b"b"])}
1098-
)
1099-
connector = _new_hma_connector(metadata, wa_store=FakeHMAStore("WA", True))
1100-
1101-
connector.start_load_kv(None)
1102-
1103-
stats = fake_ucmmetrics.updated[-1]
1104-
assert stats["load_requests_num"] == 1
1105-
assert stats["load_blocks_num"] == 3
1106-
assert stats["load_bytes_total"] == 220
1107-
assert stats["load_speed"] == pytest.approx(220 / 100 / 1024 / 1024)
1108-
assert connector._invalid_block_ids == {1, 2}
1109-
1110-
1111-
@pytest.mark.parametrize("drain_method", ["get_finished", "preemption"])
1112-
def test_hma_connector_records_mixed_fa_wa_save_completion_metrics(
1113-
monkeypatch, drain_method
1114-
):
1115-
_reset_fakes()
1116-
times = iter([1.0, 1.01, 1.02, 1.10, 1.12, 1.13, 1.15])
1117-
monkeypatch.setattr(
1118-
hma_connector_module.time,
1119-
"perf_counter",
1120-
lambda: next(times),
1121-
)
1122-
metadata = UCMFAWAConnectorMetadata(
1123-
request_meta={"req-1": _fawa_request(dump_keys=[b"a", b"b"])}
1124-
)
1125-
connector = _new_hma_connector(metadata)
1126-
handles = iter([11, 12])
1127-
connector._get_dump_event_handle = lambda: next(handles)
1128-
connector._direct_task_has_load = True
1129-
connector._direct_task_load_duration_ms = 10.0
1130-
connector._direct_task_compute_start_ms = 900.0
1131-
1132-
connector.wait_for_save()
1133-
1134-
assert fake_ucmmetrics.updated == [
1135-
{
1136-
"save_requests_num": 1,
1137-
"save_blocks_num": 4,
1138-
"save_bytes_total": 240,
1139-
}
1140-
]
1141-
if drain_method == "get_finished":
1142-
connector.get_finished({"req-1"})
1143-
else:
1144-
connector.handle_preemptions(
1145-
UCMFAWAConnectorMetadata(preempted_req_ids={"req-1"})
1146-
)
1147-
1148-
assert fake_ucmmetrics.updated[1] == {
1149-
"save_duration": pytest.approx(110.0),
1150-
"save_speed": pytest.approx(200 / 110 / 1024 / 1024),
1151-
"save_completion_wait_duration": pytest.approx(20.0),
1152-
"save_completion_wait_speed": pytest.approx(200 / 20 / 1024 / 1024),
1153-
}
1154-
assert fake_ucmmetrics.updated[2] == {
1155-
"connector_task_load_stage_load_dump_ms": 10.0,
1156-
"connector_task_compute_stage_load_dump_ms": pytest.approx(100.0),
1157-
"connector_task_dump_stage_load_dump_ms": pytest.approx(110.0),
1158-
"connector_task_total_load_dump_ms": pytest.approx(220.0),
1159-
}
1160-
assert fake_ucmmetrics.updated[3] == {
1161-
"save_duration": pytest.approx(130.0),
1162-
"save_speed": pytest.approx(40 / 130 / 1024 / 1024),
1163-
"save_completion_wait_duration": pytest.approx(20.0),
1164-
"save_completion_wait_speed": pytest.approx(40 / 20 / 1024 / 1024),
1165-
}
1166-
assert fake_ucmmetrics.updated[4] == {
1167-
"connector_task_load_stage_load_dump_ms": 10.0,
1168-
"connector_task_compute_stage_load_dump_ms": pytest.approx(100.0),
1169-
"connector_task_dump_stage_load_dump_ms": pytest.approx(130.0),
1170-
"connector_task_total_load_dump_ms": pytest.approx(240.0),
1171-
}
1172-
assert connector.device.destroyed == [11, 12]
1173-
assert connector.tp_dump_tasks == {}
1174-
1175-
1176-
def test_hma_connector_save_metrics_count_only_submitted_requests_without_changing_tp_keys(
1177-
monkeypatch,
1178-
):
1179-
_reset_fakes()
1180-
times = iter([1.0, 1.01, 1.02])
1181-
monkeypatch.setattr(
1182-
hma_connector_module.time,
1183-
"perf_counter",
1184-
lambda: next(times),
1185-
)
1186-
metadata = UCMFAWAConnectorMetadata(
1187-
request_meta={
1188-
"req-no-rank-work": _fawa_request(dump_keys=[b"a"]),
1189-
"req-rank-work": _fawa_request(dump_keys=[b"b", b"c"]),
1190-
}
1191-
)
1192-
connector = _new_hma_connector(metadata)
1193-
connector.tp_size = 2
1194-
connector.tp_rank = 0
1195-
handles = iter([11, 12])
1196-
connector._get_dump_event_handle = lambda: next(handles)
1197-
1198-
connector.wait_for_save()
1199-
1200-
assert list(connector.tp_dump_tasks) == [("req-no-rank-work", "req-rank-work")]
1201-
assert fake_ucmmetrics.updated == [
1202-
{
1203-
"save_requests_num": 1,
1204-
"save_blocks_num": 2,
1205-
"save_bytes_total": 120,
1206-
}
1207-
]
1208-
1209-
1210-
def test_hma_connector_no_dump_records_load_only_breakdown(monkeypatch):
1211-
_reset_fakes()
1212-
times = iter([1.0])
1213-
monkeypatch.setattr(
1214-
hma_connector_module.time,
1215-
"perf_counter",
1216-
lambda: next(times),
1217-
)
1218-
metadata = UCMFAWAConnectorMetadata(
1219-
request_meta={"req-1": _fawa_request(load_keys=[b"a"])}
1220-
)
1221-
connector = _new_hma_connector(metadata)
1222-
connector._direct_task_has_load = True
1223-
connector._direct_task_load_duration_ms = 7.0
1224-
connector._direct_task_compute_start_ms = 900.0
1225-
1226-
connector.wait_for_save()
1227-
1228-
task_stats = fake_ucmmetrics.updated[-1]
1229-
assert task_stats["connector_task_load_stage_load_only_ms"] == 7.0
1230-
assert task_stats["connector_task_compute_stage_load_only_ms"] == pytest.approx(
1231-
100.0
1232-
)
1233-
assert task_stats["connector_task_total_load_only_ms"] == pytest.approx(107.0)
1234-
assert connector._direct_task_has_load is False
1235-
assert connector._direct_task_load_duration_ms == 0.0
1236-
assert connector._direct_task_compute_start_ms is None
1237-
1238-
1239988
def test_multiproc_logger_uses_prefix_and_dispatcher_snapshot(tmp_path):
1240989
_reset_fakes()
1241990
import ucm.observability as observability
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[build-system]
2+
requires = ["setuptools>=64", "wheel", "pybind11>=2.10"]
3+
build-backend = "setuptools.build_meta"
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
from pybind11.setup_helpers import Pybind11Extension, build_ext
2+
from setuptools import setup
3+
4+
ext_modules = [
5+
Pybind11Extension(
6+
"uc_hash_ext",
7+
["uc_hash_ext.cpp"],
8+
cxx_std=17,
9+
extra_compile_args=["-O3", "-march=native"],
10+
extra_link_args=["-Wl,-z,relro,-z,now", "-Wl,-s"],
11+
)
12+
]
13+
14+
setup(
15+
name="uc_hash_ext",
16+
version="0.0.1",
17+
ext_modules=ext_modules,
18+
cmdclass={"build_ext": build_ext},
19+
)

0 commit comments

Comments
 (0)