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,734 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# Standard
|
|
4
|
+
from concurrent.futures import Future
|
|
5
|
+
from typing import Any, Callable, List, Optional, Sequence, Union
|
|
6
|
+
import asyncio
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
# Third Party
|
|
11
|
+
from maru import MaruConfig, MaruHandler
|
|
12
|
+
from maru_lmcache import CxlMemoryAdapter
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
# First Party
|
|
16
|
+
from lmcache.integration.vllm.utils import get_size_bytes
|
|
17
|
+
from lmcache.logging import init_logger
|
|
18
|
+
from lmcache.utils import CacheEngineKey
|
|
19
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
20
|
+
from lmcache.v1.memory_management import (
|
|
21
|
+
MemoryAllocatorInterface,
|
|
22
|
+
MemoryFormat,
|
|
23
|
+
MemoryObj,
|
|
24
|
+
)
|
|
25
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
26
|
+
from lmcache.v1.storage_backend.abstract_backend import AllocatorBackendInterface
|
|
27
|
+
|
|
28
|
+
logger = init_logger(__name__)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class MaruBackend(AllocatorBackendInterface):
|
|
32
|
+
"""Maru CXL shared memory storage backend.
|
|
33
|
+
|
|
34
|
+
Implements AllocatorBackendInterface with its own CxlMemoryAdapter.
|
|
35
|
+
No LocalCPUBackend needed — data lives directly in CXL mmap memory.
|
|
36
|
+
|
|
37
|
+
Put is async (Future): metadata registration via RPC.
|
|
38
|
+
Get is sync: CXL memory direct read (no network I/O).
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
config: LMCache engine configuration. Must have maru_path set.
|
|
42
|
+
metadata: LMCache engine metadata.
|
|
43
|
+
loop: asyncio event loop for async put tasks.
|
|
44
|
+
dst_device: Target device string (unused for CXL, kept for interface).
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(
|
|
48
|
+
self,
|
|
49
|
+
config: LMCacheEngineConfig,
|
|
50
|
+
metadata: LMCacheMetadata,
|
|
51
|
+
loop: asyncio.AbstractEventLoop,
|
|
52
|
+
dst_device: str = "cuda",
|
|
53
|
+
):
|
|
54
|
+
super().__init__(dst_device=dst_device)
|
|
55
|
+
|
|
56
|
+
if config.use_layerwise:
|
|
57
|
+
raise NotImplementedError(
|
|
58
|
+
"MaruBackend does not yet support layerwise KV cache."
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
# 1. Config
|
|
62
|
+
self.config = config
|
|
63
|
+
self.loop = loop
|
|
64
|
+
|
|
65
|
+
self._full_chunk_size_bytes: int = get_size_bytes(
|
|
66
|
+
metadata.get_shapes(), metadata.get_dtypes()
|
|
67
|
+
)
|
|
68
|
+
assert self._full_chunk_size_bytes % metadata.chunk_size == 0
|
|
69
|
+
self._single_token_size: int = (
|
|
70
|
+
self._full_chunk_size_bytes // metadata.chunk_size
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self._mla_worker_id_as0_mode: bool = (
|
|
74
|
+
config.get_extra_config_value(
|
|
75
|
+
"remote_enable_mla_worker_id_as0", metadata.use_mla
|
|
76
|
+
)
|
|
77
|
+
and metadata.use_mla
|
|
78
|
+
and metadata.world_size > 1
|
|
79
|
+
and metadata.worker_id != 0
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
# 2. Handler
|
|
83
|
+
self._handler = self._create_handler(config)
|
|
84
|
+
|
|
85
|
+
# 3. Allocator
|
|
86
|
+
self.memory_allocator = self.initialize_allocator(config, metadata)
|
|
87
|
+
|
|
88
|
+
# 4. State
|
|
89
|
+
self.put_lock = threading.Lock()
|
|
90
|
+
self.put_tasks: set[CacheEngineKey] = set()
|
|
91
|
+
|
|
92
|
+
def __str__(self) -> str:
|
|
93
|
+
return self.__class__.__name__
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def _pool_size_gb_to_bytes(size_gb: float) -> int:
|
|
97
|
+
"""Convert pool size in GB to bytes."""
|
|
98
|
+
return int(size_gb * 1024**3)
|
|
99
|
+
|
|
100
|
+
# =========================================================================
|
|
101
|
+
# Initialization helpers
|
|
102
|
+
# =========================================================================
|
|
103
|
+
|
|
104
|
+
def _create_handler(
|
|
105
|
+
self,
|
|
106
|
+
config: LMCacheEngineConfig,
|
|
107
|
+
) -> "MaruHandler":
|
|
108
|
+
"""Create and connect a MaruHandler.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
config: LMCache engine configuration.
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Connected MaruHandler instance.
|
|
115
|
+
|
|
116
|
+
Raises:
|
|
117
|
+
RuntimeError: If MaruHandler connection fails.
|
|
118
|
+
"""
|
|
119
|
+
assert config.maru_path is not None, "maru_path must be set for MaruBackend"
|
|
120
|
+
|
|
121
|
+
# Convert maru:// scheme to tcp:// for ZMQ
|
|
122
|
+
server_url = config.maru_path
|
|
123
|
+
if server_url.startswith("maru://"):
|
|
124
|
+
server_url = "tcp://" + server_url[len("maru://") :]
|
|
125
|
+
|
|
126
|
+
extra = config.extra_config or {}
|
|
127
|
+
maru_config = MaruConfig(
|
|
128
|
+
server_url=server_url,
|
|
129
|
+
instance_id=extra.get("maru_instance_id"),
|
|
130
|
+
pool_size=self._pool_size_gb_to_bytes(config.maru_pool_size),
|
|
131
|
+
chunk_size_bytes=self._full_chunk_size_bytes,
|
|
132
|
+
auto_connect=False,
|
|
133
|
+
timeout_ms=extra.get("maru_timeout_ms", 5000),
|
|
134
|
+
use_async_rpc=extra.get("maru_use_async_rpc", True),
|
|
135
|
+
max_inflight=extra.get("maru_max_inflight", 64),
|
|
136
|
+
eager_map=extra.get("maru_eager_map", True),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
handler = MaruHandler(maru_config)
|
|
140
|
+
if not handler.connect():
|
|
141
|
+
raise RuntimeError(f"Failed to connect MaruHandler to {config.maru_path}")
|
|
142
|
+
logger.debug("[Maru] Connected to %s", config.maru_path)
|
|
143
|
+
return handler
|
|
144
|
+
|
|
145
|
+
# =========================================================================
|
|
146
|
+
# AllocatorBackendInterface
|
|
147
|
+
# =========================================================================
|
|
148
|
+
|
|
149
|
+
def initialize_allocator(
|
|
150
|
+
self, config: LMCacheEngineConfig, metadata: LMCacheMetadata
|
|
151
|
+
) -> MemoryAllocatorInterface:
|
|
152
|
+
"""Create CxlMemoryAdapter backed by the connected handler.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
config: LMCache engine configuration.
|
|
156
|
+
metadata: LMCache engine metadata.
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
CxlMemoryAdapter instance.
|
|
160
|
+
"""
|
|
161
|
+
shapes = metadata.get_shapes()
|
|
162
|
+
dtypes = metadata.get_dtypes()
|
|
163
|
+
fmt = MemoryFormat.KV_MLA_FMT if metadata.use_mla else MemoryFormat.KV_2LTD
|
|
164
|
+
chunk_size = self._handler.get_chunk_size()
|
|
165
|
+
|
|
166
|
+
return CxlMemoryAdapter(
|
|
167
|
+
handler=self._handler,
|
|
168
|
+
shapes=shapes,
|
|
169
|
+
dtypes=dtypes,
|
|
170
|
+
fmt=fmt,
|
|
171
|
+
chunk_size=chunk_size,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
def get_memory_allocator(self) -> MemoryAllocatorInterface:
|
|
175
|
+
"""Returns the underlying CxlMemoryAdapter."""
|
|
176
|
+
return self.memory_allocator
|
|
177
|
+
|
|
178
|
+
def get_allocator_backend(self) -> "MaruBackend":
|
|
179
|
+
"""Returns self as the allocator backend."""
|
|
180
|
+
return self
|
|
181
|
+
|
|
182
|
+
def allocate(
|
|
183
|
+
self,
|
|
184
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
185
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
186
|
+
fmt: MemoryFormat = MemoryFormat.KV_2LTD,
|
|
187
|
+
eviction: bool = True,
|
|
188
|
+
busy_loop: bool = True,
|
|
189
|
+
) -> Optional[MemoryObj]:
|
|
190
|
+
"""Allocate CXL-backed memory via CxlMemoryAdapter.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
shapes: Tensor shape(s).
|
|
194
|
+
dtypes: Tensor dtype(s).
|
|
195
|
+
fmt: Memory format.
|
|
196
|
+
eviction: Unused.
|
|
197
|
+
busy_loop: Unused.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
MemoryObj backed by CXL memory, or None on failure.
|
|
201
|
+
"""
|
|
202
|
+
obj = self.memory_allocator.allocate(shapes, dtypes, fmt)
|
|
203
|
+
if obj is not None:
|
|
204
|
+
logger.debug(
|
|
205
|
+
"[Maru] allocate rid=%d pid=%d",
|
|
206
|
+
*CxlMemoryAdapter.decode_address(obj.metadata.address),
|
|
207
|
+
)
|
|
208
|
+
else:
|
|
209
|
+
logger.debug("[Maru] allocate failed shapes=%s dtypes=%s", shapes, dtypes)
|
|
210
|
+
return obj
|
|
211
|
+
|
|
212
|
+
def batched_allocate(
|
|
213
|
+
self,
|
|
214
|
+
shapes: Union[torch.Size, list[torch.Size]],
|
|
215
|
+
dtypes: Union[torch.dtype, list[torch.dtype]],
|
|
216
|
+
batch_size: int,
|
|
217
|
+
fmt: MemoryFormat = MemoryFormat.KV_2LTD,
|
|
218
|
+
eviction: bool = True,
|
|
219
|
+
busy_loop: bool = True,
|
|
220
|
+
) -> Optional[list[MemoryObj]]:
|
|
221
|
+
"""Allocate multiple CXL-backed MemoryObjs.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
shapes: Tensor shape(s) (same for each allocation).
|
|
225
|
+
dtypes: Tensor dtype(s) (same for each allocation).
|
|
226
|
+
batch_size: Number of allocations.
|
|
227
|
+
fmt: Memory format.
|
|
228
|
+
eviction: Unused.
|
|
229
|
+
busy_loop: Unused.
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
List of MemoryObj, or None if any allocation fails.
|
|
233
|
+
"""
|
|
234
|
+
return self.memory_allocator.batched_allocate(shapes, dtypes, batch_size, fmt)
|
|
235
|
+
|
|
236
|
+
# =========================================================================
|
|
237
|
+
# Put (async)
|
|
238
|
+
# =========================================================================
|
|
239
|
+
|
|
240
|
+
def exists_in_put_tasks(self, key: CacheEngineKey) -> bool:
|
|
241
|
+
"""Check whether key is in ongoing put tasks.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
key: The cache key.
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
True if the key has a pending put task.
|
|
248
|
+
"""
|
|
249
|
+
with self.put_lock:
|
|
250
|
+
return key in self.put_tasks
|
|
251
|
+
|
|
252
|
+
@staticmethod
|
|
253
|
+
def _create_immediate_empty_future() -> Future:
|
|
254
|
+
"""Create a Future that is already resolved with None."""
|
|
255
|
+
f: Future = Future()
|
|
256
|
+
f.set_result(None)
|
|
257
|
+
return f
|
|
258
|
+
|
|
259
|
+
def submit_put_task(
|
|
260
|
+
self,
|
|
261
|
+
key: CacheEngineKey,
|
|
262
|
+
memory_obj: MemoryObj,
|
|
263
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
264
|
+
) -> Future:
|
|
265
|
+
"""Submit a put task to register KV metadata with MaruServer.
|
|
266
|
+
|
|
267
|
+
Data is already in CXL memory (zero-copy). This only registers
|
|
268
|
+
the key -> location metadata via RPC.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
key: The cache key.
|
|
272
|
+
memory_obj: MemoryObj with data already written to CXL.
|
|
273
|
+
on_complete_callback: Optional callback after registration.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
Future that completes when metadata is registered.
|
|
277
|
+
"""
|
|
278
|
+
# If MLA worker id as 0 mode is enabled, skip put tasks
|
|
279
|
+
if self._mla_worker_id_as0_mode:
|
|
280
|
+
return self._create_immediate_empty_future()
|
|
281
|
+
|
|
282
|
+
assert memory_obj.tensor is not None
|
|
283
|
+
|
|
284
|
+
# Keep CXL page alive: ref_count_down is only called on failure.
|
|
285
|
+
# On success the ref is retained so the CXL memory is not reclaimed.
|
|
286
|
+
memory_obj.ref_count_up()
|
|
287
|
+
|
|
288
|
+
with self.put_lock:
|
|
289
|
+
self.put_tasks.add(key)
|
|
290
|
+
|
|
291
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
292
|
+
self._async_store(key, memory_obj, on_complete_callback),
|
|
293
|
+
self.loop,
|
|
294
|
+
)
|
|
295
|
+
return future
|
|
296
|
+
|
|
297
|
+
def batched_submit_put_task(
|
|
298
|
+
self,
|
|
299
|
+
keys: Sequence[CacheEngineKey],
|
|
300
|
+
memory_objs: List[MemoryObj],
|
|
301
|
+
transfer_spec: Any = None,
|
|
302
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
303
|
+
) -> Union[List[Future], None]:
|
|
304
|
+
"""Submit batched put tasks via single batch_store RPC.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
keys: The cache keys.
|
|
308
|
+
memory_objs: MemoryObjs with data already in CXL.
|
|
309
|
+
transfer_spec: Unused.
|
|
310
|
+
on_complete_callback: Optional per-key callback.
|
|
311
|
+
|
|
312
|
+
Returns:
|
|
313
|
+
List containing a single Future for the entire batch.
|
|
314
|
+
"""
|
|
315
|
+
# If MLA worker id as 0 mode is enabled, skip put tasks
|
|
316
|
+
if self._mla_worker_id_as0_mode:
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
for memory_obj in memory_objs:
|
|
320
|
+
assert memory_obj.tensor is not None
|
|
321
|
+
memory_obj.ref_count_up()
|
|
322
|
+
|
|
323
|
+
with self.put_lock:
|
|
324
|
+
self.put_tasks.update(keys)
|
|
325
|
+
|
|
326
|
+
future = asyncio.run_coroutine_threadsafe(
|
|
327
|
+
self._async_batch_store(list(keys), memory_objs, on_complete_callback),
|
|
328
|
+
self.loop,
|
|
329
|
+
)
|
|
330
|
+
return [future]
|
|
331
|
+
|
|
332
|
+
async def _async_store(
|
|
333
|
+
self,
|
|
334
|
+
key: CacheEngineKey,
|
|
335
|
+
memory_obj: MemoryObj,
|
|
336
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
337
|
+
) -> None:
|
|
338
|
+
"""Register KV metadata with MaruServer (runs in event loop).
|
|
339
|
+
|
|
340
|
+
Uses CxlMemoryAdapter.create_store_handle() to extract
|
|
341
|
+
(region_id, page_index) from the MemoryObj's encoded address.
|
|
342
|
+
|
|
343
|
+
Args:
|
|
344
|
+
key: The cache key.
|
|
345
|
+
memory_obj: MemoryObj backed by CXL memory.
|
|
346
|
+
on_complete_callback: Optional callback after registration.
|
|
347
|
+
"""
|
|
348
|
+
success = False
|
|
349
|
+
try:
|
|
350
|
+
allocator = self.memory_allocator
|
|
351
|
+
assert isinstance(allocator, CxlMemoryAdapter)
|
|
352
|
+
handle = allocator.create_store_handle(memory_obj)
|
|
353
|
+
key_str = key.to_string()
|
|
354
|
+
|
|
355
|
+
success = await asyncio.to_thread(self._handler.store, key_str, handle)
|
|
356
|
+
|
|
357
|
+
logger.debug(
|
|
358
|
+
"[Maru] store key=%s rid=%d pid=%d",
|
|
359
|
+
key,
|
|
360
|
+
handle.region_id,
|
|
361
|
+
handle.page_index,
|
|
362
|
+
)
|
|
363
|
+
|
|
364
|
+
except Exception as e:
|
|
365
|
+
logger.error("[Maru] store failed key=%s: %s", key, e)
|
|
366
|
+
raise
|
|
367
|
+
finally:
|
|
368
|
+
with self.put_lock:
|
|
369
|
+
self.put_tasks.discard(key)
|
|
370
|
+
|
|
371
|
+
if not success:
|
|
372
|
+
memory_obj.ref_count_down()
|
|
373
|
+
|
|
374
|
+
if success and on_complete_callback is not None:
|
|
375
|
+
try:
|
|
376
|
+
on_complete_callback(key)
|
|
377
|
+
except Exception as e:
|
|
378
|
+
logger.warning("on_complete_callback failed for key %s: %s", key, e)
|
|
379
|
+
|
|
380
|
+
async def _async_batch_store(
|
|
381
|
+
self,
|
|
382
|
+
keys: List[CacheEngineKey],
|
|
383
|
+
memory_objs: List[MemoryObj],
|
|
384
|
+
on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
|
|
385
|
+
) -> None:
|
|
386
|
+
"""Register multiple KV metadata entries via single batch_store RPC."""
|
|
387
|
+
results: Optional[list[bool]] = None
|
|
388
|
+
try:
|
|
389
|
+
allocator = self.memory_allocator
|
|
390
|
+
assert isinstance(allocator, CxlMemoryAdapter)
|
|
391
|
+
|
|
392
|
+
key_strs = [k.to_string() for k in keys]
|
|
393
|
+
handles = [allocator.create_store_handle(m) for m in memory_objs]
|
|
394
|
+
|
|
395
|
+
results = await asyncio.to_thread(
|
|
396
|
+
self._handler.batch_store, key_strs, handles
|
|
397
|
+
)
|
|
398
|
+
if results is not None:
|
|
399
|
+
logger.debug("[Maru] batch_store %d/%d ok", sum(results), len(results))
|
|
400
|
+
except Exception as e:
|
|
401
|
+
logger.error("[Maru] batch_store failed: %s", e)
|
|
402
|
+
raise
|
|
403
|
+
finally:
|
|
404
|
+
with self.put_lock:
|
|
405
|
+
self.put_tasks.difference_update(keys)
|
|
406
|
+
|
|
407
|
+
# Release ref_count for failed stores
|
|
408
|
+
for i, memory_obj in enumerate(memory_objs):
|
|
409
|
+
succeeded = results is not None and i < len(results) and results[i]
|
|
410
|
+
if not succeeded:
|
|
411
|
+
memory_obj.ref_count_down()
|
|
412
|
+
|
|
413
|
+
if on_complete_callback is not None:
|
|
414
|
+
for i, key in enumerate(keys):
|
|
415
|
+
if results is not None and i < len(results) and results[i]:
|
|
416
|
+
try:
|
|
417
|
+
on_complete_callback(key)
|
|
418
|
+
except Exception as e:
|
|
419
|
+
logger.warning(
|
|
420
|
+
"on_complete_callback failed for key %s: %s",
|
|
421
|
+
key,
|
|
422
|
+
e,
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
# =========================================================================
|
|
426
|
+
# Get (sync)
|
|
427
|
+
# =========================================================================
|
|
428
|
+
|
|
429
|
+
def get_blocking(
|
|
430
|
+
self,
|
|
431
|
+
key: CacheEngineKey,
|
|
432
|
+
) -> Optional[MemoryObj]:
|
|
433
|
+
"""Blocking get: read KV cache directly from CXL memory.
|
|
434
|
+
|
|
435
|
+
Queries MaruServer for metadata, then returns a MemoryObj
|
|
436
|
+
via CxlMemoryAdapter.get_by_location().
|
|
437
|
+
|
|
438
|
+
Args:
|
|
439
|
+
key: The cache key.
|
|
440
|
+
|
|
441
|
+
Returns:
|
|
442
|
+
MemoryObj backed by CXL memory, or None if not found.
|
|
443
|
+
"""
|
|
444
|
+
if self._mla_worker_id_as0_mode:
|
|
445
|
+
key = key.with_new_worker_id(0)
|
|
446
|
+
|
|
447
|
+
key_str = key.to_string()
|
|
448
|
+
mem_info = self._handler.retrieve(key_str)
|
|
449
|
+
if mem_info is None:
|
|
450
|
+
logger.debug("[Maru] get_blocking miss key=%s", key)
|
|
451
|
+
return None
|
|
452
|
+
|
|
453
|
+
allocator = self.memory_allocator
|
|
454
|
+
assert isinstance(allocator, CxlMemoryAdapter)
|
|
455
|
+
|
|
456
|
+
memory_obj = allocator.get_by_location(
|
|
457
|
+
region_id=mem_info.region_id,
|
|
458
|
+
page_index=mem_info.page_index,
|
|
459
|
+
actual_size=len(mem_info.view),
|
|
460
|
+
single_token_size=self._single_token_size,
|
|
461
|
+
)
|
|
462
|
+
if memory_obj is None:
|
|
463
|
+
logger.debug(
|
|
464
|
+
"[Maru] get_blocking pool miss rid=%d pid=%d",
|
|
465
|
+
mem_info.region_id,
|
|
466
|
+
mem_info.page_index,
|
|
467
|
+
)
|
|
468
|
+
return None
|
|
469
|
+
|
|
470
|
+
memory_obj.ref_count_up()
|
|
471
|
+
|
|
472
|
+
logger.debug(
|
|
473
|
+
"[Maru] get_blocking rid=%d pid=%d size=%d",
|
|
474
|
+
mem_info.region_id,
|
|
475
|
+
mem_info.page_index,
|
|
476
|
+
len(mem_info.view),
|
|
477
|
+
)
|
|
478
|
+
return memory_obj
|
|
479
|
+
|
|
480
|
+
def batched_get_blocking(
|
|
481
|
+
self,
|
|
482
|
+
keys: List[CacheEngineKey],
|
|
483
|
+
) -> List[Optional[MemoryObj]]:
|
|
484
|
+
"""Blocking batched get via single batch_retrieve RPC.
|
|
485
|
+
|
|
486
|
+
Args:
|
|
487
|
+
keys: The cache keys.
|
|
488
|
+
|
|
489
|
+
Returns:
|
|
490
|
+
List of MemoryObj (None for misses).
|
|
491
|
+
"""
|
|
492
|
+
if self._mla_worker_id_as0_mode:
|
|
493
|
+
keys = [k.with_new_worker_id(0) for k in keys]
|
|
494
|
+
|
|
495
|
+
key_strs = [k.to_string() for k in keys]
|
|
496
|
+
mem_infos = self._handler.batch_retrieve(key_strs)
|
|
497
|
+
|
|
498
|
+
allocator = self.memory_allocator
|
|
499
|
+
assert isinstance(allocator, CxlMemoryAdapter)
|
|
500
|
+
|
|
501
|
+
results: List[Optional[MemoryObj]] = []
|
|
502
|
+
for mem_info in mem_infos:
|
|
503
|
+
if mem_info is None:
|
|
504
|
+
results.append(None)
|
|
505
|
+
continue
|
|
506
|
+
memory_obj = allocator.get_by_location(
|
|
507
|
+
region_id=mem_info.region_id,
|
|
508
|
+
page_index=mem_info.page_index,
|
|
509
|
+
actual_size=len(mem_info.view),
|
|
510
|
+
single_token_size=self._single_token_size,
|
|
511
|
+
)
|
|
512
|
+
if memory_obj is None:
|
|
513
|
+
results.append(None)
|
|
514
|
+
continue
|
|
515
|
+
memory_obj.ref_count_up()
|
|
516
|
+
results.append(memory_obj)
|
|
517
|
+
|
|
518
|
+
hits = sum(1 for r in results if r is not None)
|
|
519
|
+
logger.debug("[Maru] batch_retrieve %d/%d hits", hits, len(results))
|
|
520
|
+
return results
|
|
521
|
+
|
|
522
|
+
# =========================================================================
|
|
523
|
+
# Async lookup API (used by StorageManager.async_lookup_and_prefetch)
|
|
524
|
+
# =========================================================================
|
|
525
|
+
|
|
526
|
+
async def batched_async_contains(
|
|
527
|
+
self,
|
|
528
|
+
lookup_id: str,
|
|
529
|
+
keys: List[CacheEngineKey],
|
|
530
|
+
pin: bool = False,
|
|
531
|
+
) -> int:
|
|
532
|
+
"""Check how many prefix keys exist via single batch_exists RPC.
|
|
533
|
+
|
|
534
|
+
Returns the count of contiguous keys starting from index 0
|
|
535
|
+
that exist. Stops at first miss.
|
|
536
|
+
|
|
537
|
+
Args:
|
|
538
|
+
lookup_id: Unique request identifier.
|
|
539
|
+
keys: Keys to check in prefix order.
|
|
540
|
+
pin: If True, atomically check and pin via batch_pin RPC.
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
Number of prefix-contiguous keys that exist.
|
|
544
|
+
"""
|
|
545
|
+
return await asyncio.to_thread(self.batched_contains, keys, pin)
|
|
546
|
+
|
|
547
|
+
async def batched_get_non_blocking(
|
|
548
|
+
self,
|
|
549
|
+
lookup_id: str,
|
|
550
|
+
keys: list[CacheEngineKey],
|
|
551
|
+
transfer_spec: Any = None,
|
|
552
|
+
) -> list[MemoryObj]:
|
|
553
|
+
"""Non-blocking batched get via single batch_retrieve RPC.
|
|
554
|
+
|
|
555
|
+
Uses handler.batch_retrieve() for a single RPC call, then
|
|
556
|
+
resolves each MemoryInfo to a MemoryObj via CxlMemoryAdapter.
|
|
557
|
+
Stops at first miss and returns the prefix.
|
|
558
|
+
|
|
559
|
+
Args:
|
|
560
|
+
lookup_id: Unique request identifier.
|
|
561
|
+
keys: Keys to retrieve (already confirmed by contains).
|
|
562
|
+
transfer_spec: Unused.
|
|
563
|
+
|
|
564
|
+
Returns:
|
|
565
|
+
List of MemoryObjs backed by CXL memory.
|
|
566
|
+
"""
|
|
567
|
+
|
|
568
|
+
def _batch_get() -> list[MemoryObj]:
|
|
569
|
+
if self._mla_worker_id_as0_mode:
|
|
570
|
+
actual_keys = [k.with_new_worker_id(0) for k in keys]
|
|
571
|
+
else:
|
|
572
|
+
actual_keys = list(keys)
|
|
573
|
+
|
|
574
|
+
key_strs = [k.to_string() for k in actual_keys]
|
|
575
|
+
mem_infos = self._handler.batch_retrieve(key_strs)
|
|
576
|
+
|
|
577
|
+
allocator = self.memory_allocator
|
|
578
|
+
assert isinstance(allocator, CxlMemoryAdapter)
|
|
579
|
+
|
|
580
|
+
results: list[MemoryObj] = []
|
|
581
|
+
for mem_info in mem_infos:
|
|
582
|
+
if mem_info is None:
|
|
583
|
+
break
|
|
584
|
+
memory_obj = allocator.get_by_location(
|
|
585
|
+
region_id=mem_info.region_id,
|
|
586
|
+
page_index=mem_info.page_index,
|
|
587
|
+
actual_size=len(mem_info.view),
|
|
588
|
+
single_token_size=self._single_token_size,
|
|
589
|
+
)
|
|
590
|
+
if memory_obj is None:
|
|
591
|
+
break
|
|
592
|
+
memory_obj.ref_count_up()
|
|
593
|
+
memory_obj.pin()
|
|
594
|
+
results.append(memory_obj)
|
|
595
|
+
|
|
596
|
+
logger.debug(
|
|
597
|
+
"[Maru] batch_get_non_blocking %d/%d hits", len(results), len(keys)
|
|
598
|
+
)
|
|
599
|
+
return results
|
|
600
|
+
|
|
601
|
+
return await asyncio.to_thread(_batch_get)
|
|
602
|
+
|
|
603
|
+
# =========================================================================
|
|
604
|
+
# Contains / Pin / Unpin / Remove
|
|
605
|
+
# =========================================================================
|
|
606
|
+
|
|
607
|
+
def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
|
|
608
|
+
"""Check if key exists on MaruServer.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
key: The cache key.
|
|
612
|
+
pin: If True, atomically check existence and pin the entry
|
|
613
|
+
to protect it from eviction.
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
True if key exists.
|
|
617
|
+
"""
|
|
618
|
+
if self._mla_worker_id_as0_mode:
|
|
619
|
+
key = key.with_new_worker_id(0)
|
|
620
|
+
|
|
621
|
+
key_str = key.to_string()
|
|
622
|
+
if pin:
|
|
623
|
+
return self._handler.pin(key_str)
|
|
624
|
+
return self._handler.exists(key_str)
|
|
625
|
+
|
|
626
|
+
def batched_contains(
|
|
627
|
+
self,
|
|
628
|
+
keys: List[CacheEngineKey],
|
|
629
|
+
pin: bool = False,
|
|
630
|
+
) -> int:
|
|
631
|
+
"""Check how many prefix keys exist via single batch_exists RPC.
|
|
632
|
+
|
|
633
|
+
Args:
|
|
634
|
+
keys: Keys to check in prefix order.
|
|
635
|
+
pin: If True, atomically check and pin via
|
|
636
|
+
batch_pin RPC.
|
|
637
|
+
|
|
638
|
+
Returns:
|
|
639
|
+
Number of prefix-contiguous keys that exist.
|
|
640
|
+
"""
|
|
641
|
+
if self._mla_worker_id_as0_mode:
|
|
642
|
+
keys = [k.with_new_worker_id(0) for k in keys]
|
|
643
|
+
|
|
644
|
+
key_strs = [k.to_string() for k in keys]
|
|
645
|
+
if pin:
|
|
646
|
+
results = self._handler.batch_pin(key_strs)
|
|
647
|
+
else:
|
|
648
|
+
results = self._handler.batch_exists(key_strs)
|
|
649
|
+
num_hit = 0
|
|
650
|
+
for exists in results:
|
|
651
|
+
if not exists:
|
|
652
|
+
break
|
|
653
|
+
num_hit += 1
|
|
654
|
+
return num_hit
|
|
655
|
+
|
|
656
|
+
def pin(self, key: CacheEngineKey) -> bool:
|
|
657
|
+
"""Pin a key to prevent eviction on MaruServer.
|
|
658
|
+
|
|
659
|
+
Increments the server-side pin_count.
|
|
660
|
+
|
|
661
|
+
Args:
|
|
662
|
+
key: The cache key.
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
True if pinned successfully.
|
|
666
|
+
"""
|
|
667
|
+
if self._mla_worker_id_as0_mode:
|
|
668
|
+
key = key.with_new_worker_id(0)
|
|
669
|
+
return self._handler.pin(key.to_string())
|
|
670
|
+
|
|
671
|
+
def unpin(self, key: CacheEngineKey) -> bool:
|
|
672
|
+
"""Unpin a key to allow eviction on MaruServer.
|
|
673
|
+
|
|
674
|
+
Decrements the server-side pin_count. When pin_count reaches 0,
|
|
675
|
+
the entry becomes eligible for eviction.
|
|
676
|
+
|
|
677
|
+
Args:
|
|
678
|
+
key: The cache key.
|
|
679
|
+
|
|
680
|
+
Returns:
|
|
681
|
+
True if unpinned successfully.
|
|
682
|
+
"""
|
|
683
|
+
if self._mla_worker_id_as0_mode:
|
|
684
|
+
key = key.with_new_worker_id(0)
|
|
685
|
+
return self._handler.unpin(key.to_string())
|
|
686
|
+
|
|
687
|
+
def batched_unpin(self, keys: List[CacheEngineKey]) -> None:
|
|
688
|
+
"""Batch-unpin keys via single RPC.
|
|
689
|
+
|
|
690
|
+
Decrements server-side pin_count for each key. When pin_count
|
|
691
|
+
reaches 0, the entry becomes eligible for eviction.
|
|
692
|
+
|
|
693
|
+
Args:
|
|
694
|
+
keys: The cache keys to unpin.
|
|
695
|
+
"""
|
|
696
|
+
if not keys:
|
|
697
|
+
return
|
|
698
|
+
if self._mla_worker_id_as0_mode:
|
|
699
|
+
keys = [k.with_new_worker_id(0) for k in keys]
|
|
700
|
+
key_strs = [k.to_string() for k in keys]
|
|
701
|
+
self._handler.batch_unpin(key_strs)
|
|
702
|
+
|
|
703
|
+
def remove(self, key: CacheEngineKey, force: bool = True) -> bool:
|
|
704
|
+
"""Remove a key from MaruServer.
|
|
705
|
+
|
|
706
|
+
Args:
|
|
707
|
+
key: The cache key.
|
|
708
|
+
force: Whether to force removal.
|
|
709
|
+
|
|
710
|
+
Returns:
|
|
711
|
+
True if removed successfully.
|
|
712
|
+
"""
|
|
713
|
+
if self._mla_worker_id_as0_mode:
|
|
714
|
+
key = key.with_new_worker_id(0)
|
|
715
|
+
key_str = key.to_string()
|
|
716
|
+
result = self._handler.delete(key_str)
|
|
717
|
+
logger.debug("[Maru] remove key=%s success=%s", key, result)
|
|
718
|
+
return result
|
|
719
|
+
|
|
720
|
+
# =========================================================================
|
|
721
|
+
# Lifecycle
|
|
722
|
+
# =========================================================================
|
|
723
|
+
|
|
724
|
+
def close(self) -> None:
|
|
725
|
+
"""Close the backend and underlying MaruHandler."""
|
|
726
|
+
while True:
|
|
727
|
+
with self.put_lock:
|
|
728
|
+
if not self.put_tasks:
|
|
729
|
+
break
|
|
730
|
+
time.sleep(0.1)
|
|
731
|
+
|
|
732
|
+
self.memory_allocator.close()
|
|
733
|
+
self._handler.close()
|
|
734
|
+
logger.info("MaruBackend closed.")
|