lmcache-cli 0.4.5.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmcache/__init__.py +84 -0
- lmcache/_version.py +24 -0
- lmcache/cli/__init__.py +1 -0
- lmcache/cli/commands/__init__.py +34 -0
- lmcache/cli/commands/base.py +157 -0
- lmcache/cli/commands/bench/__init__.py +557 -0
- lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
- lmcache/cli/commands/bench/engine_bench/config.py +245 -0
- lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
- lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
- lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
- lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
- lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
- lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
- lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
- lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
- lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
- lmcache/cli/commands/describe.py +310 -0
- lmcache/cli/commands/kvcache.py +133 -0
- lmcache/cli/commands/mock.py +75 -0
- lmcache/cli/commands/ping.py +113 -0
- lmcache/cli/commands/query/__init__.py +155 -0
- lmcache/cli/commands/query/prompt.py +134 -0
- lmcache/cli/commands/query/request.py +357 -0
- lmcache/cli/commands/server.py +99 -0
- lmcache/cli/commands/tool/__init__.py +63 -0
- lmcache/cli/commands/tool/cache_simulator.py +113 -0
- lmcache/cli/commands/trace/__init__.py +505 -0
- lmcache/cli/commands/trace/dispatch.py +249 -0
- lmcache/cli/commands/trace/driver.py +372 -0
- lmcache/cli/commands/trace/stats.py +289 -0
- lmcache/cli/documents/lmcache.txt +11 -0
- lmcache/cli/main.py +42 -0
- lmcache/cli/metrics/__init__.py +29 -0
- lmcache/cli/metrics/formatter.py +171 -0
- lmcache/cli/metrics/handler.py +94 -0
- lmcache/cli/metrics/metrics.py +161 -0
- lmcache/cli/metrics/section.py +77 -0
- lmcache/connections.py +173 -0
- lmcache/integration/__init__.py +2 -0
- lmcache/integration/base_service_factory.py +165 -0
- lmcache/integration/request_telemetry/__init__.py +1 -0
- lmcache/integration/request_telemetry/base.py +51 -0
- lmcache/integration/request_telemetry/factory.py +113 -0
- lmcache/integration/request_telemetry/fastapi.py +109 -0
- lmcache/integration/request_telemetry/noop.py +35 -0
- lmcache/integration/sglang/__init__.py +2 -0
- lmcache/integration/sglang/sglang_adapter.py +326 -0
- lmcache/integration/sglang/utils.py +39 -0
- lmcache/integration/vllm/__init__.py +1 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
- lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
- lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
- lmcache/integration/vllm/utils.py +433 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
- lmcache/integration/vllm/vllm_service_factory.py +339 -0
- lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
- lmcache/logging.py +107 -0
- lmcache/native_storage_ops.pyi +230 -0
- lmcache/non_cuda_equivalents.py +1424 -0
- lmcache/observability.py +1958 -0
- lmcache/storage_backend/serde/__init__.py +1 -0
- lmcache/storage_backend/serde/cachegen_basics.py +210 -0
- lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
- lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
- lmcache/storage_backend/serde/serde.py +75 -0
- lmcache/tools/__init__.py +1 -0
- lmcache/tools/cache_simulator/README.md +392 -0
- lmcache/tools/cache_simulator/__init__.py +1 -0
- lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
- lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
- lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
- lmcache/tools/cache_simulator/lru_cache.py +124 -0
- lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
- lmcache/tools/cache_simulator/simulator.py +795 -0
- lmcache/tools/controller_benchmark/README.md +161 -0
- lmcache/tools/controller_benchmark/__init__.py +1 -0
- lmcache/tools/controller_benchmark/__main__.py +331 -0
- lmcache/tools/controller_benchmark/benchmark.py +660 -0
- lmcache/tools/controller_benchmark/config.py +44 -0
- lmcache/tools/controller_benchmark/constants.py +10 -0
- lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
- lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
- lmcache/tools/controller_benchmark/handlers/base.py +47 -0
- lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
- lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
- lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
- lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
- lmcache/tools/controller_benchmark/handlers/register.py +56 -0
- lmcache/tools/mp_status_viewer/__init__.py +1 -0
- lmcache/tools/mp_status_viewer/__main__.py +95 -0
- lmcache/usage_context.py +417 -0
- lmcache/utils.py +665 -0
- lmcache/v1/__init__.py +2 -0
- lmcache/v1/api_server/__init__.py +2 -0
- lmcache/v1/api_server/__main__.py +537 -0
- lmcache/v1/basic_check.py +112 -0
- lmcache/v1/cache_controller/__init__.py +9 -0
- lmcache/v1/cache_controller/commands/__init__.py +15 -0
- lmcache/v1/cache_controller/commands/base.py +35 -0
- lmcache/v1/cache_controller/commands/full_sync.py +49 -0
- lmcache/v1/cache_controller/config.py +176 -0
- lmcache/v1/cache_controller/controller_manager.py +535 -0
- lmcache/v1/cache_controller/controllers/__init__.py +11 -0
- lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
- lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
- lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
- lmcache/v1/cache_controller/executor.py +463 -0
- lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
- lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
- lmcache/v1/cache_controller/frontend/static/index.html +234 -0
- lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
- lmcache/v1/cache_controller/full_sync_sender.py +475 -0
- lmcache/v1/cache_controller/locks.py +149 -0
- lmcache/v1/cache_controller/message.py +828 -0
- lmcache/v1/cache_controller/observability.py +208 -0
- lmcache/v1/cache_controller/utils.py +679 -0
- lmcache/v1/cache_controller/worker.py +665 -0
- lmcache/v1/cache_engine.py +2058 -0
- lmcache/v1/cache_interface.py +19 -0
- lmcache/v1/check/__init__.py +74 -0
- lmcache/v1/check/check_mode_gen.py +86 -0
- lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
- lmcache/v1/check/check_mode_test_remote.py +155 -0
- lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
- lmcache/v1/check/utils.py +571 -0
- lmcache/v1/compute/__init__.py +2 -0
- lmcache/v1/compute/attention/__init__.py +0 -0
- lmcache/v1/compute/attention/abstract.py +39 -0
- lmcache/v1/compute/attention/flash_attn.py +129 -0
- lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
- lmcache/v1/compute/attention/metadata.py +85 -0
- lmcache/v1/compute/attention/utils.py +14 -0
- lmcache/v1/compute/blend/__init__.py +7 -0
- lmcache/v1/compute/blend/blender.py +168 -0
- lmcache/v1/compute/blend/metadata.py +34 -0
- lmcache/v1/compute/blend/utils.py +63 -0
- lmcache/v1/compute/models/__init__.py +0 -0
- lmcache/v1/compute/models/base.py +141 -0
- lmcache/v1/compute/models/llama.py +9 -0
- lmcache/v1/compute/models/qwen3.py +24 -0
- lmcache/v1/compute/models/utils.py +68 -0
- lmcache/v1/compute/positional_encoding.py +199 -0
- lmcache/v1/config.py +848 -0
- lmcache/v1/config_base.py +848 -0
- lmcache/v1/distributed/api.py +248 -0
- lmcache/v1/distributed/config.py +321 -0
- lmcache/v1/distributed/error.py +64 -0
- lmcache/v1/distributed/eviction.py +192 -0
- lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
- lmcache/v1/distributed/eviction_policy/factory.py +27 -0
- lmcache/v1/distributed/eviction_policy/lru.py +244 -0
- lmcache/v1/distributed/eviction_policy/noop.py +50 -0
- lmcache/v1/distributed/internal_api.py +170 -0
- lmcache/v1/distributed/l1_manager.py +835 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
- lmcache/v1/distributed/l2_adapters/base.py +360 -0
- lmcache/v1/distributed/l2_adapters/config.py +385 -0
- lmcache/v1/distributed/l2_adapters/factory.py +205 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
- lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
- lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
- lmcache/v1/distributed/memory_manager.py +179 -0
- lmcache/v1/distributed/storage_controller.py +39 -0
- lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
- lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
- lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
- lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
- lmcache/v1/distributed/storage_manager.py +532 -0
- lmcache/v1/event_manager.py +145 -0
- lmcache/v1/exceptions/__init__.py +16 -0
- lmcache/v1/gpu_connector/__init__.py +126 -0
- lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
- lmcache/v1/gpu_connector/gpu_ops.py +85 -0
- lmcache/v1/gpu_connector/hpu_connector.py +326 -0
- lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
- lmcache/v1/gpu_connector/utils.py +890 -0
- lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
- lmcache/v1/health_monitor/__init__.py +1 -0
- lmcache/v1/health_monitor/base.py +587 -0
- lmcache/v1/health_monitor/checks/__init__.py +1 -0
- lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
- lmcache/v1/health_monitor/constants.py +36 -0
- lmcache/v1/internal_api_server/__init__.py +0 -0
- lmcache/v1/internal_api_server/api_registry.py +59 -0
- lmcache/v1/internal_api_server/api_server.py +120 -0
- lmcache/v1/internal_api_server/common/__init__.py +1 -0
- lmcache/v1/internal_api_server/common/env_api.py +22 -0
- lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
- lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
- lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
- lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
- lmcache/v1/internal_api_server/common/thread_api.py +63 -0
- lmcache/v1/internal_api_server/controller/__init__.py +1 -0
- lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
- lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
- lmcache/v1/internal_api_server/utils.py +43 -0
- lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
- lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
- lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
- lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
- lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
- lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
- lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
- lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
- lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
- lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
- lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
- lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
- lmcache/v1/kv_layer_groups.py +267 -0
- lmcache/v1/lazy_memory_allocator.py +284 -0
- lmcache/v1/lookup_client/__init__.py +25 -0
- lmcache/v1/lookup_client/abstract_client.py +77 -0
- lmcache/v1/lookup_client/async_lookup_message.py +50 -0
- lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
- lmcache/v1/lookup_client/factory.py +251 -0
- lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
- lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
- lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
- lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
- lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
- lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
- lmcache/v1/lookup_client/record_strategies/base.py +327 -0
- lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
- lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
- lmcache/v1/manager.py +539 -0
- lmcache/v1/memory_management.py +2619 -0
- lmcache/v1/metadata.py +114 -0
- lmcache/v1/mp_observability/AGENTS.override.md +21 -0
- lmcache/v1/mp_observability/README.md +204 -0
- lmcache/v1/mp_observability/config.py +340 -0
- lmcache/v1/mp_observability/event.py +100 -0
- lmcache/v1/mp_observability/event_bus.py +313 -0
- lmcache/v1/mp_observability/otel_init.py +145 -0
- lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
- lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
- lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
- lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
- lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
- lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
- lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
- lmcache/v1/mp_observability/trace/__init__.py +50 -0
- lmcache/v1/mp_observability/trace/codecs.py +255 -0
- lmcache/v1/mp_observability/trace/decorator.py +147 -0
- lmcache/v1/mp_observability/trace/format.py +132 -0
- lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
- lmcache/v1/mp_observability/trace/reader.py +167 -0
- lmcache/v1/mp_observability/trace/recorder.py +300 -0
- lmcache/v1/multiprocess/__init__.py +0 -0
- lmcache/v1/multiprocess/affinity_pool.py +102 -0
- lmcache/v1/multiprocess/blend_server_v2.py +891 -0
- lmcache/v1/multiprocess/config.py +253 -0
- lmcache/v1/multiprocess/custom_types.py +281 -0
- lmcache/v1/multiprocess/futures.py +194 -0
- lmcache/v1/multiprocess/gpu_context.py +511 -0
- lmcache/v1/multiprocess/http_server.py +235 -0
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
- lmcache/v1/multiprocess/mq.py +732 -0
- lmcache/v1/multiprocess/protocol.py +86 -0
- lmcache/v1/multiprocess/protocols/README.md +213 -0
- lmcache/v1/multiprocess/protocols/__init__.py +127 -0
- lmcache/v1/multiprocess/protocols/base.py +89 -0
- lmcache/v1/multiprocess/protocols/blend.py +109 -0
- lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
- lmcache/v1/multiprocess/protocols/controller.py +53 -0
- lmcache/v1/multiprocess/protocols/debug.py +34 -0
- lmcache/v1/multiprocess/protocols/engine.py +146 -0
- lmcache/v1/multiprocess/protocols/observability.py +39 -0
- lmcache/v1/multiprocess/server.py +1134 -0
- lmcache/v1/multiprocess/session.py +190 -0
- lmcache/v1/multiprocess/token_hasher.py +441 -0
- lmcache/v1/offload_server/__init__.py +17 -0
- lmcache/v1/offload_server/abstract_server.py +37 -0
- lmcache/v1/offload_server/message.py +30 -0
- lmcache/v1/offload_server/zmq_server.py +122 -0
- lmcache/v1/periodic_thread.py +579 -0
- lmcache/v1/pin_monitor.py +246 -0
- lmcache/v1/plugin/__init__.py +0 -0
- lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
- lmcache/v1/protocol.py +317 -0
- lmcache/v1/rpc/__init__.py +17 -0
- lmcache/v1/rpc/transport.py +105 -0
- lmcache/v1/rpc/zmq_transport.py +213 -0
- lmcache/v1/rpc_utils.py +165 -0
- lmcache/v1/server/__init__.py +2 -0
- lmcache/v1/server/__main__.py +170 -0
- lmcache/v1/server/storage_backend/__init__.py +21 -0
- lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
- lmcache/v1/server/storage_backend/local_backend.py +75 -0
- lmcache/v1/server/utils.py +21 -0
- lmcache/v1/standalone/__init__.py +1 -0
- lmcache/v1/standalone/__main__.py +583 -0
- lmcache/v1/standalone/manager.py +80 -0
- lmcache/v1/standalone/standalone_service_factory.py +86 -0
- lmcache/v1/storage_backend/__init__.py +313 -0
- lmcache/v1/storage_backend/abstract_backend.py +445 -0
- lmcache/v1/storage_backend/audit_backend.py +233 -0
- lmcache/v1/storage_backend/batched_message_sender.py +222 -0
- lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
- lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
- lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
- lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
- lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
- lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
- lmcache/v1/storage_backend/connector/__init__.py +443 -0
- lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
- lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
- lmcache/v1/storage_backend/connector/base_connector.py +379 -0
- lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
- lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
- lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
- lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
- lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
- lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
- lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
- lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
- lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
- lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
- lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
- lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
- lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
- lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
- lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
- lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
- lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
- lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
- lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
- lmcache/v1/storage_backend/gds_backend.py +1199 -0
- lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
- lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
- lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
- lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
- lmcache/v1/storage_backend/local_disk_backend.py +656 -0
- lmcache/v1/storage_backend/maru_backend.py +734 -0
- lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
- lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
- lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
- lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
- lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
- lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
- lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
- lmcache/v1/storage_backend/p2p_backend.py +788 -0
- lmcache/v1/storage_backend/path_sharder.py +117 -0
- lmcache/v1/storage_backend/pd_backend.py +646 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
- lmcache/v1/storage_backend/remote_backend.py +624 -0
- lmcache/v1/storage_backend/resp_client.py +227 -0
- lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
- lmcache/v1/storage_backend/storage_manager.py +1352 -0
- lmcache/v1/system_detection.py +110 -0
- lmcache/v1/token_database.py +551 -0
- lmcache/v1/transfer_channel/__init__.py +83 -0
- lmcache/v1/transfer_channel/abstract.py +285 -0
- lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
- lmcache/v1/transfer_channel/nixl_channel.py +639 -0
- lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
- lmcache/v1/transfer_channel/transfer_utils.py +63 -0
- lmcache/v1/utils/__init__.py +1 -0
- lmcache/v1/utils/bloom_filter.py +109 -0
- lmcache/v1/utils/cache_utils.py +125 -0
- lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
- lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
- lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
- lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
- lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
- lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Distributed multi-tier storage manager for MP mode
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
# Standard
|
|
7
|
+
from contextlib import contextmanager
|
|
8
|
+
from typing import Iterator, Literal
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
# First Party
|
|
12
|
+
from lmcache.logging import init_logger
|
|
13
|
+
from lmcache.v1.distributed.api import (
|
|
14
|
+
MemoryLayoutDesc,
|
|
15
|
+
ObjectKey,
|
|
16
|
+
PrefetchHandle,
|
|
17
|
+
)
|
|
18
|
+
from lmcache.v1.distributed.config import StorageManagerConfig
|
|
19
|
+
from lmcache.v1.distributed.error import L1Error, strerror
|
|
20
|
+
from lmcache.v1.distributed.l1_manager import L1Manager
|
|
21
|
+
from lmcache.v1.distributed.l2_adapters import create_l2_adapter
|
|
22
|
+
from lmcache.v1.distributed.l2_adapters.base import L2AdapterInterface
|
|
23
|
+
from lmcache.v1.distributed.storage_controllers import (
|
|
24
|
+
L1EvictionController,
|
|
25
|
+
L2AdapterEvictionState,
|
|
26
|
+
L2EvictionController,
|
|
27
|
+
PrefetchController,
|
|
28
|
+
StoreController,
|
|
29
|
+
)
|
|
30
|
+
from lmcache.v1.distributed.storage_controllers.prefetch_policy import (
|
|
31
|
+
create_prefetch_policy,
|
|
32
|
+
)
|
|
33
|
+
from lmcache.v1.distributed.storage_controllers.store_policy import (
|
|
34
|
+
AdapterDescriptor,
|
|
35
|
+
create_store_policy,
|
|
36
|
+
)
|
|
37
|
+
from lmcache.v1.memory_management import MemoryObj
|
|
38
|
+
from lmcache.v1.mp_observability.event import Event, EventType
|
|
39
|
+
from lmcache.v1.mp_observability.event_bus import get_event_bus
|
|
40
|
+
from lmcache.v1.mp_observability.trace.decorator import (
|
|
41
|
+
enable_tracing,
|
|
42
|
+
is_tracing_enabled,
|
|
43
|
+
publish_call_event,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
logger = init_logger(__name__)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class StorageManager:
|
|
50
|
+
def __init__(self, config: StorageManagerConfig):
|
|
51
|
+
self._l1_manager = L1Manager(config.l1_manager_config)
|
|
52
|
+
self._event_bus = get_event_bus()
|
|
53
|
+
|
|
54
|
+
# L1 eviction controller
|
|
55
|
+
self._eviction_controller = L1EvictionController(
|
|
56
|
+
l1_manager=self._l1_manager,
|
|
57
|
+
eviction_config=config.eviction_config,
|
|
58
|
+
)
|
|
59
|
+
self._eviction_controller.start()
|
|
60
|
+
|
|
61
|
+
# L2 adapters and store controller
|
|
62
|
+
l1_memory_desc = self._l1_manager.get_l1_memory_desc()
|
|
63
|
+
self._l2_adapters: list[L2AdapterInterface] = [
|
|
64
|
+
create_l2_adapter(ac, l1_memory_desc)
|
|
65
|
+
for ac in config.l2_adapter_config.adapters
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# Unified L2 eviction controller for all adapters with eviction config
|
|
69
|
+
l2_eviction_states = [
|
|
70
|
+
L2AdapterEvictionState(
|
|
71
|
+
adapter=adapter,
|
|
72
|
+
eviction_config=ac.eviction_config,
|
|
73
|
+
)
|
|
74
|
+
for adapter, ac in zip(
|
|
75
|
+
self._l2_adapters, config.l2_adapter_config.adapters, strict=False
|
|
76
|
+
)
|
|
77
|
+
if ac.eviction_config is not None
|
|
78
|
+
]
|
|
79
|
+
self._l2_eviction_controller = L2EvictionController(l2_eviction_states)
|
|
80
|
+
self._l2_eviction_controller.start()
|
|
81
|
+
|
|
82
|
+
adapter_descriptors = [
|
|
83
|
+
AdapterDescriptor(index=i, config=ac)
|
|
84
|
+
for i, ac in enumerate(config.l2_adapter_config.adapters)
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
self._store_controller = StoreController(
|
|
88
|
+
l1_manager=self._l1_manager,
|
|
89
|
+
l2_adapters=self._l2_adapters,
|
|
90
|
+
adapter_descriptors=adapter_descriptors,
|
|
91
|
+
policy=create_store_policy(config.store_policy),
|
|
92
|
+
)
|
|
93
|
+
self._store_controller.start()
|
|
94
|
+
|
|
95
|
+
# Prefetch controller
|
|
96
|
+
self._prefetch_controller = PrefetchController(
|
|
97
|
+
l1_manager=self._l1_manager,
|
|
98
|
+
l2_adapters=self._l2_adapters,
|
|
99
|
+
adapter_descriptors=adapter_descriptors,
|
|
100
|
+
policy=create_prefetch_policy(config.prefetch_policy),
|
|
101
|
+
max_in_flight=config.prefetch_max_in_flight,
|
|
102
|
+
)
|
|
103
|
+
self._prefetch_controller.start()
|
|
104
|
+
|
|
105
|
+
# External APIs for serving engine integration code to call
|
|
106
|
+
@enable_tracing()
|
|
107
|
+
def reserve_write(
|
|
108
|
+
self,
|
|
109
|
+
keys: list[ObjectKey],
|
|
110
|
+
layout_desc: MemoryLayoutDesc,
|
|
111
|
+
mode: Literal["new", "update", "all"],
|
|
112
|
+
) -> dict[ObjectKey, MemoryObj]:
|
|
113
|
+
"""
|
|
114
|
+
Reserve the object for writing into the storage manager.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
keys (list[ObjectKey]): List of object keys to reserve for writing.
|
|
118
|
+
layout_desc (MemoryLayoutDesc): Description of the memory layout
|
|
119
|
+
for the objects to be reserved.
|
|
120
|
+
mode (Literal["new", "update", "all"]): Reservation mode.
|
|
121
|
+
- "new": Reserve only new objects that do not exist.
|
|
122
|
+
- "update": Reserve only existing objects for update.
|
|
123
|
+
- "all": Reserve all writable objects regardless of existence.
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
dict[ObjectKey, MemoryObj]: A dictionary mapping object keys to their
|
|
127
|
+
reserved memory objects. Note that not all requested keys could be
|
|
128
|
+
reserved (e.g., out of memory or write conflict)
|
|
129
|
+
"""
|
|
130
|
+
reserve_result = self._l1_manager.reserve_write(
|
|
131
|
+
keys=keys,
|
|
132
|
+
is_temporary=[False] * len(keys),
|
|
133
|
+
layout_desc=layout_desc,
|
|
134
|
+
mode=mode,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
result = {k: m for k, (e, m) in reserve_result.items() if m is not None}
|
|
138
|
+
successful_keys = list(result.keys())
|
|
139
|
+
failed_keys = [k for k, (e, m) in reserve_result.items() if m is None]
|
|
140
|
+
self._event_bus.publish(
|
|
141
|
+
Event(
|
|
142
|
+
event_type=EventType.SM_WRITE_RESERVED,
|
|
143
|
+
metadata={
|
|
144
|
+
"succeeded_keys": successful_keys,
|
|
145
|
+
"failed_keys": failed_keys,
|
|
146
|
+
},
|
|
147
|
+
)
|
|
148
|
+
)
|
|
149
|
+
return result
|
|
150
|
+
|
|
151
|
+
@enable_tracing()
|
|
152
|
+
def finish_write(
|
|
153
|
+
self,
|
|
154
|
+
keys: list[ObjectKey],
|
|
155
|
+
) -> None:
|
|
156
|
+
"""
|
|
157
|
+
Finish writing the objects into the storage manager.
|
|
158
|
+
|
|
159
|
+
Args:
|
|
160
|
+
keys (list[ObjectKey]): List of object keys that have been written.
|
|
161
|
+
"""
|
|
162
|
+
finish_result = self._l1_manager.finish_write(keys)
|
|
163
|
+
successful_keys = [k for k, e in finish_result.items() if e == L1Error.SUCCESS]
|
|
164
|
+
failed_keys = [k for k, e in finish_result.items() if e != L1Error.SUCCESS]
|
|
165
|
+
self._event_bus.publish(
|
|
166
|
+
Event(
|
|
167
|
+
event_type=EventType.SM_WRITE_FINISHED,
|
|
168
|
+
metadata={
|
|
169
|
+
"succeeded_keys": successful_keys,
|
|
170
|
+
"failed_keys": failed_keys,
|
|
171
|
+
},
|
|
172
|
+
)
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# TODO: global key states update
|
|
176
|
+
|
|
177
|
+
@contextmanager
|
|
178
|
+
def read_prefetched_results(
|
|
179
|
+
self,
|
|
180
|
+
keys: list[ObjectKey],
|
|
181
|
+
) -> Iterator[list[MemoryObj] | None]:
|
|
182
|
+
"""
|
|
183
|
+
Read the memory objects from L1 storage that has been prefetched beforehand.
|
|
184
|
+
Yielding an optional list of memory objects corresponding to the requested
|
|
185
|
+
keys. If any the object is not found in L1, None is yielded.
|
|
186
|
+
|
|
187
|
+
Args:
|
|
188
|
+
keys (list[ObjectKey]): List of object keys to reserve for reading.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Iterator[list[MemoryObj] | None]: An iterator yielding an optional list of
|
|
192
|
+
memory objects corresponding to the requested keys.
|
|
193
|
+
|
|
194
|
+
Note:
|
|
195
|
+
If any object is not found in L1 storage, None is yielded. In this case,
|
|
196
|
+
this function will release release the read lock of all successfully read
|
|
197
|
+
memory objects when exiting the context.
|
|
198
|
+
|
|
199
|
+
If the caller raised exception during the processing of the yielded memory
|
|
200
|
+
objects, this function will ensure that the read locks will be decreased.
|
|
201
|
+
"""
|
|
202
|
+
# Manual TRACE_CALL emission for the context manager. The
|
|
203
|
+
# ``@enable_tracing`` decorator cannot wrap a ``@contextmanager``
|
|
204
|
+
# generator function (it would publish the call to the wrapper
|
|
205
|
+
# rather than to ``__enter__``). Emit enter/exit events
|
|
206
|
+
# directly, gated on the tracing flag for zero overhead when
|
|
207
|
+
# disabled.
|
|
208
|
+
if is_tracing_enabled():
|
|
209
|
+
publish_call_event(
|
|
210
|
+
"lmcache.v1.distributed.storage_manager."
|
|
211
|
+
"StorageManager.read_prefetched_results.__enter__",
|
|
212
|
+
{"keys": keys},
|
|
213
|
+
)
|
|
214
|
+
read_results = self._l1_manager.unsafe_read(keys)
|
|
215
|
+
good_keys: list[ObjectKey] = []
|
|
216
|
+
good_objs: list[MemoryObj] = []
|
|
217
|
+
bad_keys: list[ObjectKey] = []
|
|
218
|
+
all_good = True
|
|
219
|
+
for k, (e, o) in read_results.items():
|
|
220
|
+
if o is None:
|
|
221
|
+
logger.error(
|
|
222
|
+
"Failed to read prefetched object %s from L1 storage: %s",
|
|
223
|
+
k,
|
|
224
|
+
strerror(e),
|
|
225
|
+
)
|
|
226
|
+
bad_keys.append(k)
|
|
227
|
+
all_good = False
|
|
228
|
+
continue
|
|
229
|
+
|
|
230
|
+
good_keys.append(k)
|
|
231
|
+
good_objs.append(o)
|
|
232
|
+
|
|
233
|
+
successfully_yielded = False
|
|
234
|
+
|
|
235
|
+
try:
|
|
236
|
+
yield good_objs if all_good else None
|
|
237
|
+
successfully_yielded = True
|
|
238
|
+
except Exception:
|
|
239
|
+
logger.exception(
|
|
240
|
+
"Exception occurred while processing read prefetched results",
|
|
241
|
+
)
|
|
242
|
+
raise
|
|
243
|
+
finally:
|
|
244
|
+
# Decrease the read lock for all successfully read memory objects
|
|
245
|
+
# if None is yielded or exception occurs during caller's processing
|
|
246
|
+
if not all_good or not successfully_yielded:
|
|
247
|
+
self._l1_manager.finish_read(good_keys)
|
|
248
|
+
self._event_bus.publish(
|
|
249
|
+
Event(
|
|
250
|
+
event_type=EventType.SM_READ_PREFETCHED_FINISHED,
|
|
251
|
+
metadata={
|
|
252
|
+
"succeeded_keys": good_keys,
|
|
253
|
+
"failed_keys": bad_keys,
|
|
254
|
+
},
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
if is_tracing_enabled():
|
|
258
|
+
publish_call_event(
|
|
259
|
+
"lmcache.v1.distributed.storage_manager."
|
|
260
|
+
"StorageManager.read_prefetched_results.__exit__",
|
|
261
|
+
{"keys": keys},
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
@enable_tracing()
|
|
265
|
+
def finish_read_prefetched(
|
|
266
|
+
self,
|
|
267
|
+
keys: list[ObjectKey],
|
|
268
|
+
extra_count: int = 0,
|
|
269
|
+
) -> None:
|
|
270
|
+
"""Finish reading prefetched objects.
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
keys: Object keys that have been read.
|
|
274
|
+
extra_count: Extra read locks to release per key
|
|
275
|
+
(on top of the default 1).
|
|
276
|
+
"""
|
|
277
|
+
finish_result = self._l1_manager.finish_read(keys, extra_count=extra_count)
|
|
278
|
+
successful_keys = [k for k, e in finish_result.items() if e == L1Error.SUCCESS]
|
|
279
|
+
failed_keys = [k for k, e in finish_result.items() if e != L1Error.SUCCESS]
|
|
280
|
+
self._event_bus.publish(
|
|
281
|
+
Event(
|
|
282
|
+
event_type=EventType.SM_READ_PREFETCHED_FINISHED,
|
|
283
|
+
metadata={
|
|
284
|
+
"succeeded_keys": successful_keys,
|
|
285
|
+
"failed_keys": failed_keys,
|
|
286
|
+
},
|
|
287
|
+
)
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
@enable_tracing()
|
|
291
|
+
def submit_prefetch_task(
|
|
292
|
+
self,
|
|
293
|
+
keys: list[ObjectKey],
|
|
294
|
+
layout_desc: MemoryLayoutDesc,
|
|
295
|
+
extra_count: int = 0,
|
|
296
|
+
external_request_id: str = "",
|
|
297
|
+
) -> PrefetchHandle:
|
|
298
|
+
"""Prefetch objects into L1 asynchronously.
|
|
299
|
+
|
|
300
|
+
Args:
|
|
301
|
+
keys: Object keys to prefetch.
|
|
302
|
+
layout_desc: Memory layout description.
|
|
303
|
+
extra_count: Extra workers (on top of the default
|
|
304
|
+
1) that will independently retrieve the same
|
|
305
|
+
key. Total locks = 1 + extra_count.
|
|
306
|
+
external_request_id: Request ID from the caller
|
|
307
|
+
for end-to-end log tracing.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
PrefetchHandle to track the task.
|
|
311
|
+
"""
|
|
312
|
+
# NOTE: now we only have L1, so the prefetch is essentially checking how many
|
|
313
|
+
# objects are already in L1, and adding read locks to them.
|
|
314
|
+
|
|
315
|
+
l1_read_result = self._l1_manager.reserve_read(keys, extra_count=extra_count)
|
|
316
|
+
hit_count = 0
|
|
317
|
+
for key in keys:
|
|
318
|
+
entry = l1_read_result.get(key, None)
|
|
319
|
+
if entry is None:
|
|
320
|
+
break
|
|
321
|
+
|
|
322
|
+
err, obj = entry
|
|
323
|
+
if err != L1Error.SUCCESS:
|
|
324
|
+
break
|
|
325
|
+
|
|
326
|
+
hit_count += 1
|
|
327
|
+
|
|
328
|
+
# NOTE: For L1, there will be cases that "object in the middle" is not found.
|
|
329
|
+
# In this case, we need to `finish_read` for the latter objects so that
|
|
330
|
+
# there won't be dangling read locks.
|
|
331
|
+
skipped_keys = []
|
|
332
|
+
for key in keys[hit_count:]:
|
|
333
|
+
if key in l1_read_result and l1_read_result[key][1] is not None:
|
|
334
|
+
# this key is actually reserved, need to release the read lock
|
|
335
|
+
skipped_keys.append(key)
|
|
336
|
+
|
|
337
|
+
if skipped_keys:
|
|
338
|
+
self._l1_manager.finish_read(skipped_keys, extra_count=extra_count)
|
|
339
|
+
|
|
340
|
+
self._event_bus.publish(
|
|
341
|
+
Event(
|
|
342
|
+
event_type=EventType.SM_READ_PREFETCHED,
|
|
343
|
+
metadata={
|
|
344
|
+
"succeeded_keys": keys[:hit_count],
|
|
345
|
+
"failed_keys": keys[hit_count:],
|
|
346
|
+
},
|
|
347
|
+
)
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
# Submit remaining keys to L2 prefetch controller
|
|
351
|
+
remaining_keys = keys[hit_count:]
|
|
352
|
+
prefetch_request_id = -1
|
|
353
|
+
if remaining_keys and self._l2_adapters:
|
|
354
|
+
prefetch_request_id = self._prefetch_controller.submit_prefetch_request(
|
|
355
|
+
remaining_keys,
|
|
356
|
+
layout_desc,
|
|
357
|
+
extra_count=extra_count,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
submit_time = time.monotonic()
|
|
361
|
+
logger.debug(
|
|
362
|
+
"Prefetch request submitted: "
|
|
363
|
+
"%d total keys, %d L1 prefix hits, "
|
|
364
|
+
"%d remaining for L2 "
|
|
365
|
+
"(external_request_id=%s, "
|
|
366
|
+
"prefetch_request_id=%d)",
|
|
367
|
+
len(keys),
|
|
368
|
+
hit_count,
|
|
369
|
+
len(remaining_keys),
|
|
370
|
+
external_request_id,
|
|
371
|
+
prefetch_request_id,
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
return PrefetchHandle(
|
|
375
|
+
prefetch_request_id=prefetch_request_id,
|
|
376
|
+
external_request_id=external_request_id,
|
|
377
|
+
l1_prefix_hit_count=hit_count,
|
|
378
|
+
total_requested_keys=len(keys),
|
|
379
|
+
submit_time=submit_time,
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
def query_prefetch_lookup_hits(
|
|
383
|
+
self,
|
|
384
|
+
handle: PrefetchHandle,
|
|
385
|
+
) -> int | None:
|
|
386
|
+
"""
|
|
387
|
+
Query the number of prefix hit chunks for a prefetch task before
|
|
388
|
+
the L2 prefetching is done.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
handle (PrefetchHandle): The handle of the lookup task.
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
the number of prefix hit chunks if the lookup is done, None if
|
|
395
|
+
it's still in progress, or the prefetch task is already done.
|
|
396
|
+
|
|
397
|
+
Note:
|
|
398
|
+
This function is designed for the scenario where the caller wants
|
|
399
|
+
to check the L1 prefix hits as soon as possible without waiting for
|
|
400
|
+
the whole prefetch task to be done.
|
|
401
|
+
When the prefetch task is already done and the prefetch task result
|
|
402
|
+
has already been queried by `query_prefetch_status`, this function
|
|
403
|
+
will return None forever for the same prefetch handle.
|
|
404
|
+
Therefore, it's the caller’s responsibility to make sure not calling
|
|
405
|
+
this function after the prefetch task is done.
|
|
406
|
+
"""
|
|
407
|
+
if handle.prefetch_request_id == -1:
|
|
408
|
+
# No L2 request, the prefix hit count is final
|
|
409
|
+
return handle.l1_prefix_hit_count
|
|
410
|
+
|
|
411
|
+
# Have L2 request, need to check the status from prefetch controller
|
|
412
|
+
l2_r = self._prefetch_controller.query_lookup_result(handle.prefetch_request_id)
|
|
413
|
+
|
|
414
|
+
if l2_r is None:
|
|
415
|
+
# L2 prefetch is still in progress or it's already done and
|
|
416
|
+
# the result has been consumed by `query_prefetch_status`
|
|
417
|
+
return None
|
|
418
|
+
|
|
419
|
+
# L2 lookup is done, return the total prefix hit count (L1 + L2)
|
|
420
|
+
return handle.l1_prefix_hit_count + l2_r
|
|
421
|
+
|
|
422
|
+
def query_prefetch_status(
|
|
423
|
+
self,
|
|
424
|
+
handle: PrefetchHandle,
|
|
425
|
+
) -> int | None:
|
|
426
|
+
"""
|
|
427
|
+
Query the status of the prefetch task.
|
|
428
|
+
|
|
429
|
+
Args:
|
|
430
|
+
handle (PrefetchHandle): The handle of the prefetch task.
|
|
431
|
+
|
|
432
|
+
Returns:
|
|
433
|
+
the number of prefix hit chunks if the prefetch is done, None if
|
|
434
|
+
it's still in progress.
|
|
435
|
+
"""
|
|
436
|
+
l2_result: int = 0
|
|
437
|
+
|
|
438
|
+
# Have L2 request, need to check the result from prefetch controller
|
|
439
|
+
if handle.prefetch_request_id != -1:
|
|
440
|
+
l2_r = self._prefetch_controller.query_prefetch_result(
|
|
441
|
+
handle.prefetch_request_id
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
if l2_r is None:
|
|
445
|
+
return None
|
|
446
|
+
l2_result = l2_r # Just to make linter happy
|
|
447
|
+
|
|
448
|
+
total_hits = handle.l1_prefix_hit_count + l2_result
|
|
449
|
+
elapsed_ms = (time.monotonic() - handle.submit_time) * 1000
|
|
450
|
+
|
|
451
|
+
if total_hits > 0:
|
|
452
|
+
logger.info(
|
|
453
|
+
"Prefetch request completed (L1+L2): "
|
|
454
|
+
"%d/%d prefix hits (%d L1, %d L2) "
|
|
455
|
+
"in %.1f ms "
|
|
456
|
+
"(external_request_id=%s, "
|
|
457
|
+
"prefetch_request_id=%d)",
|
|
458
|
+
total_hits,
|
|
459
|
+
handle.total_requested_keys,
|
|
460
|
+
handle.l1_prefix_hit_count,
|
|
461
|
+
l2_result,
|
|
462
|
+
elapsed_ms,
|
|
463
|
+
handle.external_request_id,
|
|
464
|
+
handle.prefetch_request_id,
|
|
465
|
+
)
|
|
466
|
+
return total_hits
|
|
467
|
+
|
|
468
|
+
def touch_l1_keys(self, keys: list[ObjectKey]):
|
|
469
|
+
"""
|
|
470
|
+
Touch the keys in L1 storage, marking the keys
|
|
471
|
+
as accessed(retrieved or stored).
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
keys (list[ObjectKey]): List of object keys to touch.
|
|
475
|
+
"""
|
|
476
|
+
self._l1_manager.touch_keys(keys)
|
|
477
|
+
|
|
478
|
+
def clear(self, force: bool = False):
|
|
479
|
+
"""
|
|
480
|
+
Clear data in the storage manager.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
force: If True, clear ALL objects including locked ones.
|
|
484
|
+
This may corrupt in-flight store/prefetch operations.
|
|
485
|
+
If False (default), only clear unlocked objects, keeping
|
|
486
|
+
write-locked and read-locked objects intact.
|
|
487
|
+
"""
|
|
488
|
+
self._l1_manager.clear(force=force)
|
|
489
|
+
|
|
490
|
+
def close(self):
|
|
491
|
+
"""
|
|
492
|
+
Close the storage manager and release all resources.
|
|
493
|
+
"""
|
|
494
|
+
self._prefetch_controller.stop()
|
|
495
|
+
self._store_controller.stop()
|
|
496
|
+
self._eviction_controller.stop()
|
|
497
|
+
self._l2_eviction_controller.stop()
|
|
498
|
+
|
|
499
|
+
for adapter in self._l2_adapters:
|
|
500
|
+
adapter.close()
|
|
501
|
+
|
|
502
|
+
self._l1_manager.close()
|
|
503
|
+
|
|
504
|
+
def report_status(self) -> dict:
|
|
505
|
+
"""Return a status dict aggregating all sub-component statuses."""
|
|
506
|
+
l1 = self._l1_manager.report_status()
|
|
507
|
+
store = self._store_controller.report_status()
|
|
508
|
+
prefetch = self._prefetch_controller.report_status()
|
|
509
|
+
l1_eviction = self._eviction_controller.report_status()
|
|
510
|
+
l2_eviction = self._l2_eviction_controller.report_status()
|
|
511
|
+
adapters = [a.report_status() for a in self._l2_adapters]
|
|
512
|
+
children = [l1, store, prefetch, l1_eviction, l2_eviction] + adapters
|
|
513
|
+
return {
|
|
514
|
+
"is_healthy": all(c["is_healthy"] for c in children),
|
|
515
|
+
"l1_manager": l1,
|
|
516
|
+
"store_controller": store,
|
|
517
|
+
"prefetch_controller": prefetch,
|
|
518
|
+
"l1_eviction_controller": l1_eviction,
|
|
519
|
+
"l2_eviction_controller": l2_eviction,
|
|
520
|
+
"l2_adapters": adapters,
|
|
521
|
+
"num_l2_adapters": len(self._l2_adapters),
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
# Functions for debugging and testing
|
|
525
|
+
def memcheck(self) -> bool:
|
|
526
|
+
"""
|
|
527
|
+
Perform memory check for all storage tiers.
|
|
528
|
+
|
|
529
|
+
Returns:
|
|
530
|
+
True if memory is consistent, False otherwise.
|
|
531
|
+
"""
|
|
532
|
+
return self._l1_manager.memcheck()
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from enum import Enum, auto
|
|
4
|
+
import asyncio
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class EventType(Enum):
|
|
9
|
+
LOADING = auto()
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class EventStatus(Enum):
|
|
13
|
+
ONGOING = auto()
|
|
14
|
+
DONE = auto()
|
|
15
|
+
NOT_FOUND = auto()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class EventManager:
|
|
19
|
+
"""
|
|
20
|
+
A thread-safe event manager to manage asynchronous events.
|
|
21
|
+
Each event is identified by its type and a unique id.
|
|
22
|
+
Events are organized by status for efficient counting.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(self) -> None:
|
|
26
|
+
# Guard by lock
|
|
27
|
+
# Structure: events[event_type][event_status][event_id] = future
|
|
28
|
+
self.events: dict[EventType, dict[EventStatus, dict[str, asyncio.Future]]] = {
|
|
29
|
+
et: {es: {} for es in EventStatus} for et in EventType
|
|
30
|
+
}
|
|
31
|
+
self.lock = threading.Lock()
|
|
32
|
+
|
|
33
|
+
def add_event(
|
|
34
|
+
self,
|
|
35
|
+
event_type: EventType,
|
|
36
|
+
event_id: str,
|
|
37
|
+
future: asyncio.Future,
|
|
38
|
+
) -> None:
|
|
39
|
+
"""
|
|
40
|
+
Add an event with the given type and id.
|
|
41
|
+
"""
|
|
42
|
+
with self.lock:
|
|
43
|
+
status_dict = self.events.get(event_type, None)
|
|
44
|
+
assert status_dict is not None, (
|
|
45
|
+
f"Invalid event type {event_type} in EventManager."
|
|
46
|
+
)
|
|
47
|
+
status_dict[EventStatus.ONGOING][event_id] = future
|
|
48
|
+
|
|
49
|
+
def pop_event(
|
|
50
|
+
self,
|
|
51
|
+
event_type: EventType,
|
|
52
|
+
event_id: str,
|
|
53
|
+
) -> asyncio.Future:
|
|
54
|
+
"""
|
|
55
|
+
Pop and return the event with the given type and id.
|
|
56
|
+
"""
|
|
57
|
+
with self.lock:
|
|
58
|
+
status_dict = self.events.get(event_type, None)
|
|
59
|
+
assert status_dict is not None, (
|
|
60
|
+
f"Invalid event type {event_type} in EventManager."
|
|
61
|
+
)
|
|
62
|
+
done_events = status_dict[EventStatus.DONE]
|
|
63
|
+
assert event_id in done_events, (
|
|
64
|
+
f"Event {event_id} of type {event_type} is not done or not found."
|
|
65
|
+
)
|
|
66
|
+
return done_events.pop(event_id)
|
|
67
|
+
|
|
68
|
+
def update_event_status(
|
|
69
|
+
self,
|
|
70
|
+
event_type: EventType,
|
|
71
|
+
event_id: str,
|
|
72
|
+
status: EventStatus,
|
|
73
|
+
) -> None:
|
|
74
|
+
"""
|
|
75
|
+
Update the status of the event with the given type and id.
|
|
76
|
+
"""
|
|
77
|
+
with self.lock:
|
|
78
|
+
status_dict = self.events.get(event_type, None)
|
|
79
|
+
assert status_dict is not None, (
|
|
80
|
+
f"Invalid event type {event_type} in EventManager."
|
|
81
|
+
)
|
|
82
|
+
# Find the event in any status dict
|
|
83
|
+
event = None
|
|
84
|
+
for s in EventStatus:
|
|
85
|
+
if event_id in status_dict[s]:
|
|
86
|
+
event = status_dict[s].pop(event_id)
|
|
87
|
+
break
|
|
88
|
+
|
|
89
|
+
if event is None:
|
|
90
|
+
raise KeyError(f"Event {event_id} of type {event_type} not found.")
|
|
91
|
+
|
|
92
|
+
# Move to new status dict
|
|
93
|
+
status_dict[status][event_id] = event
|
|
94
|
+
|
|
95
|
+
def get_event_status(
|
|
96
|
+
self,
|
|
97
|
+
event_type: EventType,
|
|
98
|
+
event_id: str,
|
|
99
|
+
) -> EventStatus:
|
|
100
|
+
"""
|
|
101
|
+
Get the status of the event with the given type and id.
|
|
102
|
+
"""
|
|
103
|
+
with self.lock:
|
|
104
|
+
status_dict = self.events.get(event_type, None)
|
|
105
|
+
assert status_dict is not None, (
|
|
106
|
+
f"Invalid event type {event_type} in EventManager."
|
|
107
|
+
)
|
|
108
|
+
for status in EventStatus:
|
|
109
|
+
if event_id in status_dict[status]:
|
|
110
|
+
return status
|
|
111
|
+
return EventStatus.NOT_FOUND
|
|
112
|
+
|
|
113
|
+
def get_events_count_by_status(
|
|
114
|
+
self,
|
|
115
|
+
event_type: EventType,
|
|
116
|
+
status: EventStatus,
|
|
117
|
+
) -> int:
|
|
118
|
+
"""
|
|
119
|
+
Get the count of events for the given event type and status.
|
|
120
|
+
This is a lightweight O(1) operation using dict length.
|
|
121
|
+
"""
|
|
122
|
+
with self.lock:
|
|
123
|
+
status_dict = self.events.get(event_type, None)
|
|
124
|
+
if status_dict is None:
|
|
125
|
+
return 0
|
|
126
|
+
return len(status_dict[status])
|
|
127
|
+
|
|
128
|
+
def get_event_future(
|
|
129
|
+
self,
|
|
130
|
+
event_type: EventType,
|
|
131
|
+
event_id: str,
|
|
132
|
+
) -> asyncio.Future:
|
|
133
|
+
"""
|
|
134
|
+
Pop and return the event with the given type and id.
|
|
135
|
+
"""
|
|
136
|
+
with self.lock:
|
|
137
|
+
status_dict = self.events.get(event_type, None)
|
|
138
|
+
assert status_dict is not None, (
|
|
139
|
+
f"Invalid event type {event_type} in EventManager."
|
|
140
|
+
)
|
|
141
|
+
done_events = status_dict[EventStatus.DONE]
|
|
142
|
+
assert event_id in done_events, (
|
|
143
|
+
f"Event {event_id} of type {event_type} is not done or not found."
|
|
144
|
+
)
|
|
145
|
+
return done_events[event_id]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Custom exceptions for LMCache v1.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class IrrecoverableException(Exception):
|
|
8
|
+
"""
|
|
9
|
+
Exception raised when an irrecoverable error occurs.
|
|
10
|
+
|
|
11
|
+
This exception indicates that the system has encountered an error
|
|
12
|
+
that cannot be recovered from, and the health monitor should stop
|
|
13
|
+
checking and propagate the error up.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
pass
|