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,665 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from typing import TYPE_CHECKING, Optional
|
|
4
|
+
import asyncio
|
|
5
|
+
import threading
|
|
6
|
+
|
|
7
|
+
# Third Party
|
|
8
|
+
import msgspec
|
|
9
|
+
import zmq
|
|
10
|
+
import zmq.asyncio
|
|
11
|
+
|
|
12
|
+
# First Party
|
|
13
|
+
from lmcache.logging import init_logger
|
|
14
|
+
from lmcache.v1.cache_controller.full_sync_sender import FullSyncSender
|
|
15
|
+
from lmcache.v1.cache_controller.message import (
|
|
16
|
+
BatchedP2PLookupMsg,
|
|
17
|
+
BatchedP2PLookupRetMsg,
|
|
18
|
+
ClearWorkerMsg,
|
|
19
|
+
ClearWorkerRetMsg,
|
|
20
|
+
CompressWorkerMsg,
|
|
21
|
+
CompressWorkerRetMsg,
|
|
22
|
+
DecompressWorkerMsg,
|
|
23
|
+
DecompressWorkerRetMsg,
|
|
24
|
+
DeRegisterMsg,
|
|
25
|
+
ErrorMsg,
|
|
26
|
+
FullSyncStartMsg,
|
|
27
|
+
FullSyncStartRetMsg,
|
|
28
|
+
FullSyncStatusMsg,
|
|
29
|
+
FullSyncStatusRetMsg,
|
|
30
|
+
HealthWorkerMsg,
|
|
31
|
+
HealthWorkerRetMsg,
|
|
32
|
+
HeartbeatMsg,
|
|
33
|
+
HeartbeatRetMsg,
|
|
34
|
+
MoveWorkerMsg,
|
|
35
|
+
MoveWorkerRetMsg,
|
|
36
|
+
Msg,
|
|
37
|
+
PinWorkerMsg,
|
|
38
|
+
PinWorkerRetMsg,
|
|
39
|
+
RegisterMsg,
|
|
40
|
+
RegisterRetMsg,
|
|
41
|
+
WorkerMsg,
|
|
42
|
+
WorkerReqMsg,
|
|
43
|
+
WorkerReqRetMsg,
|
|
44
|
+
)
|
|
45
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
46
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
47
|
+
from lmcache.v1.rpc_utils import (
|
|
48
|
+
DEFAULT_SOCKET_RECV_TIMEOUT_MS,
|
|
49
|
+
DEFAULT_SOCKET_SEND_TIMEOUT_MS,
|
|
50
|
+
close_zmq_socket,
|
|
51
|
+
get_ip,
|
|
52
|
+
get_zmq_context,
|
|
53
|
+
get_zmq_socket,
|
|
54
|
+
get_zmq_socket_with_timeout,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if TYPE_CHECKING:
|
|
58
|
+
# First Party
|
|
59
|
+
from lmcache.v1.cache_engine import LMCacheEngine
|
|
60
|
+
|
|
61
|
+
logger = init_logger(__name__)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class LMCacheWorker:
|
|
65
|
+
"""
|
|
66
|
+
LMCache Worker class to handle the execution of cache operations.
|
|
67
|
+
This class is responsible for receiving requests from the executor and
|
|
68
|
+
executing the corresponding operations on the LMCache engine.
|
|
69
|
+
Each worker is associated with a specific LMCache instance and a worker id.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self,
|
|
74
|
+
config: LMCacheEngineConfig,
|
|
75
|
+
metadata: LMCacheMetadata,
|
|
76
|
+
lmcache_engine: "LMCacheEngine",
|
|
77
|
+
):
|
|
78
|
+
# TODO (Jiayi): "instance_id" might not be needed anymore.
|
|
79
|
+
# Please consider removing it.
|
|
80
|
+
self.config = config
|
|
81
|
+
self.lmcache_instance_id = config.lmcache_instance_id
|
|
82
|
+
if self.lmcache_instance_id is None:
|
|
83
|
+
raise ValueError(
|
|
84
|
+
"lmcache_instance_id is required when enable_controller=True"
|
|
85
|
+
)
|
|
86
|
+
self.lmcache_engine = lmcache_engine
|
|
87
|
+
self.worker_id = metadata.worker_id
|
|
88
|
+
|
|
89
|
+
self.context = get_zmq_context()
|
|
90
|
+
|
|
91
|
+
# Load timeout configurations from extra_config (in milliseconds)
|
|
92
|
+
self.socket_recv_timeout_ms = config.get_extra_config_value(
|
|
93
|
+
"worker_socket_recv_timeout_ms", DEFAULT_SOCKET_RECV_TIMEOUT_MS
|
|
94
|
+
)
|
|
95
|
+
self.socket_send_timeout_ms = config.get_extra_config_value(
|
|
96
|
+
"worker_socket_send_timeout_ms", DEFAULT_SOCKET_SEND_TIMEOUT_MS
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
if config.controller_pull_url is None:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
"controller_pull_url is required when enable_controller=True"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
controller_pull_url = config.controller_pull_url
|
|
105
|
+
self.push_socket = get_zmq_socket(
|
|
106
|
+
self.context,
|
|
107
|
+
controller_pull_url,
|
|
108
|
+
protocol="tcp",
|
|
109
|
+
role=zmq.PUSH, # type: ignore[attr-defined]
|
|
110
|
+
bind_or_connect="connect",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
if config.controller_reply_url is not None:
|
|
114
|
+
self.controller_rep_url = config.controller_reply_url
|
|
115
|
+
self._create_req_socket()
|
|
116
|
+
|
|
117
|
+
# Heartbeat socket will be created dynamically after register
|
|
118
|
+
# based on heartbeat_url returned from controller
|
|
119
|
+
self.heartbeat_socket: Optional[zmq.asyncio.Socket] = None
|
|
120
|
+
self.controller_heartbeat_url: Optional[str] = None
|
|
121
|
+
|
|
122
|
+
# metadata.world_size comes from vLLM's parallel_config.world_size.
|
|
123
|
+
# For MLA models, vLLM divides this by tp_size (e.g. TP=8 PP=1 on
|
|
124
|
+
# 8 GPUs → world_size=1), so it may be much smaller than the total
|
|
125
|
+
# GPU count. For non-MLA models it equals TP × PP.
|
|
126
|
+
#
|
|
127
|
+
# get_lmcache_worker_ids() decides which workers run an LMCache
|
|
128
|
+
# instance: [0] for MLA (only one worker needed since KV caches
|
|
129
|
+
# are not TP-sharded), or range(world_size) for non-MLA.
|
|
130
|
+
#
|
|
131
|
+
# We use >= because extra ports are harmless — port selection
|
|
132
|
+
# indexes by worker_id or lmcache_worker_ids position, so
|
|
133
|
+
# trailing entries are never bound to sockets.
|
|
134
|
+
lmcache_worker_ids = config.get_lmcache_worker_ids(
|
|
135
|
+
metadata.use_mla, metadata.world_size
|
|
136
|
+
)
|
|
137
|
+
if not lmcache_worker_ids:
|
|
138
|
+
# start lmcache worker on all ranks;
|
|
139
|
+
# need at least one port per rank (world_size)
|
|
140
|
+
if len(config.lmcache_worker_ports) < metadata.world_size:
|
|
141
|
+
raise ValueError(
|
|
142
|
+
f"lmcache_worker_ports must have at least {metadata.world_size} "
|
|
143
|
+
f"port(s) (world_size), got {len(config.lmcache_worker_ports)}"
|
|
144
|
+
)
|
|
145
|
+
lmcache_worker_port = config.lmcache_worker_ports[self.worker_id]
|
|
146
|
+
else:
|
|
147
|
+
# start lmcache worker on given worker ids;
|
|
148
|
+
# need at least one port per explicitly listed worker
|
|
149
|
+
if len(config.lmcache_worker_ports) < len(lmcache_worker_ids):
|
|
150
|
+
raise ValueError(
|
|
151
|
+
f"lmcache_worker_ports must have at least "
|
|
152
|
+
f"{len(lmcache_worker_ids)} "
|
|
153
|
+
f"port(s) (one per lmcache worker), "
|
|
154
|
+
f"got {len(config.lmcache_worker_ports)}"
|
|
155
|
+
)
|
|
156
|
+
index = lmcache_worker_ids.index(self.worker_id)
|
|
157
|
+
lmcache_worker_port = config.lmcache_worker_ports[index]
|
|
158
|
+
|
|
159
|
+
self.lmcache_worker_internal_url = f"*:{lmcache_worker_port}"
|
|
160
|
+
self.lmcache_worker_ip = get_ip()
|
|
161
|
+
self.lmcache_worker_port = lmcache_worker_port
|
|
162
|
+
|
|
163
|
+
self.p2p_init_url = None
|
|
164
|
+
if config.enable_p2p:
|
|
165
|
+
self.p2p_host = config.p2p_host
|
|
166
|
+
self.p2p_init_port = config.p2p_init_ports[self.worker_id]
|
|
167
|
+
self.p2p_init_url = f"{self.p2p_host}:{self.p2p_init_port}"
|
|
168
|
+
|
|
169
|
+
self.reply_socket = get_zmq_socket(
|
|
170
|
+
self.context,
|
|
171
|
+
self.lmcache_worker_internal_url,
|
|
172
|
+
protocol="tcp",
|
|
173
|
+
role=zmq.REP, # type: ignore[attr-defined]
|
|
174
|
+
bind_or_connect="bind",
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
logger.info(f"Reply socket established at {self.lmcache_worker_internal_url}")
|
|
178
|
+
|
|
179
|
+
self.loop = asyncio.new_event_loop()
|
|
180
|
+
self.thread = threading.Thread(
|
|
181
|
+
target=self.loop.run_forever, daemon=True, name="lmcache-worker-thread"
|
|
182
|
+
)
|
|
183
|
+
self.thread.start()
|
|
184
|
+
asyncio.run_coroutine_threadsafe(self.start_all(), self.loop)
|
|
185
|
+
|
|
186
|
+
self.msg_queue: asyncio.Queue[WorkerMsg] = asyncio.Queue()
|
|
187
|
+
|
|
188
|
+
# Full sync sender (initialized lazily when needed)
|
|
189
|
+
self._full_sync_sender: Optional["FullSyncSender"] = None
|
|
190
|
+
|
|
191
|
+
async def register(self):
|
|
192
|
+
"""
|
|
193
|
+
Register the lmcache worker with the controller via DEALER-ROUTER.
|
|
194
|
+
|
|
195
|
+
This method sends a RegisterMsg and waits for RegisterRetMsg
|
|
196
|
+
which contains extra_config (e.g., heartbeat_url).
|
|
197
|
+
"""
|
|
198
|
+
assert self.lmcache_instance_id is not None
|
|
199
|
+
logger.info(
|
|
200
|
+
"Registering lmcache instance-worker: %s",
|
|
201
|
+
(self.lmcache_instance_id, self.worker_id),
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
register_msg = RegisterMsg(
|
|
205
|
+
instance_id=self.lmcache_instance_id,
|
|
206
|
+
worker_id=self.worker_id,
|
|
207
|
+
ip=self.lmcache_worker_ip,
|
|
208
|
+
port=self.lmcache_worker_port,
|
|
209
|
+
peer_init_url=self.p2p_init_url,
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
# Send via DEALER socket (empty frame + payload) and wait for response
|
|
213
|
+
try:
|
|
214
|
+
await self.req_socket.send_multipart(
|
|
215
|
+
[b"", msgspec.msgpack.encode(register_msg)]
|
|
216
|
+
)
|
|
217
|
+
# DEALER receives: [empty_frame, payload]
|
|
218
|
+
frames = await self.req_socket.recv_multipart()
|
|
219
|
+
serialized_ret_msg = frames[-1]
|
|
220
|
+
ret_msg = msgspec.msgpack.decode(serialized_ret_msg, type=Msg)
|
|
221
|
+
|
|
222
|
+
if isinstance(ret_msg, RegisterRetMsg):
|
|
223
|
+
self._process_register_response(ret_msg)
|
|
224
|
+
else:
|
|
225
|
+
logger.warning("Unexpected register response type: %s", type(ret_msg))
|
|
226
|
+
except zmq.ZMQError as e:
|
|
227
|
+
logger.error("Failed to register with controller: %s", e)
|
|
228
|
+
raise
|
|
229
|
+
|
|
230
|
+
def _process_register_response(self, ret_msg: RegisterRetMsg):
|
|
231
|
+
"""Process RegisterRetMsg and initialize components based on extra_config."""
|
|
232
|
+
extra_config = ret_msg.extra_config
|
|
233
|
+
|
|
234
|
+
# Initialize heartbeat socket if heartbeat_url is provided
|
|
235
|
+
heartbeat_url = extra_config.get("heartbeat_url")
|
|
236
|
+
if heartbeat_url:
|
|
237
|
+
logger.info("Received heartbeat_url from controller: %s", heartbeat_url)
|
|
238
|
+
self.controller_heartbeat_url = heartbeat_url
|
|
239
|
+
self._create_heartbeat_socket()
|
|
240
|
+
else:
|
|
241
|
+
logger.info("No dedicated heartbeat_url provided by controller")
|
|
242
|
+
|
|
243
|
+
def deregister(self):
|
|
244
|
+
"""
|
|
245
|
+
De-register the lmcache worker from the controller.
|
|
246
|
+
"""
|
|
247
|
+
assert self.lmcache_instance_id is not None
|
|
248
|
+
self.put_msg(
|
|
249
|
+
DeRegisterMsg(
|
|
250
|
+
instance_id=self.lmcache_instance_id,
|
|
251
|
+
worker_id=self.worker_id,
|
|
252
|
+
ip=self.lmcache_worker_ip,
|
|
253
|
+
port=self.lmcache_worker_port,
|
|
254
|
+
)
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
async def async_put_and_wait_msg(
|
|
258
|
+
self,
|
|
259
|
+
msg: WorkerReqMsg,
|
|
260
|
+
) -> WorkerReqRetMsg:
|
|
261
|
+
"""
|
|
262
|
+
Send a message to the controller and wait for the response.
|
|
263
|
+
|
|
264
|
+
This method handles different types of WorkerReqMsg using appropriate sockets:
|
|
265
|
+
- HeartbeatMsg: Uses dedicated heartbeat socket
|
|
266
|
+
- Other messages (RegisterMsg, BatchedP2PLookupMsg, FullSyncStartMsg,
|
|
267
|
+
FullSyncStatusMsg): Uses DEALER socket (req_socket)
|
|
268
|
+
|
|
269
|
+
Note: With DEALER-ROUTER mode, we no longer need to separate FullSync
|
|
270
|
+
messages to heartbeat socket since DEALER supports async concurrent requests.
|
|
271
|
+
"""
|
|
272
|
+
# Send heartbeat via dedicated heartbeat socket
|
|
273
|
+
if isinstance(msg, HeartbeatMsg):
|
|
274
|
+
return await self._send_heartbeat_msg(msg)
|
|
275
|
+
|
|
276
|
+
# Send other messages via DEALER socket
|
|
277
|
+
try:
|
|
278
|
+
# DEALER socket: send [empty_frame, payload]
|
|
279
|
+
await self.req_socket.send_multipart([b"", msgspec.msgpack.encode(msg)])
|
|
280
|
+
frames = await self.req_socket.recv_multipart()
|
|
281
|
+
# DEALER receives: [empty_frame, payload]
|
|
282
|
+
serialized_ret_msg = frames[-1]
|
|
283
|
+
ret_msg = msgspec.msgpack.decode(serialized_ret_msg, type=Msg)
|
|
284
|
+
return ret_msg
|
|
285
|
+
except zmq.Again as e:
|
|
286
|
+
logger.error("Timeout occurred, recreating socket. Error: %s", e)
|
|
287
|
+
self._recreate_req_socket()
|
|
288
|
+
return self._on_request_failure(msg)
|
|
289
|
+
except zmq.ZMQError as e:
|
|
290
|
+
logger.error("ZMQ error occurred, recreating socket. Error: %s", e)
|
|
291
|
+
self._recreate_req_socket()
|
|
292
|
+
return self._on_request_failure(msg)
|
|
293
|
+
except Exception as e:
|
|
294
|
+
logger.error("Error happens in lmcache worker req_socket. Error: %s", e)
|
|
295
|
+
return self._on_request_failure(msg)
|
|
296
|
+
|
|
297
|
+
def _create_req_socket(self):
|
|
298
|
+
self.req_socket = get_zmq_socket_with_timeout(
|
|
299
|
+
self.context,
|
|
300
|
+
self.controller_rep_url,
|
|
301
|
+
"tcp",
|
|
302
|
+
zmq.DEALER, # type: ignore[attr-defined]
|
|
303
|
+
"connect",
|
|
304
|
+
self.socket_recv_timeout_ms,
|
|
305
|
+
self.socket_send_timeout_ms,
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
def _create_heartbeat_socket(self):
|
|
309
|
+
logger.info(
|
|
310
|
+
"Creating heartbeat socket to connect to: %s, "
|
|
311
|
+
"recv_timeout: %dms, send_timeout: %dms",
|
|
312
|
+
self.controller_heartbeat_url,
|
|
313
|
+
self.socket_recv_timeout_ms,
|
|
314
|
+
self.socket_send_timeout_ms,
|
|
315
|
+
)
|
|
316
|
+
self.heartbeat_socket = get_zmq_socket_with_timeout(
|
|
317
|
+
self.context,
|
|
318
|
+
self.controller_heartbeat_url,
|
|
319
|
+
"tcp",
|
|
320
|
+
zmq.DEALER, # type: ignore[attr-defined]
|
|
321
|
+
"connect",
|
|
322
|
+
self.socket_recv_timeout_ms,
|
|
323
|
+
self.socket_send_timeout_ms,
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
def _recreate_heartbeat_socket(self):
|
|
327
|
+
try:
|
|
328
|
+
self.heartbeat_socket.close(linger=0)
|
|
329
|
+
except Exception as e:
|
|
330
|
+
logger.error("Error closing heartbeat socket: %s", e)
|
|
331
|
+
self._create_heartbeat_socket()
|
|
332
|
+
|
|
333
|
+
def _recreate_req_socket(self):
|
|
334
|
+
try:
|
|
335
|
+
self.req_socket.close(linger=0)
|
|
336
|
+
except Exception as e:
|
|
337
|
+
logger.error("Error closing req socket: %s", e)
|
|
338
|
+
self._create_req_socket()
|
|
339
|
+
|
|
340
|
+
def _get_full_sync_sender(self):
|
|
341
|
+
"""Lazy initialization of FullSyncSender"""
|
|
342
|
+
if self._full_sync_sender is None:
|
|
343
|
+
# Get the local_cpu_backend from lmcache_engine
|
|
344
|
+
local_cpu_backend = self.lmcache_engine.storage_manager.local_cpu_backend
|
|
345
|
+
self._full_sync_sender = FullSyncSender(
|
|
346
|
+
config=self.config,
|
|
347
|
+
worker=self,
|
|
348
|
+
lmcache_engine=self.lmcache_engine,
|
|
349
|
+
local_cpu_backend=local_cpu_backend,
|
|
350
|
+
)
|
|
351
|
+
return self._full_sync_sender
|
|
352
|
+
|
|
353
|
+
def _on_request_failure(self, msg: WorkerReqMsg) -> WorkerReqRetMsg:
|
|
354
|
+
"""
|
|
355
|
+
Create a default return message when worker -> controller
|
|
356
|
+
request encounters an error (e.g., timeout, ZMQ error).
|
|
357
|
+
"""
|
|
358
|
+
if isinstance(msg, BatchedP2PLookupMsg):
|
|
359
|
+
return BatchedP2PLookupRetMsg(layout_info=[("", "", 0, "")])
|
|
360
|
+
elif isinstance(msg, HeartbeatMsg):
|
|
361
|
+
return HeartbeatRetMsg() # No command by default
|
|
362
|
+
elif isinstance(msg, FullSyncStartMsg):
|
|
363
|
+
return FullSyncStartRetMsg(
|
|
364
|
+
sync_id=msg.sync_id,
|
|
365
|
+
accepted=False,
|
|
366
|
+
error_msg="Communication error",
|
|
367
|
+
)
|
|
368
|
+
elif isinstance(msg, FullSyncStatusMsg):
|
|
369
|
+
return FullSyncStatusRetMsg(
|
|
370
|
+
sync_id=msg.sync_id,
|
|
371
|
+
is_complete=False,
|
|
372
|
+
global_progress=0.0,
|
|
373
|
+
can_exit_freeze=False,
|
|
374
|
+
)
|
|
375
|
+
else:
|
|
376
|
+
raise ValueError(f"Unknown message type: {type(msg)}")
|
|
377
|
+
|
|
378
|
+
def put_msg(self, msg: WorkerMsg):
|
|
379
|
+
"""
|
|
380
|
+
Put a message into the message queue.
|
|
381
|
+
"""
|
|
382
|
+
# TODO(Jiayi): This might introduce ~0.05ms latency than
|
|
383
|
+
# a normal function call.
|
|
384
|
+
# Not sure how much overhead is blocking though.
|
|
385
|
+
self.loop.call_soon_threadsafe(self.msg_queue.put_nowait, msg)
|
|
386
|
+
|
|
387
|
+
async def batched_get_msg(self, max_bsz: int = 50) -> list[WorkerMsg]:
|
|
388
|
+
"""
|
|
389
|
+
Get a batch of messages from the message queue.
|
|
390
|
+
"""
|
|
391
|
+
batch = []
|
|
392
|
+
|
|
393
|
+
# use blocking get for the first msg
|
|
394
|
+
try:
|
|
395
|
+
item = await self.msg_queue.get()
|
|
396
|
+
batch.append(item)
|
|
397
|
+
except asyncio.CancelledError:
|
|
398
|
+
return batch # shutdown path
|
|
399
|
+
|
|
400
|
+
for _ in range(max_bsz - 1):
|
|
401
|
+
try:
|
|
402
|
+
item = self.msg_queue.get_nowait()
|
|
403
|
+
batch.append(item)
|
|
404
|
+
except asyncio.QueueEmpty:
|
|
405
|
+
break
|
|
406
|
+
return batch
|
|
407
|
+
|
|
408
|
+
async def _send_heartbeat_msg(self, msg: HeartbeatMsg) -> HeartbeatRetMsg:
|
|
409
|
+
"""
|
|
410
|
+
Send heartbeat message via dedicated heartbeat DEALER socket.
|
|
411
|
+
This is separate from async_put_and_wait_msg to keep heartbeat independent.
|
|
412
|
+
"""
|
|
413
|
+
if self.heartbeat_socket is None:
|
|
414
|
+
logger.warning("Heartbeat socket is not initialized")
|
|
415
|
+
return HeartbeatRetMsg()
|
|
416
|
+
try:
|
|
417
|
+
# DEALER socket: send [empty_frame, payload]
|
|
418
|
+
logger.info("Sending heartbeat message to controller...")
|
|
419
|
+
await self.heartbeat_socket.send_multipart(
|
|
420
|
+
[b"", msgspec.msgpack.encode(msg)]
|
|
421
|
+
)
|
|
422
|
+
logger.info("Heartbeat message sent, waiting for response...")
|
|
423
|
+
frames = await self.heartbeat_socket.recv_multipart()
|
|
424
|
+
logger.info("Received heartbeat response with %d frames", len(frames))
|
|
425
|
+
# DEALER receives: [empty_frame, payload]
|
|
426
|
+
serialized_ret_msg = frames[-1]
|
|
427
|
+
ret_msg = msgspec.msgpack.decode(serialized_ret_msg, type=Msg)
|
|
428
|
+
return ret_msg
|
|
429
|
+
except zmq.Again as e:
|
|
430
|
+
logger.error("Heartbeat timeout occurred, recreating socket. Error: %s", e)
|
|
431
|
+
self._recreate_heartbeat_socket()
|
|
432
|
+
return HeartbeatRetMsg()
|
|
433
|
+
except zmq.ZMQError as e:
|
|
434
|
+
logger.error(
|
|
435
|
+
"Heartbeat ZMQ error occurred, recreating socket. Error: %s", e
|
|
436
|
+
)
|
|
437
|
+
self._recreate_heartbeat_socket()
|
|
438
|
+
return HeartbeatRetMsg()
|
|
439
|
+
except Exception as e:
|
|
440
|
+
logger.error("Error happens in heartbeat socket. Error: %s", e)
|
|
441
|
+
return HeartbeatRetMsg()
|
|
442
|
+
|
|
443
|
+
async def heartbeat(self):
|
|
444
|
+
"""
|
|
445
|
+
Send periodic heartbeats to the controller (DEALER-ROUTER mode).
|
|
446
|
+
|
|
447
|
+
Process any commands received in the heartbeat response.
|
|
448
|
+
Uses dedicated heartbeat socket to avoid blocking from other requests.
|
|
449
|
+
"""
|
|
450
|
+
enable_heartbeat = (
|
|
451
|
+
self.config.lmcache_worker_heartbeat_time is not None
|
|
452
|
+
and self.config.lmcache_worker_heartbeat_time > 0
|
|
453
|
+
and self.heartbeat_socket is not None
|
|
454
|
+
)
|
|
455
|
+
if enable_heartbeat:
|
|
456
|
+
await asyncio.sleep(self.config.lmcache_worker_heartbeat_delay_time)
|
|
457
|
+
logger.info(
|
|
458
|
+
"Start heartbeat in %s : %s, delay time: %ss, heartbeat time: %ss",
|
|
459
|
+
self.lmcache_instance_id,
|
|
460
|
+
self.worker_id,
|
|
461
|
+
self.config.lmcache_worker_heartbeat_delay_time,
|
|
462
|
+
self.config.lmcache_worker_heartbeat_time,
|
|
463
|
+
)
|
|
464
|
+
while True:
|
|
465
|
+
# Send heartbeat via dedicated heartbeat socket
|
|
466
|
+
heartbeat_msg = HeartbeatMsg(
|
|
467
|
+
instance_id=self.lmcache_instance_id,
|
|
468
|
+
worker_id=self.worker_id,
|
|
469
|
+
ip=self.lmcache_worker_ip,
|
|
470
|
+
port=self.lmcache_worker_port,
|
|
471
|
+
peer_init_url=self.p2p_init_url,
|
|
472
|
+
)
|
|
473
|
+
|
|
474
|
+
try:
|
|
475
|
+
ret_msg = await self._send_heartbeat_msg(heartbeat_msg)
|
|
476
|
+
|
|
477
|
+
if isinstance(ret_msg, HeartbeatRetMsg):
|
|
478
|
+
self._handle_heartbeat_commands(ret_msg)
|
|
479
|
+
else:
|
|
480
|
+
logger.warning(
|
|
481
|
+
"Unexpected heartbeat response type: %s", type(ret_msg)
|
|
482
|
+
)
|
|
483
|
+
except Exception as e:
|
|
484
|
+
logger.error("Error during heartbeat: %s", e)
|
|
485
|
+
|
|
486
|
+
await asyncio.sleep(self.config.lmcache_worker_heartbeat_time)
|
|
487
|
+
|
|
488
|
+
def _handle_heartbeat_commands(self, ret_msg: HeartbeatRetMsg):
|
|
489
|
+
"""
|
|
490
|
+
Handle commands received in heartbeat response.
|
|
491
|
+
|
|
492
|
+
Uses polymorphic dispatch - each command class implements its own
|
|
493
|
+
execute() method. Commands are executed sequentially.
|
|
494
|
+
"""
|
|
495
|
+
if not ret_msg.has_commands():
|
|
496
|
+
return
|
|
497
|
+
|
|
498
|
+
for command in ret_msg.commands:
|
|
499
|
+
logger.info(
|
|
500
|
+
"Executing heartbeat command: %s",
|
|
501
|
+
command.describe(),
|
|
502
|
+
)
|
|
503
|
+
try:
|
|
504
|
+
command.execute(self)
|
|
505
|
+
except NotImplementedError:
|
|
506
|
+
logger.warning(
|
|
507
|
+
"Command %s.execute() not implemented yet",
|
|
508
|
+
type(command).__name__,
|
|
509
|
+
)
|
|
510
|
+
except Exception as e:
|
|
511
|
+
logger.error(
|
|
512
|
+
"Error executing command %s: %s",
|
|
513
|
+
type(command).__name__,
|
|
514
|
+
e,
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
async def push(self):
|
|
518
|
+
while True:
|
|
519
|
+
try:
|
|
520
|
+
msgs = await self.batched_get_msg()
|
|
521
|
+
logger.debug(f"Sending {len(msgs)} messages")
|
|
522
|
+
self.push_socket.send_multipart(
|
|
523
|
+
[msgspec.msgpack.encode(msg) for msg in msgs]
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
except Exception as e:
|
|
527
|
+
logger.error(f"Push error: {e}")
|
|
528
|
+
|
|
529
|
+
async def handle_request(self):
|
|
530
|
+
"""
|
|
531
|
+
Handle incoming requests (control msgs) from the controller.
|
|
532
|
+
"""
|
|
533
|
+
while True:
|
|
534
|
+
try:
|
|
535
|
+
serialized_request = await self.reply_socket.recv()
|
|
536
|
+
request = msgspec.msgpack.decode(serialized_request, type=Msg)
|
|
537
|
+
logger.debug(f"Received message: {request}")
|
|
538
|
+
if isinstance(request, MoveWorkerMsg):
|
|
539
|
+
tokens = request.tokens
|
|
540
|
+
old_position = request.old_position
|
|
541
|
+
new_position = request.new_position
|
|
542
|
+
do_copy = request.copy
|
|
543
|
+
worker_event_id = request.worker_event_id
|
|
544
|
+
|
|
545
|
+
# Intra node move
|
|
546
|
+
if new_position[0] == self.lmcache_worker_internal_url:
|
|
547
|
+
# TODO(Jiayi): currently we only support moving from
|
|
548
|
+
# local disk to local cpu.
|
|
549
|
+
assert old_position[1] == "LocalDiskBackend"
|
|
550
|
+
assert new_position[1] == "LocalCPUBackend"
|
|
551
|
+
assert do_copy
|
|
552
|
+
|
|
553
|
+
# TODO(Jiayi): We need to align prefetch and move.
|
|
554
|
+
logger.debug("Executing prefetch operation.")
|
|
555
|
+
raise NotImplementedError(
|
|
556
|
+
"Prefetch from controller is not implemented yet."
|
|
557
|
+
)
|
|
558
|
+
else:
|
|
559
|
+
assert new_position[1] == "LocalCPUBackend", (
|
|
560
|
+
"Only support moving to cpu for now."
|
|
561
|
+
)
|
|
562
|
+
logger.debug("Executing cross-node move operation.")
|
|
563
|
+
num_tokens = self.lmcache_engine.move(
|
|
564
|
+
tokens=tokens,
|
|
565
|
+
old_position=old_position,
|
|
566
|
+
new_position=new_position,
|
|
567
|
+
event_id=worker_event_id,
|
|
568
|
+
do_copy=do_copy,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
# TODO(Jiayi): LMCache needs to have an event tracking
|
|
572
|
+
# pool to enable more advanced control-plane optims.
|
|
573
|
+
# For now, we use a dummy `event_id`.
|
|
574
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
575
|
+
MoveWorkerRetMsg(num_tokens=num_tokens)
|
|
576
|
+
)
|
|
577
|
+
elif isinstance(request, CompressWorkerMsg):
|
|
578
|
+
num_compressed_tokens = self.lmcache_engine.compress(
|
|
579
|
+
tokens=request.tokens,
|
|
580
|
+
method=request.method,
|
|
581
|
+
location=request.location,
|
|
582
|
+
event_id=request.worker_event_id,
|
|
583
|
+
)
|
|
584
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
585
|
+
CompressWorkerRetMsg(num_tokens=num_compressed_tokens)
|
|
586
|
+
)
|
|
587
|
+
elif isinstance(request, DecompressWorkerMsg):
|
|
588
|
+
num_decompressed_tokens = self.lmcache_engine.decompress(
|
|
589
|
+
tokens=request.tokens,
|
|
590
|
+
method=request.method,
|
|
591
|
+
location=request.location,
|
|
592
|
+
event_id=request.worker_event_id,
|
|
593
|
+
)
|
|
594
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
595
|
+
DecompressWorkerRetMsg(num_tokens=num_decompressed_tokens)
|
|
596
|
+
)
|
|
597
|
+
elif isinstance(request, PinWorkerMsg):
|
|
598
|
+
num_pinned_tokens = self.lmcache_engine.lookup(
|
|
599
|
+
tokens=request.tokens,
|
|
600
|
+
search_range=[request.location],
|
|
601
|
+
lookup_id=request.worker_event_id,
|
|
602
|
+
pin=True,
|
|
603
|
+
)
|
|
604
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
605
|
+
PinWorkerRetMsg(num_tokens=num_pinned_tokens)
|
|
606
|
+
)
|
|
607
|
+
elif isinstance(request, ClearWorkerMsg):
|
|
608
|
+
num_cleared_tokens = self.lmcache_engine.clear(
|
|
609
|
+
locations=[request.location],
|
|
610
|
+
)
|
|
611
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
612
|
+
ClearWorkerRetMsg(num_tokens=num_cleared_tokens)
|
|
613
|
+
)
|
|
614
|
+
elif isinstance(request, HealthWorkerMsg):
|
|
615
|
+
error_code = self.lmcache_engine.health()
|
|
616
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
617
|
+
HealthWorkerRetMsg(error_code=error_code)
|
|
618
|
+
)
|
|
619
|
+
else:
|
|
620
|
+
logger.error(f"Unknown message: {request}")
|
|
621
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
622
|
+
ErrorMsg(error=f"Unknown message: {request}")
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
await self.reply_socket.send(serialized_ret_msg)
|
|
626
|
+
except Exception as e:
|
|
627
|
+
logger.error(f"Worker error: {e}")
|
|
628
|
+
serialized_ret_msg = msgspec.msgpack.encode(
|
|
629
|
+
ErrorMsg(error=f"Worker error: {e}")
|
|
630
|
+
)
|
|
631
|
+
await self.reply_socket.send(serialized_ret_msg)
|
|
632
|
+
|
|
633
|
+
async def start_all(self):
|
|
634
|
+
try:
|
|
635
|
+
# Register first to get heartbeat_url before starting heartbeat task
|
|
636
|
+
await self.register()
|
|
637
|
+
|
|
638
|
+
logger.info(
|
|
639
|
+
f"Starting lmcache worker {self.worker_id}"
|
|
640
|
+
f"for instance {self.lmcache_instance_id}"
|
|
641
|
+
)
|
|
642
|
+
await asyncio.gather(
|
|
643
|
+
self.push(),
|
|
644
|
+
self.handle_request(),
|
|
645
|
+
self.heartbeat(),
|
|
646
|
+
)
|
|
647
|
+
except Exception as e:
|
|
648
|
+
logger.error(
|
|
649
|
+
f"Instance {self.lmcache_instance_id}, "
|
|
650
|
+
f"worker {self.worker_id} error: {e}"
|
|
651
|
+
)
|
|
652
|
+
|
|
653
|
+
def close(self):
|
|
654
|
+
self.deregister()
|
|
655
|
+
if self.loop.is_running():
|
|
656
|
+
self.loop.call_soon_threadsafe(self.loop.stop)
|
|
657
|
+
if self.thread.is_alive():
|
|
658
|
+
self.thread.join()
|
|
659
|
+
self.loop.close()
|
|
660
|
+
close_zmq_socket(self.push_socket)
|
|
661
|
+
close_zmq_socket(self.reply_socket)
|
|
662
|
+
if self.heartbeat_socket is not None:
|
|
663
|
+
close_zmq_socket(self.heartbeat_socket)
|
|
664
|
+
if hasattr(self, "req_socket"):
|
|
665
|
+
close_zmq_socket(self.req_socket)
|