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,788 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, List, Optional, Sequence, Union
|
|
5
|
+
import asyncio
|
|
6
|
+
import enum
|
|
7
|
+
|
|
8
|
+
# Third Party
|
|
9
|
+
import msgspec
|
|
10
|
+
import torch
|
|
11
|
+
import zmq
|
|
12
|
+
import zmq.asyncio
|
|
13
|
+
|
|
14
|
+
# First Party
|
|
15
|
+
from lmcache.logging import init_logger
|
|
16
|
+
from lmcache.observability import LMCStatsMonitor
|
|
17
|
+
from lmcache.utils import CacheEngineKey
|
|
18
|
+
from lmcache.v1.cache_controller.message import (
|
|
19
|
+
BatchedP2PLookupMsg,
|
|
20
|
+
BatchedP2PLookupRetMsg,
|
|
21
|
+
ErrorMsg,
|
|
22
|
+
)
|
|
23
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
24
|
+
from lmcache.v1.memory_management import (
|
|
25
|
+
MemoryFormat,
|
|
26
|
+
MemoryObj,
|
|
27
|
+
PagedCpuGpuMemoryAllocator,
|
|
28
|
+
)
|
|
29
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
30
|
+
from lmcache.v1.rpc_utils import (
|
|
31
|
+
DEFAULT_SOCKET_RECV_TIMEOUT_MS,
|
|
32
|
+
DEFAULT_SOCKET_SEND_TIMEOUT_MS,
|
|
33
|
+
get_zmq_context,
|
|
34
|
+
get_zmq_socket_with_timeout,
|
|
35
|
+
)
|
|
36
|
+
from lmcache.v1.storage_backend.abstract_backend import StorageBackendInterface
|
|
37
|
+
from lmcache.v1.storage_backend.local_cpu_backend import LocalCPUBackend
|
|
38
|
+
from lmcache.v1.transfer_channel import CreateTransferChannel
|
|
39
|
+
from lmcache.v1.transfer_channel.transfer_utils import (
|
|
40
|
+
P2PInitSideMsg,
|
|
41
|
+
P2PInitSideRetMsg,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
if TYPE_CHECKING:
|
|
45
|
+
# First Party
|
|
46
|
+
from lmcache.v1.cache_controller import LMCacheWorker
|
|
47
|
+
|
|
48
|
+
logger = init_logger(__name__)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class P2PMsgBase(msgspec.Struct, tag=True):
|
|
52
|
+
"""Base class for all P2P-related messages"""
|
|
53
|
+
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class BatchedLookupAndGetMsg(P2PMsgBase):
|
|
58
|
+
"""Lookup and retrieve message"""
|
|
59
|
+
|
|
60
|
+
lookup_id: str
|
|
61
|
+
|
|
62
|
+
receiver_id: str
|
|
63
|
+
|
|
64
|
+
# CacheEngineKey in string form
|
|
65
|
+
keys: list[str]
|
|
66
|
+
|
|
67
|
+
# Indexes (remote) of allocated memory objects (to be written)
|
|
68
|
+
mem_indexes: list[int]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class BatchedLookupAndGetRetMsg(P2PMsgBase):
|
|
72
|
+
"""Lookup and retrieve message"""
|
|
73
|
+
|
|
74
|
+
# Number of hit chunks
|
|
75
|
+
num_hit_chunks: int
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class BatchedLookupAndPutMsg(P2PMsgBase):
|
|
79
|
+
"""Lookup and retrieve message"""
|
|
80
|
+
|
|
81
|
+
sender_id: str
|
|
82
|
+
|
|
83
|
+
# CacheEngineKey in string form
|
|
84
|
+
keys: list[str]
|
|
85
|
+
|
|
86
|
+
# Number of tokens for each chunk
|
|
87
|
+
offsets: list[int]
|
|
88
|
+
|
|
89
|
+
# Indexes (remote) of allocated memory objects (to be read)
|
|
90
|
+
mem_indexes: list[int]
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class BatchedLookupAndPutRetMsg(P2PMsgBase):
|
|
94
|
+
"""Lookup and retrieve message"""
|
|
95
|
+
|
|
96
|
+
# Number of read chunks
|
|
97
|
+
num_read_chunks: int
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class P2PErrorCode(enum.Enum):
|
|
101
|
+
"""P2P error codes enumeration"""
|
|
102
|
+
|
|
103
|
+
P2P_SERVER_ERROR = enum.auto()
|
|
104
|
+
UNKNOWN_MSG_TYPE = enum.auto()
|
|
105
|
+
REMOTE_XFER_HANDLER_NOT_INITIALIZED = enum.auto()
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class P2PErrorMsg(P2PMsgBase):
|
|
109
|
+
"""
|
|
110
|
+
Error message, return error code to client.
|
|
111
|
+
|
|
112
|
+
-1 represents unknown msg type;
|
|
113
|
+
-2 represents remote xfer handler not initialized,
|
|
114
|
+
call `_ensure_peer_connection` first;
|
|
115
|
+
-3 represents p2p peer_request_handler error;
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
error_code: P2PErrorCode
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
P2PMsg = Union[
|
|
122
|
+
BatchedLookupAndGetMsg,
|
|
123
|
+
BatchedLookupAndGetRetMsg,
|
|
124
|
+
BatchedLookupAndPutMsg,
|
|
125
|
+
BatchedLookupAndPutRetMsg,
|
|
126
|
+
P2PErrorMsg,
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
@dataclass
|
|
131
|
+
class PeerInfo:
|
|
132
|
+
"""Peer information"""
|
|
133
|
+
|
|
134
|
+
peer_init_url: str # peer id
|
|
135
|
+
peer_lookup_url: str
|
|
136
|
+
lookup_lock: asyncio.Lock
|
|
137
|
+
lookup_socket: zmq.asyncio.Socket
|
|
138
|
+
|
|
139
|
+
def update_peer_lookup_url(self, new_peer_lookup_url: str):
|
|
140
|
+
if self.peer_lookup_url != new_peer_lookup_url:
|
|
141
|
+
logger.info(
|
|
142
|
+
"Target peer %s lookup url changed from %s to %s",
|
|
143
|
+
self.peer_init_url,
|
|
144
|
+
self.peer_lookup_url,
|
|
145
|
+
new_peer_lookup_url,
|
|
146
|
+
)
|
|
147
|
+
self.peer_lookup_url = new_peer_lookup_url
|
|
148
|
+
|
|
149
|
+
def update_lookup_socket(self, new_lookup_socket: zmq.asyncio.Socket):
|
|
150
|
+
try:
|
|
151
|
+
self.lookup_socket.close(linger=0)
|
|
152
|
+
except Exception as e:
|
|
153
|
+
logger.error("Failed to close peer %s lookup socket", self.peer_init_url, e)
|
|
154
|
+
self.lookup_socket = new_lookup_socket
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# TODO(Jiayi): handle asymmetric TP.
|
|
158
|
+
class P2PBackend(StorageBackendInterface):
|
|
159
|
+
def __init__(
|
|
160
|
+
self,
|
|
161
|
+
config: LMCacheEngineConfig,
|
|
162
|
+
metadata: LMCacheMetadata,
|
|
163
|
+
loop: asyncio.AbstractEventLoop,
|
|
164
|
+
local_cpu_backend: LocalCPUBackend,
|
|
165
|
+
lmcache_worker: "LMCacheWorker",
|
|
166
|
+
):
|
|
167
|
+
self.config = config
|
|
168
|
+
self.loop = loop
|
|
169
|
+
self.lmcache_worker = lmcache_worker
|
|
170
|
+
self.stats_monitor = LMCStatsMonitor.GetOrCreate()
|
|
171
|
+
assert config.p2p_host is not None, "p2p_host must be specified"
|
|
172
|
+
assert config.p2p_init_ports is not None, "p2p_init_ports must be specified"
|
|
173
|
+
assert config.p2p_lookup_ports is not None, "p2p_lookup_ports must be specified"
|
|
174
|
+
|
|
175
|
+
# Load timeout configurations from extra_config (in milliseconds)
|
|
176
|
+
self.socket_recv_timeout_ms = config.get_extra_config_value(
|
|
177
|
+
"p2p_socket_recv_timeout_ms", DEFAULT_SOCKET_RECV_TIMEOUT_MS
|
|
178
|
+
)
|
|
179
|
+
self.socket_send_timeout_ms = config.get_extra_config_value(
|
|
180
|
+
"p2p_socket_send_timeout_ms", DEFAULT_SOCKET_SEND_TIMEOUT_MS
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Load max retry count from extra_config
|
|
184
|
+
self.max_retry_count = config.get_extra_config_value("p2p_max_retry_count", 3)
|
|
185
|
+
|
|
186
|
+
# tp rank is worker id for now
|
|
187
|
+
self.tp_rank = metadata.worker_id
|
|
188
|
+
|
|
189
|
+
self.peer_host = config.p2p_host
|
|
190
|
+
self.peer_init_port = config.p2p_init_ports[self.tp_rank]
|
|
191
|
+
self.peer_init_url = f"{self.peer_host}:{self.peer_init_port}"
|
|
192
|
+
|
|
193
|
+
self.peer_lookup_port = config.p2p_lookup_ports[self.tp_rank]
|
|
194
|
+
self.peer_lookup_url = f"{self.peer_host}:{self.peer_lookup_port}"
|
|
195
|
+
|
|
196
|
+
self.lmcache_instance_id = config.lmcache_instance_id
|
|
197
|
+
|
|
198
|
+
# A CacheEngineKey (in int form) -> a list of
|
|
199
|
+
# (peer_init_url, peer_lookup_url, location)
|
|
200
|
+
self.local_lookup_cache: dict[int, tuple[str, str, str]] = {}
|
|
201
|
+
# the target peer info mapping
|
|
202
|
+
self.target_peer_info_mapping: dict[str, PeerInfo] = {}
|
|
203
|
+
# the lock for updating target peer info mapping
|
|
204
|
+
self.update_peer_lock = asyncio.Lock()
|
|
205
|
+
|
|
206
|
+
# A lookup_id -> (peer_init_url, location)
|
|
207
|
+
# TODO(chunxiaozheng): location is not used for now
|
|
208
|
+
self.lookup_id_to_peer_mapping: dict[str, tuple[str, str]] = {}
|
|
209
|
+
|
|
210
|
+
# TODO(Jiayi): support gpu and local storage p2p as well.
|
|
211
|
+
self.local_cpu_backend = local_cpu_backend
|
|
212
|
+
self.memory_allocator = local_cpu_backend.get_memory_allocator()
|
|
213
|
+
assert isinstance(self.memory_allocator, PagedCpuGpuMemoryAllocator)
|
|
214
|
+
|
|
215
|
+
self.full_size_shapes = self.memory_allocator.cpu_allocator.shapes
|
|
216
|
+
self.dtypes = self.memory_allocator.cpu_allocator.dtypes
|
|
217
|
+
self.fmt: MemoryFormat = (
|
|
218
|
+
MemoryFormat.KV_MLA_FMT if metadata.use_mla else MemoryFormat.KV_2LTD
|
|
219
|
+
)
|
|
220
|
+
self.chunk_size = config.chunk_size
|
|
221
|
+
|
|
222
|
+
device_type = (
|
|
223
|
+
"cpu" if config.nixl_buffer_device is None else config.nixl_buffer_device
|
|
224
|
+
)
|
|
225
|
+
self.transfer_channel = CreateTransferChannel(
|
|
226
|
+
channel_type=config.transfer_channel,
|
|
227
|
+
async_mode=True,
|
|
228
|
+
role="both",
|
|
229
|
+
buffer_ptr=self.memory_allocator.cpu_allocator.buffer_ptr,
|
|
230
|
+
buffer_size=self.memory_allocator.cpu_allocator.buffer_size,
|
|
231
|
+
align_bytes=self.memory_allocator.cpu_allocator.align_bytes,
|
|
232
|
+
tp_rank=self.tp_rank,
|
|
233
|
+
peer_init_url=self.peer_init_url,
|
|
234
|
+
peer_lookup_url=self.peer_lookup_url,
|
|
235
|
+
backends=config.nixl_backends,
|
|
236
|
+
event_loop=loop,
|
|
237
|
+
device=device_type,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
self.running = asyncio.Event()
|
|
241
|
+
self.running.set()
|
|
242
|
+
self.async_context: Optional[zmq.asyncio.Context] = None
|
|
243
|
+
self.async_peer_socket: Optional[zmq.asyncio.Socket] = None
|
|
244
|
+
asyncio.run_coroutine_threadsafe(
|
|
245
|
+
self._run_peer_request_handler_with_recovery(), loop
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
def __str__(self) -> str:
|
|
249
|
+
return "P2PBackend"
|
|
250
|
+
|
|
251
|
+
async def _run_peer_request_handler_with_recovery(self) -> None:
|
|
252
|
+
"""
|
|
253
|
+
Wrapper method that runs _handle_peer_requests with exception handling.
|
|
254
|
+
This ensures the handler keeps running even if unexpected errors occur.
|
|
255
|
+
"""
|
|
256
|
+
while self.running.is_set():
|
|
257
|
+
try:
|
|
258
|
+
await self._handle_peer_requests()
|
|
259
|
+
# If _handle_peer_requests exits normally, break the loop
|
|
260
|
+
break
|
|
261
|
+
except asyncio.CancelledError:
|
|
262
|
+
logger.info("Peer request handler cancelled, shutting down")
|
|
263
|
+
break
|
|
264
|
+
except Exception as e:
|
|
265
|
+
logger.error(
|
|
266
|
+
"Peer request handler crashed: %s",
|
|
267
|
+
e,
|
|
268
|
+
exc_info=True,
|
|
269
|
+
)
|
|
270
|
+
# Fast failure: log error but continue running
|
|
271
|
+
# Add small delay to prevent tight error loop
|
|
272
|
+
await asyncio.sleep(0.1)
|
|
273
|
+
if self.async_peer_socket is not None:
|
|
274
|
+
logger.warning("Closing async peer socket.")
|
|
275
|
+
try:
|
|
276
|
+
self.async_peer_socket.close(linger=0)
|
|
277
|
+
except Exception as e:
|
|
278
|
+
logger.warning(
|
|
279
|
+
"Failed to close peer socket: %s",
|
|
280
|
+
e,
|
|
281
|
+
exc_info=True,
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
async def batched_async_contains(
|
|
285
|
+
self,
|
|
286
|
+
lookup_id: str,
|
|
287
|
+
keys: List[CacheEngineKey],
|
|
288
|
+
pin: bool = False,
|
|
289
|
+
) -> int:
|
|
290
|
+
# Convert to hashes (int form)
|
|
291
|
+
hashes = [key.chunk_hash for key in keys]
|
|
292
|
+
|
|
293
|
+
# Tier 1 lookup: local lookup cache
|
|
294
|
+
# TODO(Jiayi): Please implement the local lookup cache.
|
|
295
|
+
|
|
296
|
+
# Tier 2 lookup in controller
|
|
297
|
+
msg = BatchedP2PLookupMsg(
|
|
298
|
+
instance_id=self.lmcache_instance_id,
|
|
299
|
+
worker_id=self.tp_rank,
|
|
300
|
+
hashes=hashes,
|
|
301
|
+
)
|
|
302
|
+
ret_msg = await self.lmcache_worker.async_put_and_wait_msg(msg)
|
|
303
|
+
|
|
304
|
+
if isinstance(ret_msg, ErrorMsg):
|
|
305
|
+
logger.error(
|
|
306
|
+
"Controller returned error for batched P2P lookup: %s",
|
|
307
|
+
ret_msg.error,
|
|
308
|
+
)
|
|
309
|
+
return 0
|
|
310
|
+
|
|
311
|
+
assert isinstance(ret_msg, BatchedP2PLookupRetMsg), (
|
|
312
|
+
f"Expected BatchedP2PLookupRetMsg, got {type(ret_msg)}"
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
# NOTE(Jiayi): For now we only support one peer hit.
|
|
316
|
+
layout_info = ret_msg.layout_info[0]
|
|
317
|
+
_, location, num_hit_chunks, target_peer_init_url = layout_info
|
|
318
|
+
|
|
319
|
+
logger.info(f"Got layout info from controller: {layout_info}")
|
|
320
|
+
|
|
321
|
+
if num_hit_chunks > 0:
|
|
322
|
+
try:
|
|
323
|
+
await self._ensure_peer_connection(target_peer_init_url)
|
|
324
|
+
self.lookup_id_to_peer_mapping[lookup_id] = (
|
|
325
|
+
target_peer_init_url,
|
|
326
|
+
location,
|
|
327
|
+
)
|
|
328
|
+
except Exception as e:
|
|
329
|
+
logger.error(
|
|
330
|
+
"Failed to ensure peer connection for lookup_id %s: %s",
|
|
331
|
+
lookup_id,
|
|
332
|
+
e,
|
|
333
|
+
exc_info=True,
|
|
334
|
+
)
|
|
335
|
+
return 0
|
|
336
|
+
|
|
337
|
+
# TODO(Jiayi): We could potentially update the local cache here.
|
|
338
|
+
# Or we can update after tier 3 lookup.
|
|
339
|
+
|
|
340
|
+
# NOTE(Jiayi): Tier 3 lookup is batched together with get
|
|
341
|
+
# in function `batched_get_non_blocking`.
|
|
342
|
+
|
|
343
|
+
return num_hit_chunks
|
|
344
|
+
|
|
345
|
+
async def _handle_peer_requests(self):
|
|
346
|
+
"""
|
|
347
|
+
Handle `BatchedLookupAndGetMsg` issued by peers in `batched_get_non_blocking`.
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
logger.info(
|
|
351
|
+
"Starting P2P backend batched get handler at %s", self.peer_lookup_url
|
|
352
|
+
)
|
|
353
|
+
self.async_context = get_zmq_context()
|
|
354
|
+
self.async_peer_socket = get_zmq_socket_with_timeout(
|
|
355
|
+
self.async_context,
|
|
356
|
+
self.peer_lookup_url,
|
|
357
|
+
"tcp",
|
|
358
|
+
zmq.REP,
|
|
359
|
+
"bind",
|
|
360
|
+
self.socket_recv_timeout_ms,
|
|
361
|
+
self.socket_send_timeout_ms,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
while self.running.is_set():
|
|
365
|
+
msg_bytes = await self.async_peer_socket.recv()
|
|
366
|
+
msg = msgspec.msgpack.decode(msg_bytes, type=P2PMsg)
|
|
367
|
+
|
|
368
|
+
num_tokens = len(msg.mem_indexes) * self.chunk_size
|
|
369
|
+
monitor_req_id = self.stats_monitor.on_p2p_transfer_request(num_tokens)
|
|
370
|
+
|
|
371
|
+
if isinstance(msg, BatchedLookupAndGetMsg):
|
|
372
|
+
ret_msg = await self._handle_batched_lookup_and_get(msg)
|
|
373
|
+
elif isinstance(msg, BatchedLookupAndPutMsg):
|
|
374
|
+
ret_msg = await self._handle_batched_lookup_and_put(msg)
|
|
375
|
+
else:
|
|
376
|
+
logger.error("Unknown message type: %s", type(msg))
|
|
377
|
+
ret_msg = P2PErrorMsg(error_code=P2PErrorCode.UNKNOWN_MSG_TYPE)
|
|
378
|
+
|
|
379
|
+
logger.info(f"P2P transfer finished for request {monitor_req_id}")
|
|
380
|
+
self.stats_monitor.on_p2p_transfer_finished(monitor_req_id)
|
|
381
|
+
|
|
382
|
+
await self.async_peer_socket.send(msgspec.msgpack.encode(ret_msg))
|
|
383
|
+
|
|
384
|
+
async def _handle_batched_lookup_and_get(
|
|
385
|
+
self, msg: BatchedLookupAndGetMsg
|
|
386
|
+
) -> P2PMsgBase:
|
|
387
|
+
lookup_id = msg.lookup_id
|
|
388
|
+
mem_objs = None
|
|
389
|
+
try:
|
|
390
|
+
logger.info(
|
|
391
|
+
"Received P2P batched lookup and get msg, lookup_id: %s", lookup_id
|
|
392
|
+
)
|
|
393
|
+
receiver_id = msg.receiver_id
|
|
394
|
+
if not self.transfer_channel.remote_xfer_handler_exists(receiver_id):
|
|
395
|
+
logger.error(
|
|
396
|
+
"Receiver %s does not exist in transfer channel",
|
|
397
|
+
receiver_id,
|
|
398
|
+
)
|
|
399
|
+
return P2PErrorMsg(
|
|
400
|
+
error_code=P2PErrorCode.REMOTE_XFER_HANDLER_NOT_INITIALIZED
|
|
401
|
+
)
|
|
402
|
+
|
|
403
|
+
remote_mem_indexes = msg.mem_indexes
|
|
404
|
+
keys = [CacheEngineKey.from_string(key) for key in msg.keys]
|
|
405
|
+
|
|
406
|
+
# TODO(Jiayi): Optimally, there's no need to use async call
|
|
407
|
+
# for some backends (e.g., local cpu) as there's overhead for
|
|
408
|
+
# async function call.
|
|
409
|
+
num_hit_chunks = await self.local_cpu_backend.batched_async_contains(
|
|
410
|
+
lookup_id=lookup_id,
|
|
411
|
+
keys=keys,
|
|
412
|
+
pin=True,
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
mem_objs = await self.local_cpu_backend.batched_get_non_blocking(
|
|
416
|
+
lookup_id=lookup_id,
|
|
417
|
+
keys=keys[:num_hit_chunks],
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
channel_transfer_spec = {
|
|
421
|
+
"receiver_id": receiver_id,
|
|
422
|
+
"remote_indexes": remote_mem_indexes[:num_hit_chunks],
|
|
423
|
+
}
|
|
424
|
+
await self.transfer_channel.async_batched_write(
|
|
425
|
+
objects=mem_objs,
|
|
426
|
+
transfer_spec=channel_transfer_spec,
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
return BatchedLookupAndGetRetMsg(num_hit_chunks=num_hit_chunks)
|
|
430
|
+
except Exception as e:
|
|
431
|
+
logger.error(
|
|
432
|
+
"Error during P2P batched lookup and get operation "
|
|
433
|
+
"for lookup_id %s: %s",
|
|
434
|
+
lookup_id,
|
|
435
|
+
e,
|
|
436
|
+
exc_info=True,
|
|
437
|
+
)
|
|
438
|
+
return P2PErrorMsg(error_code=P2PErrorCode.P2P_SERVER_ERROR)
|
|
439
|
+
finally:
|
|
440
|
+
if mem_objs is not None:
|
|
441
|
+
for mem_obj in mem_objs:
|
|
442
|
+
mem_obj.ref_count_down()
|
|
443
|
+
mem_obj.unpin()
|
|
444
|
+
|
|
445
|
+
async def _handle_batched_lookup_and_put(
|
|
446
|
+
self, msg: BatchedLookupAndPutMsg
|
|
447
|
+
) -> BatchedLookupAndPutRetMsg:
|
|
448
|
+
try:
|
|
449
|
+
logger.info("Received P2P batched lookup and put msg")
|
|
450
|
+
sender_id = msg.sender_id
|
|
451
|
+
r_mem_indexes = msg.mem_indexes
|
|
452
|
+
keys = [CacheEngineKey.from_string(key) for key in msg.keys]
|
|
453
|
+
offsets = msg.offsets
|
|
454
|
+
|
|
455
|
+
# TODO(Jiayi): Need to support more backend
|
|
456
|
+
r_mem_indexes_to_read = []
|
|
457
|
+
keys_to_read = []
|
|
458
|
+
local_mem_objs = []
|
|
459
|
+
keys_len = len(keys)
|
|
460
|
+
for idx, key in enumerate(keys):
|
|
461
|
+
if self.local_cpu_backend.contains(key, pin=False):
|
|
462
|
+
continue
|
|
463
|
+
r_mem_indexes_to_read.append(r_mem_indexes[idx])
|
|
464
|
+
if not self.config.save_unfull_chunk or idx < keys_len - 1:
|
|
465
|
+
shapes = self.full_size_shapes
|
|
466
|
+
else:
|
|
467
|
+
shapes = self._get_unfull_chunk_shapes(offsets[idx])
|
|
468
|
+
local_mem_obj = self.local_cpu_backend.allocate(
|
|
469
|
+
shapes, self.dtypes, self.fmt
|
|
470
|
+
)
|
|
471
|
+
local_mem_objs.append(local_mem_obj)
|
|
472
|
+
keys_to_read.append(key)
|
|
473
|
+
|
|
474
|
+
channel_transfer_spec = {
|
|
475
|
+
"sender_id": sender_id,
|
|
476
|
+
"remote_indexes": r_mem_indexes_to_read,
|
|
477
|
+
}
|
|
478
|
+
await self.transfer_channel.async_batched_read(
|
|
479
|
+
buffers=local_mem_objs,
|
|
480
|
+
transfer_spec=channel_transfer_spec,
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
self.local_cpu_backend.batched_submit_put_task(
|
|
484
|
+
keys=keys_to_read,
|
|
485
|
+
memory_objs=local_mem_objs,
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
return BatchedLookupAndPutRetMsg(num_read_chunks=len(local_mem_objs))
|
|
489
|
+
except Exception as e:
|
|
490
|
+
logger.error(
|
|
491
|
+
"Error during P2P batched lookup and put operation: %s",
|
|
492
|
+
e,
|
|
493
|
+
exc_info=True,
|
|
494
|
+
)
|
|
495
|
+
return BatchedLookupAndPutRetMsg(num_read_chunks=0)
|
|
496
|
+
|
|
497
|
+
async def _ensure_peer_connection(
|
|
498
|
+
self,
|
|
499
|
+
target_peer_init_url: str,
|
|
500
|
+
force_update: bool = False,
|
|
501
|
+
) -> None:
|
|
502
|
+
if not force_update and target_peer_init_url in self.target_peer_info_mapping:
|
|
503
|
+
return
|
|
504
|
+
|
|
505
|
+
async with self.update_peer_lock:
|
|
506
|
+
# double check
|
|
507
|
+
if (
|
|
508
|
+
not force_update
|
|
509
|
+
and target_peer_init_url in self.target_peer_info_mapping
|
|
510
|
+
):
|
|
511
|
+
return
|
|
512
|
+
|
|
513
|
+
init_side_msg = P2PInitSideMsg()
|
|
514
|
+
init_ret_msg = await self.transfer_channel.async_lazy_init_peer_connection(
|
|
515
|
+
local_id=self.peer_init_url,
|
|
516
|
+
peer_id=target_peer_init_url,
|
|
517
|
+
peer_init_url=target_peer_init_url,
|
|
518
|
+
init_side_msg=init_side_msg,
|
|
519
|
+
)
|
|
520
|
+
assert isinstance(init_ret_msg, P2PInitSideRetMsg)
|
|
521
|
+
|
|
522
|
+
peer_lookup_url = init_ret_msg.peer_lookup_url
|
|
523
|
+
peer_info = self.target_peer_info_mapping.get(target_peer_init_url, None)
|
|
524
|
+
lookup_socket = get_zmq_socket_with_timeout(
|
|
525
|
+
self.async_context,
|
|
526
|
+
peer_lookup_url,
|
|
527
|
+
"tcp",
|
|
528
|
+
zmq.REQ,
|
|
529
|
+
"connect",
|
|
530
|
+
self.socket_recv_timeout_ms,
|
|
531
|
+
self.socket_send_timeout_ms,
|
|
532
|
+
)
|
|
533
|
+
if peer_info is not None:
|
|
534
|
+
peer_info.update_peer_lookup_url(peer_lookup_url)
|
|
535
|
+
peer_info.update_lookup_socket(lookup_socket)
|
|
536
|
+
else:
|
|
537
|
+
self.target_peer_info_mapping[target_peer_init_url] = PeerInfo(
|
|
538
|
+
peer_init_url=target_peer_init_url,
|
|
539
|
+
peer_lookup_url=peer_lookup_url,
|
|
540
|
+
lookup_lock=asyncio.Lock(),
|
|
541
|
+
lookup_socket=lookup_socket,
|
|
542
|
+
)
|
|
543
|
+
|
|
544
|
+
logger.info(
|
|
545
|
+
"Established connection to peer_init_url: %s, peer_lookup_url: %s",
|
|
546
|
+
target_peer_init_url,
|
|
547
|
+
peer_lookup_url,
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
async def batched_get_non_blocking(
|
|
551
|
+
self,
|
|
552
|
+
lookup_id: str,
|
|
553
|
+
keys: list[CacheEngineKey],
|
|
554
|
+
transfer_spec: Any = None,
|
|
555
|
+
) -> list[MemoryObj]:
|
|
556
|
+
target_peer_init_url, _ = self.lookup_id_to_peer_mapping.pop(lookup_id)
|
|
557
|
+
|
|
558
|
+
assert isinstance(transfer_spec, dict)
|
|
559
|
+
cum_chunk_lengths = transfer_spec.get("cum_chunk_lengths", None)
|
|
560
|
+
assert cum_chunk_lengths is not None, "cum_chunk_lengths must be provided"
|
|
561
|
+
assert isinstance(cum_chunk_lengths, list), "cum_chunk_lengths must be a list"
|
|
562
|
+
|
|
563
|
+
mem_objs = []
|
|
564
|
+
str_keys = []
|
|
565
|
+
keys_len = len(keys)
|
|
566
|
+
for idx, key in enumerate(keys):
|
|
567
|
+
if not self.config.save_unfull_chunk or idx < keys_len - 1:
|
|
568
|
+
shapes = self.full_size_shapes
|
|
569
|
+
else:
|
|
570
|
+
shapes = self._get_unfull_chunk_shapes(
|
|
571
|
+
cum_chunk_lengths[idx + 1] - cum_chunk_lengths[idx]
|
|
572
|
+
)
|
|
573
|
+
mem_obj = self.local_cpu_backend.allocate(shapes, self.dtypes, self.fmt)
|
|
574
|
+
mem_objs.append(mem_obj)
|
|
575
|
+
str_keys.append(key.to_string())
|
|
576
|
+
|
|
577
|
+
local_indexes = self.transfer_channel.get_local_mem_indices(mem_objs)
|
|
578
|
+
|
|
579
|
+
# NOTE(Jiayi): Tier 3 lookup is batched with retrieval.
|
|
580
|
+
msg = BatchedLookupAndGetMsg(
|
|
581
|
+
lookup_id=lookup_id,
|
|
582
|
+
receiver_id=self.peer_init_url,
|
|
583
|
+
keys=str_keys,
|
|
584
|
+
mem_indexes=local_indexes,
|
|
585
|
+
)
|
|
586
|
+
|
|
587
|
+
retry_count = 0
|
|
588
|
+
while retry_count < self.max_retry_count:
|
|
589
|
+
peer_info = self.target_peer_info_mapping[target_peer_init_url]
|
|
590
|
+
lookup_lock = peer_info.lookup_lock
|
|
591
|
+
async with lookup_lock:
|
|
592
|
+
lookup_socket = peer_info.lookup_socket
|
|
593
|
+
try:
|
|
594
|
+
retry_count += 1
|
|
595
|
+
await lookup_socket.send(msgspec.msgpack.encode(msg))
|
|
596
|
+
ret_msg_bytes = await lookup_socket.recv()
|
|
597
|
+
ret_msg = msgspec.msgpack.decode(ret_msg_bytes, type=P2PMsg)
|
|
598
|
+
if (
|
|
599
|
+
isinstance(ret_msg, P2PErrorMsg)
|
|
600
|
+
and ret_msg.error_code
|
|
601
|
+
== P2PErrorCode.REMOTE_XFER_HANDLER_NOT_INITIALIZED
|
|
602
|
+
):
|
|
603
|
+
logger.warning(
|
|
604
|
+
"Peer connection not initialized for lookup_id %s, "
|
|
605
|
+
"ensure peer connection first, retry count: %s",
|
|
606
|
+
lookup_id,
|
|
607
|
+
retry_count,
|
|
608
|
+
)
|
|
609
|
+
await self._ensure_peer_connection(target_peer_init_url, True)
|
|
610
|
+
else:
|
|
611
|
+
break
|
|
612
|
+
except zmq.ZMQError as e:
|
|
613
|
+
logger.error(
|
|
614
|
+
"ZMQ error occurred for lookup_id %s. Error: %s",
|
|
615
|
+
lookup_id,
|
|
616
|
+
e,
|
|
617
|
+
)
|
|
618
|
+
await self._ensure_peer_connection(target_peer_init_url, True)
|
|
619
|
+
if retry_count == self.max_retry_count:
|
|
620
|
+
logger.error(
|
|
621
|
+
"Max retry count reached for lookup_id %s",
|
|
622
|
+
lookup_id,
|
|
623
|
+
)
|
|
624
|
+
self._cleanup_memory_objects(mem_objs)
|
|
625
|
+
return []
|
|
626
|
+
except Exception as e:
|
|
627
|
+
logger.error(
|
|
628
|
+
"Error during P2P get operation for lookup_id %s: %s",
|
|
629
|
+
lookup_id,
|
|
630
|
+
e,
|
|
631
|
+
exc_info=True,
|
|
632
|
+
)
|
|
633
|
+
self._cleanup_memory_objects(mem_objs)
|
|
634
|
+
return []
|
|
635
|
+
|
|
636
|
+
if isinstance(ret_msg, P2PErrorMsg):
|
|
637
|
+
logger.error(
|
|
638
|
+
"P2P error for lookup_id %s, error code: %s",
|
|
639
|
+
lookup_id,
|
|
640
|
+
ret_msg.error_code,
|
|
641
|
+
)
|
|
642
|
+
num_hit_chunks = 0
|
|
643
|
+
else:
|
|
644
|
+
num_hit_chunks = ret_msg.num_hit_chunks
|
|
645
|
+
|
|
646
|
+
hit_mem_objs = mem_objs[:num_hit_chunks]
|
|
647
|
+
for hit_mem_obj in hit_mem_objs:
|
|
648
|
+
hit_mem_obj.pin()
|
|
649
|
+
for missed_mem_obj in mem_objs[num_hit_chunks:]:
|
|
650
|
+
missed_mem_obj.ref_count_down()
|
|
651
|
+
return hit_mem_objs
|
|
652
|
+
|
|
653
|
+
def _get_unfull_chunk_shapes(self, num_tokens: int) -> list[torch.Size]:
|
|
654
|
+
shapes = []
|
|
655
|
+
for shape in self.full_size_shapes:
|
|
656
|
+
shape_list = list(shape)
|
|
657
|
+
shape_list[self.fmt.token_dim()] = num_tokens
|
|
658
|
+
shapes.append(torch.Size(shape_list))
|
|
659
|
+
return shapes
|
|
660
|
+
|
|
661
|
+
# NOTE: put-related functions are not supported for now.
|
|
662
|
+
async def async_batched_submit_put_task(
|
|
663
|
+
self,
|
|
664
|
+
keys: Sequence[CacheEngineKey],
|
|
665
|
+
objs: List[MemoryObj],
|
|
666
|
+
transfer_spec: Any = None,
|
|
667
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
668
|
+
) -> None:
|
|
669
|
+
# TODO(baoloongmao): Add exception handling for socket operations
|
|
670
|
+
# Code path for `move` operation in controller.
|
|
671
|
+
assert isinstance(transfer_spec, dict)
|
|
672
|
+
assert "target_peer_init_url" in transfer_spec
|
|
673
|
+
assert "offsets" in transfer_spec
|
|
674
|
+
|
|
675
|
+
target_peer_init_url = transfer_spec["target_peer_init_url"]
|
|
676
|
+
offsets = transfer_spec["offsets"]
|
|
677
|
+
|
|
678
|
+
await self._ensure_peer_connection(transfer_spec["target_peer_init_url"])
|
|
679
|
+
|
|
680
|
+
str_keys = [key.to_string() for key in keys]
|
|
681
|
+
local_indexes = self.transfer_channel.get_local_mem_indices(objs)
|
|
682
|
+
|
|
683
|
+
msg = BatchedLookupAndPutMsg(
|
|
684
|
+
sender_id=self.peer_init_url,
|
|
685
|
+
keys=str_keys,
|
|
686
|
+
offsets=offsets,
|
|
687
|
+
mem_indexes=local_indexes,
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
peer_info = self.target_peer_info_mapping[target_peer_init_url]
|
|
691
|
+
lookup_lock = peer_info.lookup_lock
|
|
692
|
+
async with lookup_lock:
|
|
693
|
+
lookup_socket = peer_info.lookup_socket
|
|
694
|
+
await lookup_socket.send(msgspec.msgpack.encode(msg))
|
|
695
|
+
ret_msg_bytes = await lookup_socket.recv()
|
|
696
|
+
ret_msg = msgspec.msgpack.decode(ret_msg_bytes, type=P2PMsg)
|
|
697
|
+
|
|
698
|
+
return ret_msg.num_read_chunks
|
|
699
|
+
|
|
700
|
+
def get_allocator_backend(self):
|
|
701
|
+
return self.local_cpu_backend
|
|
702
|
+
|
|
703
|
+
def _cleanup_memory_objects(self, mem_objs: list[MemoryObj]) -> None:
|
|
704
|
+
"""Safely cleanup memory objects by decrementing reference counts"""
|
|
705
|
+
for mem_obj in mem_objs:
|
|
706
|
+
try:
|
|
707
|
+
mem_obj.ref_count_down()
|
|
708
|
+
except Exception as e:
|
|
709
|
+
logger.error("Error cleaning up memory object: %s", e)
|
|
710
|
+
|
|
711
|
+
def close(
|
|
712
|
+
self,
|
|
713
|
+
) -> None:
|
|
714
|
+
"""
|
|
715
|
+
Close the P2P backend and cleanup resources.
|
|
716
|
+
"""
|
|
717
|
+
logger.info("Closing P2P backend")
|
|
718
|
+
self.running.clear()
|
|
719
|
+
|
|
720
|
+
# Close all lookup sockets
|
|
721
|
+
for peer_info in self.target_peer_info_mapping.values():
|
|
722
|
+
try:
|
|
723
|
+
peer_info.lookup_socket.close(linger=0)
|
|
724
|
+
except Exception as e:
|
|
725
|
+
logger.warning("Failed to close lookup socket: %s", e)
|
|
726
|
+
self.target_peer_info_mapping.clear()
|
|
727
|
+
|
|
728
|
+
# Close async peer socket
|
|
729
|
+
if self.async_peer_socket is not None:
|
|
730
|
+
try:
|
|
731
|
+
self.async_peer_socket.close(linger=0)
|
|
732
|
+
except Exception as e:
|
|
733
|
+
logger.warning("Failed to close async peer socket: %s", e)
|
|
734
|
+
self.async_peer_socket = None
|
|
735
|
+
|
|
736
|
+
# Close transfer channel
|
|
737
|
+
if hasattr(self, "transfer_channel") and self.transfer_channel is not None:
|
|
738
|
+
try:
|
|
739
|
+
self.transfer_channel.close()
|
|
740
|
+
except Exception as e:
|
|
741
|
+
logger.warning("Failed to close transfer channel: %s", e)
|
|
742
|
+
|
|
743
|
+
############################################################
|
|
744
|
+
# Not-supported functions
|
|
745
|
+
############################################################
|
|
746
|
+
|
|
747
|
+
# NOTE: synchronous contain is not supported for now.
|
|
748
|
+
def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
|
|
749
|
+
return False
|
|
750
|
+
|
|
751
|
+
# NOTE: put-related functions are not supported for now.
|
|
752
|
+
def exists_in_put_tasks(self, key: CacheEngineKey) -> bool:
|
|
753
|
+
raise NotImplementedError
|
|
754
|
+
|
|
755
|
+
def batched_submit_put_task(
|
|
756
|
+
self,
|
|
757
|
+
keys: Sequence[CacheEngineKey],
|
|
758
|
+
objs: List[MemoryObj],
|
|
759
|
+
transfer_spec: Any = None,
|
|
760
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
761
|
+
) -> None:
|
|
762
|
+
"""P2P backend does not support put operations."""
|
|
763
|
+
pass
|
|
764
|
+
|
|
765
|
+
# NOTE: Synchronous get is not supported for now.
|
|
766
|
+
def get_blocking(
|
|
767
|
+
self,
|
|
768
|
+
key: CacheEngineKey,
|
|
769
|
+
) -> Optional[MemoryObj]:
|
|
770
|
+
raise NotImplementedError
|
|
771
|
+
|
|
772
|
+
# NOTE: pin is useless for P2P backend now.
|
|
773
|
+
def pin(
|
|
774
|
+
self,
|
|
775
|
+
key: CacheEngineKey,
|
|
776
|
+
) -> bool:
|
|
777
|
+
return False
|
|
778
|
+
|
|
779
|
+
# NOTE: unpin is useless for P2P backend now.
|
|
780
|
+
def unpin(
|
|
781
|
+
self,
|
|
782
|
+
key: CacheEngineKey,
|
|
783
|
+
) -> bool:
|
|
784
|
+
return False
|
|
785
|
+
|
|
786
|
+
# NOTE: remove is useless for P2P backend now.
|
|
787
|
+
def remove(self, key: CacheEngineKey, force: bool = True) -> bool:
|
|
788
|
+
return False
|