Files
2026-07-13 12:24:33 +08:00

223 lines
7.2 KiB
Python

# SPDX-License-Identifier: Apache-2.0
"""
Tests for the report_status() interface across the StorageManager subtree.
"""
# Standard
# Third Party
import pytest
import torch
# First Party
from lmcache.v1.distributed.api import MemoryLayoutDesc, ObjectKey
from lmcache.v1.distributed.config import (
EvictionConfig,
L1ManagerConfig,
L1MemoryManagerConfig,
StorageManagerConfig,
)
from lmcache.v1.distributed.l2_adapters.config import (
L2AdaptersConfig,
)
from lmcache.v1.distributed.l2_adapters.mock_l2_adapter import (
MockL2AdapterConfig,
)
try:
# First Party
from lmcache.v1.distributed.storage_manager import StorageManager
except ImportError:
pytest.skip(
"Skipping because StorageManager cannot be imported", allow_module_level=True
)
# Skip all tests in this module if CUDA is not available
pytestmark = pytest.mark.skipif(
not torch.cuda.is_available(), reason="CUDA is not available"
)
def should_use_lazy_alloc() -> bool:
return torch.cuda.is_available()
# =============================================================================
# Fixtures
# =============================================================================
@pytest.fixture
def basic_memory_config():
return L1MemoryManagerConfig(
size_in_bytes=128 * 1024 * 1024,
use_lazy=should_use_lazy_alloc(),
init_size_in_bytes=64 * 1024 * 1024,
align_bytes=0x1000,
)
@pytest.fixture
def basic_l1_config(basic_memory_config):
return L1ManagerConfig(
memory_config=basic_memory_config,
write_ttl_seconds=600,
read_ttl_seconds=300,
)
@pytest.fixture
def basic_layout():
return MemoryLayoutDesc(
shapes=[torch.Size([100, 2, 512])],
dtypes=[torch.bfloat16],
)
@pytest.fixture
def storage_manager_no_l2(basic_l1_config):
"""StorageManager without L2 adapters."""
config = StorageManagerConfig(
l1_manager_config=basic_l1_config,
eviction_config=EvictionConfig(eviction_policy="LRU"),
)
sm = StorageManager(config)
yield sm
sm.close()
@pytest.fixture
def storage_manager_with_l2(basic_l1_config):
"""StorageManager with one mock L2 adapter."""
config = StorageManagerConfig(
l1_manager_config=basic_l1_config,
eviction_config=EvictionConfig(eviction_policy="LRU"),
l2_adapter_config=L2AdaptersConfig(
adapters=[
MockL2AdapterConfig(
max_size_gb=0.1,
mock_bandwidth_gb=10.0,
),
]
),
)
sm = StorageManager(config)
yield sm
sm.close()
def make_object_key(chunk_hash: int, model_name: str = "test_model", kv_rank: int = 0):
hash_bytes = ObjectKey.IntHash2Bytes(chunk_hash)
return ObjectKey(chunk_hash=hash_bytes, model_name=model_name, kv_rank=kv_rank)
# =============================================================================
# Tests
# =============================================================================
class TestStorageManagerReportStatus:
"""Tests for StorageManager.report_status() shape and values."""
def test_report_status_shape_no_l2(self, storage_manager_no_l2):
status = storage_manager_no_l2.report_status()
# Top-level keys
assert "is_healthy" in status
assert "l1_manager" in status
assert "store_controller" in status
assert "prefetch_controller" in status
assert "l1_eviction_controller" in status
assert "l2_adapters" in status
assert "num_l2_adapters" in status
assert status["is_healthy"] is True
assert status["num_l2_adapters"] == 0
assert status["l2_adapters"] == []
def test_report_status_shape_with_l2(self, storage_manager_with_l2):
status = storage_manager_with_l2.report_status()
assert status["is_healthy"] is True
assert status["num_l2_adapters"] == 1
assert len(status["l2_adapters"]) == 1
adapter_status = status["l2_adapters"][0]
assert adapter_status["is_healthy"] is True
assert adapter_status["type"] == "MockL2Adapter"
assert "stored_object_count" in adapter_status
assert "max_capacity_bytes" in adapter_status
def test_l1_manager_status_shape(self, storage_manager_no_l2):
l1 = storage_manager_no_l2.report_status()["l1_manager"]
assert l1["is_healthy"] is True
assert l1["total_object_count"] == 0
assert l1["write_locked_count"] == 0
assert l1["read_locked_count"] == 0
assert l1["temporary_count"] == 0
assert l1["memory_used_bytes"] == 0
assert l1["memory_total_bytes"] > 0
assert l1["memory_usage_ratio"] == 0.0
assert "write_ttl_seconds" in l1
assert "read_ttl_seconds" in l1
def test_store_controller_status_shape(self, storage_manager_no_l2):
sc = storage_manager_no_l2.report_status()["store_controller"]
assert sc["is_healthy"] is True
assert sc["thread_alive"] is True
assert "pending_keys_count" in sc
assert "in_flight_task_count" in sc
assert "num_l2_adapters" in sc
def test_prefetch_controller_status_shape(self, storage_manager_no_l2):
pc = storage_manager_no_l2.report_status()["prefetch_controller"]
assert pc["is_healthy"] is True
assert pc["thread_alive"] is True
assert "max_in_flight" in pc
assert "submission_queue_size" in pc
assert "pending_queue_size" in pc
assert "in_flight_request_count" in pc
assert "lookup_phase_count" in pc
assert "load_phase_count" in pc
assert "completed_results_count" in pc
assert "num_l2_adapters" in pc
def test_eviction_controller_status_shape(self, storage_manager_no_l2):
ec = storage_manager_no_l2.report_status()["l1_eviction_controller"]
assert ec["is_healthy"] is True
assert ec["thread_alive"] is True
assert "eviction_policy" in ec
assert "trigger_watermark" in ec
assert "eviction_ratio" in ec
def test_l1_status_reflects_writes(self, storage_manager_no_l2, basic_layout):
"""After writing objects, L1 status should reflect them."""
keys = [make_object_key(i) for i in range(3)]
reserved = storage_manager_no_l2.reserve_write(keys, basic_layout, "new")
assert len(reserved) == 3
l1 = storage_manager_no_l2.report_status()["l1_manager"]
assert l1["total_object_count"] == 3
assert l1["write_locked_count"] == 3
assert l1["memory_used_bytes"] > 0
# Finish writes
storage_manager_no_l2.finish_write(keys)
l1 = storage_manager_no_l2.report_status()["l1_manager"]
assert l1["write_locked_count"] == 0
def test_health_propagation(self, storage_manager_no_l2):
"""Top-level is_healthy should be True when all children are healthy."""
status = storage_manager_no_l2.report_status()
assert status["is_healthy"] is True
# Verify all child components are healthy
assert status["l1_manager"]["is_healthy"] is True
assert status["store_controller"]["is_healthy"] is True
assert status["prefetch_controller"]["is_healthy"] is True
assert status["l1_eviction_controller"]["is_healthy"] is True