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,167 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Streaming reader for trace files.
|
|
4
|
+
|
|
5
|
+
The reader yields ``(Header, Iterator[Record])`` pairs. Records are
|
|
6
|
+
yielded lazily so that arbitrarily large traces can be inspected
|
|
7
|
+
without loading the whole file into memory.
|
|
8
|
+
|
|
9
|
+
Trailing partial frames (truncated by SIGKILL or filesystem buffering)
|
|
10
|
+
are detected and the iterator stops cleanly with a WARNING log.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Future
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
# Standard
|
|
17
|
+
from typing import BinaryIO, Iterator
|
|
18
|
+
import struct
|
|
19
|
+
|
|
20
|
+
# First Party
|
|
21
|
+
from lmcache.logging import init_logger
|
|
22
|
+
from lmcache.v1.mp_observability.trace.format import (
|
|
23
|
+
FORMAT_VERSION,
|
|
24
|
+
MAGIC,
|
|
25
|
+
TRACE_SCHEMA_VERSION,
|
|
26
|
+
Header,
|
|
27
|
+
Record,
|
|
28
|
+
decode_header,
|
|
29
|
+
decode_record,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
logger = init_logger(__name__)
|
|
33
|
+
|
|
34
|
+
_LEN_STRUCT = struct.Struct(">I")
|
|
35
|
+
_LEN_PREFIX = _LEN_STRUCT.size
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class TraceReader:
|
|
39
|
+
"""Streaming reader for a binary trace file.
|
|
40
|
+
|
|
41
|
+
Usage::
|
|
42
|
+
|
|
43
|
+
with TraceReader("/tmp/run.lct") as r:
|
|
44
|
+
header = r.header
|
|
45
|
+
for record in r.records():
|
|
46
|
+
...
|
|
47
|
+
|
|
48
|
+
Closing the reader closes the underlying file handle.
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(self, path: str) -> None:
|
|
52
|
+
self._path = path
|
|
53
|
+
self._fh: BinaryIO | None = open(path, "rb")
|
|
54
|
+
try:
|
|
55
|
+
self._header = self._read_header()
|
|
56
|
+
except Exception:
|
|
57
|
+
self._fh.close()
|
|
58
|
+
self._fh = None
|
|
59
|
+
raise
|
|
60
|
+
|
|
61
|
+
def __enter__(self) -> TraceReader:
|
|
62
|
+
return self
|
|
63
|
+
|
|
64
|
+
def __exit__(self, *_exc: object) -> None:
|
|
65
|
+
self.close()
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def header(self) -> Header:
|
|
69
|
+
"""Return the file header. Always present; populated by
|
|
70
|
+
``__init__``."""
|
|
71
|
+
return self._header
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def path(self) -> str:
|
|
75
|
+
"""Path of the trace file."""
|
|
76
|
+
return self._path
|
|
77
|
+
|
|
78
|
+
def records(self) -> Iterator[Record]:
|
|
79
|
+
"""Yield every record in the file in order.
|
|
80
|
+
|
|
81
|
+
Yields each :class:`Record` as it is read. When the file ends
|
|
82
|
+
cleanly (boundary aligned to a frame), iteration stops without
|
|
83
|
+
error. When a partial trailing frame is detected, a warning
|
|
84
|
+
is logged and iteration stops.
|
|
85
|
+
"""
|
|
86
|
+
if self._fh is None:
|
|
87
|
+
raise RuntimeError("TraceReader is closed")
|
|
88
|
+
while True:
|
|
89
|
+
frame = self._read_frame(strict=False)
|
|
90
|
+
if frame is None:
|
|
91
|
+
return
|
|
92
|
+
try:
|
|
93
|
+
yield decode_record(frame)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
logger.warning(
|
|
96
|
+
"TraceReader: skipping malformed record at offset %d: %s",
|
|
97
|
+
self._fh.tell(),
|
|
98
|
+
e,
|
|
99
|
+
)
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
def close(self) -> None:
|
|
103
|
+
"""Close the underlying file. Idempotent."""
|
|
104
|
+
if self._fh is not None:
|
|
105
|
+
self._fh.close()
|
|
106
|
+
self._fh = None
|
|
107
|
+
|
|
108
|
+
# ---- internal -----------------------------------------------------
|
|
109
|
+
|
|
110
|
+
def _read_header(self) -> Header:
|
|
111
|
+
frame = self._read_frame(strict=True)
|
|
112
|
+
if frame is None:
|
|
113
|
+
raise ValueError(f"trace file {self._path!r} is empty")
|
|
114
|
+
header = decode_header(frame)
|
|
115
|
+
if header.magic != MAGIC:
|
|
116
|
+
raise ValueError(
|
|
117
|
+
f"trace file {self._path!r}: bad magic "
|
|
118
|
+
f"(got {header.magic!r}, expected {MAGIC!r})"
|
|
119
|
+
)
|
|
120
|
+
if header.format_version != FORMAT_VERSION:
|
|
121
|
+
raise ValueError(
|
|
122
|
+
f"trace file {self._path!r}: unsupported format_version "
|
|
123
|
+
f"{header.format_version} (this build expects {FORMAT_VERSION})"
|
|
124
|
+
)
|
|
125
|
+
if header.trace_schema_version != TRACE_SCHEMA_VERSION:
|
|
126
|
+
raise ValueError(
|
|
127
|
+
f"trace file {self._path!r}: unsupported trace_schema_version "
|
|
128
|
+
f"{header.trace_schema_version} "
|
|
129
|
+
f"(this build expects {TRACE_SCHEMA_VERSION})"
|
|
130
|
+
)
|
|
131
|
+
return header
|
|
132
|
+
|
|
133
|
+
def _read_frame(self, strict: bool) -> bytes | None:
|
|
134
|
+
"""Read one length-prefixed frame.
|
|
135
|
+
|
|
136
|
+
Returns ``None`` on clean EOF (when ``strict=False``). On
|
|
137
|
+
truncation in the middle of a frame, logs a WARNING and
|
|
138
|
+
returns ``None``. In ``strict=True`` mode, both partial and
|
|
139
|
+
empty reads raise.
|
|
140
|
+
"""
|
|
141
|
+
assert self._fh is not None
|
|
142
|
+
prefix = self._fh.read(_LEN_PREFIX)
|
|
143
|
+
if not prefix:
|
|
144
|
+
if strict:
|
|
145
|
+
raise ValueError("unexpected EOF reading frame length")
|
|
146
|
+
return None
|
|
147
|
+
if len(prefix) < _LEN_PREFIX:
|
|
148
|
+
msg = (
|
|
149
|
+
f"truncated frame length prefix at offset "
|
|
150
|
+
f"{self._fh.tell() - len(prefix)}"
|
|
151
|
+
)
|
|
152
|
+
if strict:
|
|
153
|
+
raise ValueError(msg)
|
|
154
|
+
logger.warning("TraceReader: %s", msg)
|
|
155
|
+
return None
|
|
156
|
+
(length,) = _LEN_STRUCT.unpack(prefix)
|
|
157
|
+
body = self._fh.read(length)
|
|
158
|
+
if len(body) < length:
|
|
159
|
+
msg = (
|
|
160
|
+
f"truncated frame body at offset "
|
|
161
|
+
f"{self._fh.tell() - len(body)} (got {len(body)} of {length})"
|
|
162
|
+
)
|
|
163
|
+
if strict:
|
|
164
|
+
raise ValueError(msg)
|
|
165
|
+
logger.warning("TraceReader: %s", msg)
|
|
166
|
+
return None
|
|
167
|
+
return body
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Trace recorder — writes TRACE_CALL events to a binary file.
|
|
4
|
+
|
|
5
|
+
Architecture:
|
|
6
|
+
|
|
7
|
+
* The recorder is an :class:`EventSubscriber` registered on the global
|
|
8
|
+
EventBus. Subscriber callbacks run on the EventBus drain thread, so
|
|
9
|
+
they are already off the request path.
|
|
10
|
+
* Encoding (codec + msgpack) and disk I/O happen synchronously inside
|
|
11
|
+
the callback. Adding a second worker thread would be premature
|
|
12
|
+
optimization; the EventBus drain thread already serves that role.
|
|
13
|
+
* Length-prefixed framing: each frame is written as a 4-byte
|
|
14
|
+
big-endian length followed by msgpack bytes. This keeps the reader
|
|
15
|
+
simple and tolerates partial-write tail truncation gracefully.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
# Future
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
# Standard
|
|
22
|
+
from abc import ABC, abstractmethod
|
|
23
|
+
from dataclasses import asdict, is_dataclass
|
|
24
|
+
from typing import Any
|
|
25
|
+
import hashlib
|
|
26
|
+
import json
|
|
27
|
+
import os
|
|
28
|
+
import struct
|
|
29
|
+
import threading
|
|
30
|
+
import time
|
|
31
|
+
|
|
32
|
+
# First Party
|
|
33
|
+
from lmcache.logging import init_logger
|
|
34
|
+
from lmcache.v1.distributed.config import StorageManagerConfig
|
|
35
|
+
from lmcache.v1.mp_observability.event import Event, EventType
|
|
36
|
+
from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
|
|
37
|
+
from lmcache.v1.mp_observability.trace import codecs
|
|
38
|
+
from lmcache.v1.mp_observability.trace.decorator import set_tracing_enabled
|
|
39
|
+
from lmcache.v1.mp_observability.trace.format import (
|
|
40
|
+
FORMAT_VERSION,
|
|
41
|
+
MAGIC,
|
|
42
|
+
TRACE_SCHEMA_VERSION,
|
|
43
|
+
Header,
|
|
44
|
+
Record,
|
|
45
|
+
encode_header,
|
|
46
|
+
encode_record,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
logger = init_logger(__name__)
|
|
50
|
+
|
|
51
|
+
#: Frame length prefix size (bytes). Big-endian uint32 — 4 GiB cap
|
|
52
|
+
#: per frame which is far above any expected record size.
|
|
53
|
+
_LEN_PREFIX = 4
|
|
54
|
+
_LEN_STRUCT = struct.Struct(">I")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class TraceRecorder(EventSubscriber, ABC):
|
|
58
|
+
"""Base class for trace recorders.
|
|
59
|
+
|
|
60
|
+
Concrete subclasses select which events they care about. This
|
|
61
|
+
base provides:
|
|
62
|
+
|
|
63
|
+
* file management (open / write / fsync / close)
|
|
64
|
+
* header emission
|
|
65
|
+
* the trace-gate flip (on at construction, off at ``close()``)
|
|
66
|
+
|
|
67
|
+
Subclasses implement :meth:`get_subscriptions`.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, output_path: str, level: str) -> None:
|
|
71
|
+
self._output_path = output_path
|
|
72
|
+
self._level = level
|
|
73
|
+
self._fd = open(output_path, "wb", buffering=0)
|
|
74
|
+
self._lock = threading.Lock()
|
|
75
|
+
self._closed = False
|
|
76
|
+
self._dropped_count = 0
|
|
77
|
+
self._header_written = False
|
|
78
|
+
self._t_mono_start = time.monotonic()
|
|
79
|
+
self._t_wall_start = time.time()
|
|
80
|
+
|
|
81
|
+
# The header is written lazily on the first of: a successful
|
|
82
|
+
# ``attach_storage_config`` call, the first record, or
|
|
83
|
+
# ``close()``. Deferring avoids the in-place rewrite problem
|
|
84
|
+
# that would otherwise corrupt the file when the placeholder
|
|
85
|
+
# header and the final header have different byte lengths.
|
|
86
|
+
# Flip the trace gate AFTER the file is open so a racing publish
|
|
87
|
+
# cannot land on a half-initialized recorder.
|
|
88
|
+
set_tracing_enabled(True)
|
|
89
|
+
logger.info("trace recorder writing to %s (level=%s)", output_path, level)
|
|
90
|
+
|
|
91
|
+
# ---- subclass extension points ------------------------------------
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
def get_subscriptions(self) -> dict[EventType, EventCallback]: ...
|
|
95
|
+
|
|
96
|
+
# ---- public API ---------------------------------------------------
|
|
97
|
+
|
|
98
|
+
@property
|
|
99
|
+
def output_path(self) -> str:
|
|
100
|
+
"""Path of the trace file on disk."""
|
|
101
|
+
return self._output_path
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def dropped_count(self) -> int:
|
|
105
|
+
"""Number of records that failed to encode/write."""
|
|
106
|
+
return self._dropped_count
|
|
107
|
+
|
|
108
|
+
def attach_storage_config(self, config: StorageManagerConfig) -> None:
|
|
109
|
+
"""Write the header populated from the StorageManagerConfig.
|
|
110
|
+
|
|
111
|
+
Must be called before any records are written; the
|
|
112
|
+
server lifecycle does this immediately after construction.
|
|
113
|
+
Subsequent calls are silently ignored — the header is written
|
|
114
|
+
once for the lifetime of the file.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
config: The StorageManagerConfig in use. Its dataclass
|
|
118
|
+
form is JSON-serialized and SHA-256 hashed for the
|
|
119
|
+
header digest, so a replay driver can detect
|
|
120
|
+
mismatched configurations.
|
|
121
|
+
"""
|
|
122
|
+
with self._lock:
|
|
123
|
+
if self._closed or self._header_written:
|
|
124
|
+
return
|
|
125
|
+
sm_json = json.dumps(self._safe_config_dict(config), sort_keys=True)
|
|
126
|
+
digest = hashlib.sha256(sm_json.encode("utf-8")).hexdigest()
|
|
127
|
+
self._write_header(sm_json, digest)
|
|
128
|
+
self._header_written = True
|
|
129
|
+
|
|
130
|
+
def shutdown(self) -> None:
|
|
131
|
+
""":class:`EventBus` shutdown hook — close the recorder."""
|
|
132
|
+
self.close()
|
|
133
|
+
|
|
134
|
+
def close(self) -> None:
|
|
135
|
+
"""Flush, fsync, and close the trace file.
|
|
136
|
+
|
|
137
|
+
Idempotent. Flips the trace gate off so any straggler
|
|
138
|
+
publishes after this point are no-ops. Writes a fallback
|
|
139
|
+
empty-config header if neither ``attach_storage_config`` nor
|
|
140
|
+
any record was written, so the resulting file is always
|
|
141
|
+
readable.
|
|
142
|
+
"""
|
|
143
|
+
with self._lock:
|
|
144
|
+
if self._closed:
|
|
145
|
+
return
|
|
146
|
+
self._closed = True
|
|
147
|
+
set_tracing_enabled(False)
|
|
148
|
+
try:
|
|
149
|
+
if not self._header_written:
|
|
150
|
+
self._write_header(sm_config_json="", sm_config_digest="")
|
|
151
|
+
self._header_written = True
|
|
152
|
+
self._fd.flush()
|
|
153
|
+
os.fsync(self._fd.fileno())
|
|
154
|
+
except OSError:
|
|
155
|
+
logger.exception("trace recorder: fsync failed")
|
|
156
|
+
finally:
|
|
157
|
+
self._fd.close()
|
|
158
|
+
if self._dropped_count:
|
|
159
|
+
logger.warning(
|
|
160
|
+
"trace recorder closed; %d record(s) dropped", self._dropped_count
|
|
161
|
+
)
|
|
162
|
+
else:
|
|
163
|
+
logger.info("trace recorder closed cleanly: %s", self._output_path)
|
|
164
|
+
|
|
165
|
+
# ---- internal -----------------------------------------------------
|
|
166
|
+
|
|
167
|
+
def _write_header(self, sm_config_json: str, sm_config_digest: str) -> None:
|
|
168
|
+
header = Header(
|
|
169
|
+
magic=MAGIC,
|
|
170
|
+
format_version=FORMAT_VERSION,
|
|
171
|
+
level=self._level,
|
|
172
|
+
trace_schema_version=TRACE_SCHEMA_VERSION,
|
|
173
|
+
t_mono_start=self._t_mono_start,
|
|
174
|
+
t_wall_start=self._t_wall_start,
|
|
175
|
+
sm_config_json=sm_config_json,
|
|
176
|
+
sm_config_digest=sm_config_digest,
|
|
177
|
+
)
|
|
178
|
+
self._write_frame(encode_header(header))
|
|
179
|
+
|
|
180
|
+
def _write_frame(self, frame: bytes) -> None:
|
|
181
|
+
# Single write so the prefix and body land atomically for frames
|
|
182
|
+
# below PIPE_BUF; two writes would let a concurrent appender
|
|
183
|
+
# interleave between them and would also double the syscall
|
|
184
|
+
# count on an unbuffered fd. Caller holds ``self._lock`` (or is
|
|
185
|
+
# in __init__ before the gate flips on).
|
|
186
|
+
self._fd.write(_LEN_STRUCT.pack(len(frame)) + frame)
|
|
187
|
+
|
|
188
|
+
def _on_trace_call(self, event: Event) -> None:
|
|
189
|
+
"""Encode and append one TRACE_CALL event.
|
|
190
|
+
|
|
191
|
+
Errors are logged at WARNING and counted, but do not propagate
|
|
192
|
+
— losing a record is preferable to taking down the EventBus
|
|
193
|
+
drain thread.
|
|
194
|
+
"""
|
|
195
|
+
try:
|
|
196
|
+
qualname = event.metadata["qualname"]
|
|
197
|
+
args = event.metadata["args"]
|
|
198
|
+
# ``t_mono`` is stamped in the metadata at publish time
|
|
199
|
+
# (see ``publish_call_event``) so the recorded value is
|
|
200
|
+
# co-temporal with ``event.timestamp`` (wall-clock) instead
|
|
201
|
+
# of picking up the drain-thread delay.
|
|
202
|
+
publish_t_mono = event.metadata["t_mono"]
|
|
203
|
+
encoded_args = codecs.encode_args(args)
|
|
204
|
+
t_mono = max(0.0, publish_t_mono - self._t_mono_start)
|
|
205
|
+
record = Record(
|
|
206
|
+
t_mono=t_mono,
|
|
207
|
+
t_wall=event.timestamp,
|
|
208
|
+
qualname=qualname,
|
|
209
|
+
args=encoded_args,
|
|
210
|
+
)
|
|
211
|
+
frame = encode_record(record)
|
|
212
|
+
except Exception:
|
|
213
|
+
self._dropped_count += 1
|
|
214
|
+
logger.warning(
|
|
215
|
+
"trace recorder: failed to encode TRACE_CALL event "
|
|
216
|
+
"(qualname=%s); dropping",
|
|
217
|
+
event.metadata.get("qualname", "<unknown>"),
|
|
218
|
+
exc_info=True,
|
|
219
|
+
)
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
with self._lock:
|
|
223
|
+
if self._closed:
|
|
224
|
+
self._dropped_count += 1
|
|
225
|
+
return
|
|
226
|
+
try:
|
|
227
|
+
# Write a placeholder header on first record if the
|
|
228
|
+
# caller never invoked ``attach_storage_config``.
|
|
229
|
+
# Ensures the file is always readable.
|
|
230
|
+
if not self._header_written:
|
|
231
|
+
self._write_header(sm_config_json="", sm_config_digest="")
|
|
232
|
+
self._header_written = True
|
|
233
|
+
self._write_frame(frame)
|
|
234
|
+
except OSError:
|
|
235
|
+
self._dropped_count += 1
|
|
236
|
+
logger.warning(
|
|
237
|
+
"trace recorder: write failed; dropping record",
|
|
238
|
+
exc_info=True,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
@staticmethod
|
|
242
|
+
def _safe_config_dict(config: StorageManagerConfig) -> dict[str, Any]:
|
|
243
|
+
"""Best-effort conversion of a StorageManagerConfig to a JSON
|
|
244
|
+
dict.
|
|
245
|
+
|
|
246
|
+
Delegates to the module-level
|
|
247
|
+
:func:`safe_storage_config_dict` so the replay driver can
|
|
248
|
+
reproduce the exact digest the recorder writes. Kept as a
|
|
249
|
+
staticmethod for backwards-compatible access.
|
|
250
|
+
"""
|
|
251
|
+
return safe_storage_config_dict(config)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def safe_storage_config_dict(config: StorageManagerConfig) -> dict[str, Any]:
|
|
255
|
+
"""Best-effort JSON-serializable dict view of a StorageManagerConfig.
|
|
256
|
+
|
|
257
|
+
Falls back to ``str(config)`` for fields that are not directly
|
|
258
|
+
serializable. The result is used only for the header digest and
|
|
259
|
+
human inspection — it does not need to be replay-faithful.
|
|
260
|
+
|
|
261
|
+
Exposed publicly so that the replay driver can compute a digest
|
|
262
|
+
of its *own* config using the same algorithm the recorder used,
|
|
263
|
+
for mismatch detection.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
config: The StorageManagerConfig to serialize.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
A JSON-friendly dict.
|
|
270
|
+
"""
|
|
271
|
+
if is_dataclass(config):
|
|
272
|
+
try:
|
|
273
|
+
return _coerce_jsonable(asdict(config))
|
|
274
|
+
except Exception:
|
|
275
|
+
pass
|
|
276
|
+
return {"repr": str(config)}
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _coerce_jsonable(obj: Any) -> Any:
|
|
280
|
+
"""Recursively coerce ``obj`` into a JSON-friendly value.
|
|
281
|
+
|
|
282
|
+
Anything not natively serializable falls through to ``str(obj)``.
|
|
283
|
+
"""
|
|
284
|
+
if isinstance(obj, (str, int, float, bool)) or obj is None:
|
|
285
|
+
return obj
|
|
286
|
+
if isinstance(obj, (list, tuple)):
|
|
287
|
+
return [_coerce_jsonable(x) for x in obj]
|
|
288
|
+
if isinstance(obj, dict):
|
|
289
|
+
return {str(k): _coerce_jsonable(v) for k, v in obj.items()}
|
|
290
|
+
return str(obj)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class StorageTraceRecorder(TraceRecorder):
|
|
294
|
+
"""Records every ``TRACE_CALL`` event into a ``"storage"``-level file."""
|
|
295
|
+
|
|
296
|
+
def __init__(self, output_path: str) -> None:
|
|
297
|
+
super().__init__(output_path=output_path, level="storage")
|
|
298
|
+
|
|
299
|
+
def get_subscriptions(self) -> dict[EventType, EventCallback]:
|
|
300
|
+
return {EventType.TRACE_CALL: self._on_trace_call}
|
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Thread pool with affinity routing.
|
|
4
|
+
|
|
5
|
+
Tasks submitted with the same ``affinity_key`` always execute on the same
|
|
6
|
+
worker thread (determined by ``affinity_key % num_workers``). Within each
|
|
7
|
+
worker, tasks execute sequentially in FIFO order.
|
|
8
|
+
|
|
9
|
+
This is used for GPU-bound request handlers (STORE / RETRIEVE) so that all
|
|
10
|
+
operations for a given vLLM instance land on one thread, eliminating the need
|
|
11
|
+
for per-instance locks on the shared temporary GPU buffer.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
# Standard
|
|
15
|
+
from concurrent.futures import Future
|
|
16
|
+
import queue
|
|
17
|
+
import threading
|
|
18
|
+
|
|
19
|
+
# First Party
|
|
20
|
+
from lmcache.logging import init_logger
|
|
21
|
+
|
|
22
|
+
logger = init_logger(__name__)
|
|
23
|
+
|
|
24
|
+
# Sentinel object to signal worker shutdown
|
|
25
|
+
_SHUTDOWN = object()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AffinityThreadPool:
|
|
29
|
+
"""Thread pool that routes tasks to workers by affinity key.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
max_workers: Number of worker threads.
|
|
33
|
+
thread_name_prefix: Prefix for worker thread names.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
max_workers: int,
|
|
39
|
+
thread_name_prefix: str = "affinity",
|
|
40
|
+
) -> None:
|
|
41
|
+
self._num_workers = max_workers
|
|
42
|
+
self._queues: list[queue.Queue] = [queue.Queue() for _ in range(max_workers)]
|
|
43
|
+
self._threads: list[threading.Thread] = []
|
|
44
|
+
for i in range(max_workers):
|
|
45
|
+
t = threading.Thread(
|
|
46
|
+
target=self._worker,
|
|
47
|
+
args=(self._queues[i],),
|
|
48
|
+
daemon=True,
|
|
49
|
+
name=f"{thread_name_prefix}-{i}",
|
|
50
|
+
)
|
|
51
|
+
t.start()
|
|
52
|
+
self._threads.append(t)
|
|
53
|
+
|
|
54
|
+
logger.debug(
|
|
55
|
+
"Created AffinityThreadPool with %d workers (prefix=%s)",
|
|
56
|
+
max_workers,
|
|
57
|
+
thread_name_prefix,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# ------------------------------------------------------------------
|
|
61
|
+
# Worker loop
|
|
62
|
+
# ------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
@staticmethod
|
|
65
|
+
def _worker(q: queue.Queue) -> None:
|
|
66
|
+
while True:
|
|
67
|
+
item = q.get()
|
|
68
|
+
if item is _SHUTDOWN:
|
|
69
|
+
break
|
|
70
|
+
future, fn, args, kwargs = item
|
|
71
|
+
if future.set_running_or_notify_cancel():
|
|
72
|
+
try:
|
|
73
|
+
result = fn(*args, **kwargs)
|
|
74
|
+
future.set_result(result)
|
|
75
|
+
except BaseException as exc:
|
|
76
|
+
future.set_exception(exc)
|
|
77
|
+
|
|
78
|
+
# ------------------------------------------------------------------
|
|
79
|
+
# Public API
|
|
80
|
+
# ------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
def submit(self, fn, *args, affinity_key: int = 0, **kwargs) -> Future:
|
|
83
|
+
"""Submit *fn* for execution on the worker determined by *affinity_key*.
|
|
84
|
+
|
|
85
|
+
Returns a :class:`concurrent.futures.Future`.
|
|
86
|
+
"""
|
|
87
|
+
future: Future = Future()
|
|
88
|
+
slot = affinity_key % self._num_workers
|
|
89
|
+
self._queues[slot].put((future, fn, args, kwargs))
|
|
90
|
+
return future
|
|
91
|
+
|
|
92
|
+
def shutdown(self, wait: bool = True) -> None:
|
|
93
|
+
"""Shut down the pool.
|
|
94
|
+
|
|
95
|
+
Sends a shutdown sentinel to every worker. If *wait* is true, blocks
|
|
96
|
+
until all workers have exited.
|
|
97
|
+
"""
|
|
98
|
+
for q in self._queues:
|
|
99
|
+
q.put(_SHUTDOWN)
|
|
100
|
+
if wait:
|
|
101
|
+
for t in self._threads:
|
|
102
|
+
t.join()
|