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,69 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""StorageManager metrics subscriber — OTel counters for SM events."""
|
|
4
|
+
|
|
5
|
+
# Future
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
# Third Party
|
|
9
|
+
from opentelemetry import metrics
|
|
10
|
+
|
|
11
|
+
# First Party
|
|
12
|
+
from lmcache.v1.mp_observability.event import Event, EventType
|
|
13
|
+
from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class SMMetricsSubscriber(EventSubscriber):
|
|
17
|
+
"""Maintains OTel counters for StorageManager operations.
|
|
18
|
+
|
|
19
|
+
Metric parity with the old ``StorageManagerStatsLogger``:
|
|
20
|
+
- ``lmcache_mp.sm_read_requests`` — SM read (prefetch) requests
|
|
21
|
+
- ``lmcache_mp.sm_read_succeed_keys`` — keys that were cache hits
|
|
22
|
+
- ``lmcache_mp.sm_read_failed_keys`` — keys that were cache misses
|
|
23
|
+
- ``lmcache_mp.sm_write_requests`` — SM write (reserve) requests
|
|
24
|
+
- ``lmcache_mp.sm_write_succeed_keys`` — keys successfully allocated
|
|
25
|
+
- ``lmcache_mp.sm_write_failed_keys`` — keys that failed allocation
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
29
|
+
meter = metrics.get_meter("lmcache.sm")
|
|
30
|
+
self._read_requests = meter.create_counter(
|
|
31
|
+
"lmcache_mp.sm_read_requests",
|
|
32
|
+
description="Total StorageManager read (prefetch) requests",
|
|
33
|
+
)
|
|
34
|
+
self._read_succeed = meter.create_counter(
|
|
35
|
+
"lmcache_mp.sm_read_succeed_keys",
|
|
36
|
+
description="Total keys that were cache hits in SM read",
|
|
37
|
+
)
|
|
38
|
+
self._read_failed = meter.create_counter(
|
|
39
|
+
"lmcache_mp.sm_read_failed_keys",
|
|
40
|
+
description="Total keys that were cache misses in SM read",
|
|
41
|
+
)
|
|
42
|
+
self._write_requests = meter.create_counter(
|
|
43
|
+
"lmcache_mp.sm_write_requests",
|
|
44
|
+
description="Total StorageManager write (reserve) requests",
|
|
45
|
+
)
|
|
46
|
+
self._write_succeed = meter.create_counter(
|
|
47
|
+
"lmcache_mp.sm_write_succeed_keys",
|
|
48
|
+
description="Total keys successfully allocated for write in SM",
|
|
49
|
+
)
|
|
50
|
+
self._write_failed = meter.create_counter(
|
|
51
|
+
"lmcache_mp.sm_write_failed_keys",
|
|
52
|
+
description="Total keys that failed allocation for write in SM",
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def get_subscriptions(self) -> dict[EventType, EventCallback]:
|
|
56
|
+
return {
|
|
57
|
+
EventType.SM_READ_PREFETCHED: self._on_read_prefetched,
|
|
58
|
+
EventType.SM_WRITE_RESERVED: self._on_write_reserved,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
def _on_read_prefetched(self, event: Event) -> None:
|
|
62
|
+
self._read_requests.add(1)
|
|
63
|
+
self._read_succeed.add(len(event.metadata["succeeded_keys"]))
|
|
64
|
+
self._read_failed.add(len(event.metadata["failed_keys"]))
|
|
65
|
+
|
|
66
|
+
def _on_write_reserved(self, event: Event) -> None:
|
|
67
|
+
self._write_requests.add(1)
|
|
68
|
+
self._write_succeed.add(len(event.metadata["succeeded_keys"]))
|
|
69
|
+
self._write_failed.add(len(event.metadata["failed_keys"]))
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
# First Party
|
|
4
|
+
from lmcache.v1.mp_observability.subscribers.tracing.mp_server import (
|
|
5
|
+
MPServerTracingSubscriber,
|
|
6
|
+
)
|
|
7
|
+
from lmcache.v1.mp_observability.subscribers.tracing.span_registry import (
|
|
8
|
+
SpanRegistry,
|
|
9
|
+
get_span_registry,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
__all__ = ["MPServerTracingSubscriber", "SpanRegistry", "get_span_registry"]
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""OTel tracing subscriber for MP server operations.
|
|
4
|
+
|
|
5
|
+
Creates a root ``"request"`` span per session wrapping all child spans.
|
|
6
|
+
Opens at ``MP_REQUEST_START``; closes at ``MP_SESSION_END``, deferred until
|
|
7
|
+
any in-flight GPU store/retrieve callbacks complete.
|
|
8
|
+
|
|
9
|
+
Accepts an optional :class:`~lmcache.v1.mp_observability.subscribers.tracing\
|
|
10
|
+
.span_registry.SpanRegistry` so other subscribers can nest spans under the
|
|
11
|
+
root or any child span — see
|
|
12
|
+
``docs/design/observability/request-event-span.md`` for examples.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# Future
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
# Standard
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
# First Party
|
|
22
|
+
from lmcache.logging import init_logger
|
|
23
|
+
from lmcache.v1.mp_observability.event import Event, EventType
|
|
24
|
+
from lmcache.v1.mp_observability.event_bus import EventCallback, EventSubscriber
|
|
25
|
+
from lmcache.v1.mp_observability.subscribers.tracing.span_registry import SpanRegistry
|
|
26
|
+
|
|
27
|
+
logger = init_logger(__name__)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Third Party
|
|
31
|
+
from opentelemetry import trace
|
|
32
|
+
|
|
33
|
+
_tracer = trace.get_tracer("lmcache_mp.server")
|
|
34
|
+
_HAS_OTEL = True
|
|
35
|
+
except ImportError:
|
|
36
|
+
_HAS_OTEL = False
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class MPServerTracingSubscriber(EventSubscriber):
|
|
40
|
+
"""Creates OTel spans from MP server events with a per-request root span.
|
|
41
|
+
|
|
42
|
+
Each session gets one root ``"request"`` span that nests all child spans
|
|
43
|
+
(``mp.lookup_prefetch``, ``mp.retrieve``, ``mp.store``) beneath it.
|
|
44
|
+
|
|
45
|
+
The root span is opened eagerly at ``MP_REQUEST_START`` and closed at
|
|
46
|
+
``MP_SESSION_END``, with deferral if GPU stores are still in flight.
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
# Maps START event types to span names
|
|
50
|
+
_SPAN_NAMES: dict[EventType, str] = {
|
|
51
|
+
EventType.MP_STORE_START: "mp.store",
|
|
52
|
+
EventType.MP_RETRIEVE_START: "mp.retrieve",
|
|
53
|
+
EventType.MP_LOOKUP_PREFETCH_START: "mp.lookup_prefetch",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_END_TO_START: dict[EventType, EventType] = {
|
|
57
|
+
EventType.MP_STORE_END: EventType.MP_STORE_START,
|
|
58
|
+
EventType.MP_RETRIEVE_END: EventType.MP_RETRIEVE_START,
|
|
59
|
+
EventType.MP_LOOKUP_PREFETCH_END: EventType.MP_LOOKUP_PREFETCH_START,
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# Logical registry names for child spans. When a child span is opened it
|
|
63
|
+
# is also stored under this name in the shared registry so that other
|
|
64
|
+
# subscribers (or future sub-span subscribers) can look up its context as
|
|
65
|
+
# a parent without coupling to this class.
|
|
66
|
+
_LOGICAL_NAMES: dict[EventType, str] = {
|
|
67
|
+
EventType.MP_STORE_START: "store",
|
|
68
|
+
EventType.MP_RETRIEVE_START: "retrieve",
|
|
69
|
+
EventType.MP_LOOKUP_PREFETCH_START: "lookup_prefetch",
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
def __init__(self, registry: SpanRegistry | None = None) -> None:
|
|
73
|
+
# Shared span registry. When a registry is provided, other subscribers
|
|
74
|
+
# that share the same instance can look up the root "request" span or
|
|
75
|
+
# any child span ("store", "retrieve", "lookup_prefetch") as a parent
|
|
76
|
+
# context. If None, a private registry is created so that the class
|
|
77
|
+
# remains usable without a shared registry.
|
|
78
|
+
self._registry = registry if registry is not None else SpanRegistry()
|
|
79
|
+
|
|
80
|
+
# session_id -> (span, start_event_type) for pending child spans
|
|
81
|
+
self._pending: dict[str, tuple[Any, EventType]] = {}
|
|
82
|
+
|
|
83
|
+
# session_id -> number of MP_STORE_SUBMITTED events without a
|
|
84
|
+
# matching MP_STORE_END; guards against SESSION_END racing GPU stores
|
|
85
|
+
self._pending_store_count: dict[str, int] = {}
|
|
86
|
+
|
|
87
|
+
# session_id -> number of MP_RETRIEVE_SUBMITTED events without a
|
|
88
|
+
# matching MP_RETRIEVE_END; guards against SESSION_END racing GPU retrieves
|
|
89
|
+
self._pending_retrieve_count: dict[str, int] = {}
|
|
90
|
+
|
|
91
|
+
# session_id -> SESSION_END timestamp saved when stores/retrieves are in
|
|
92
|
+
# flight; the last MP_STORE_END / MP_RETRIEVE_END uses this to close the
|
|
93
|
+
# root span
|
|
94
|
+
self._deferred_session_end_ts: dict[str, float] = {}
|
|
95
|
+
|
|
96
|
+
def get_subscriptions(self) -> dict[EventType, EventCallback]:
|
|
97
|
+
"""Return the event-to-callback mapping for this subscriber."""
|
|
98
|
+
return {
|
|
99
|
+
EventType.MP_REQUEST_START: self._on_request_start,
|
|
100
|
+
EventType.MP_STORE_SUBMITTED: self._on_store_submitted,
|
|
101
|
+
EventType.MP_RETRIEVE_SUBMITTED: self._on_retrieve_submitted,
|
|
102
|
+
EventType.MP_SESSION_END: self._on_session_end,
|
|
103
|
+
EventType.MP_STORE_START: self._on_start,
|
|
104
|
+
EventType.MP_STORE_END: self._on_end,
|
|
105
|
+
EventType.MP_RETRIEVE_START: self._on_start,
|
|
106
|
+
EventType.MP_RETRIEVE_END: self._on_end,
|
|
107
|
+
EventType.MP_LOOKUP_PREFETCH_START: self._on_start,
|
|
108
|
+
EventType.MP_LOOKUP_PREFETCH_END: self._on_end,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# ------------------------------------------------------------------
|
|
112
|
+
# Public lifecycle
|
|
113
|
+
# ------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
def shutdown(self) -> None:
|
|
116
|
+
"""End all leaked spans on bus shutdown."""
|
|
117
|
+
for key, (span, _) in self._pending.items():
|
|
118
|
+
try:
|
|
119
|
+
span.end()
|
|
120
|
+
except Exception:
|
|
121
|
+
pass
|
|
122
|
+
self._pending.clear()
|
|
123
|
+
|
|
124
|
+
sessions = (
|
|
125
|
+
set(self._pending_store_count)
|
|
126
|
+
| set(self._pending_retrieve_count)
|
|
127
|
+
| set(self._deferred_session_end_ts)
|
|
128
|
+
| self._registry.all_session_ids()
|
|
129
|
+
)
|
|
130
|
+
for sid in sessions:
|
|
131
|
+
self._registry.clear_session(sid)
|
|
132
|
+
self._pending_store_count.clear()
|
|
133
|
+
self._pending_retrieve_count.clear()
|
|
134
|
+
self._deferred_session_end_ts.clear()
|
|
135
|
+
|
|
136
|
+
# ------------------------------------------------------------------
|
|
137
|
+
# Root span handlers
|
|
138
|
+
# ------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
def _on_request_start(self, event: Event) -> None:
|
|
141
|
+
"""Create the root span eagerly at true request arrival.
|
|
142
|
+
|
|
143
|
+
Args:
|
|
144
|
+
event: ``MP_REQUEST_START`` event with ``session_id`` set.
|
|
145
|
+
"""
|
|
146
|
+
if not _HAS_OTEL:
|
|
147
|
+
return
|
|
148
|
+
self._get_or_create_request_span(event.session_id, event.timestamp)
|
|
149
|
+
|
|
150
|
+
def _on_store_submitted(self, event: Event) -> None:
|
|
151
|
+
"""Increment the in-flight store counter for the session.
|
|
152
|
+
|
|
153
|
+
Called CPU-synchronously before the GPU store is enqueued, ensuring
|
|
154
|
+
the counter is non-zero before ``MP_SESSION_END`` can arrive.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
event: ``MP_STORE_SUBMITTED`` event.
|
|
158
|
+
"""
|
|
159
|
+
if not _HAS_OTEL:
|
|
160
|
+
return
|
|
161
|
+
sid = event.session_id
|
|
162
|
+
self._pending_store_count[sid] = self._pending_store_count.get(sid, 0) + 1
|
|
163
|
+
|
|
164
|
+
def _on_retrieve_submitted(self, event: Event) -> None:
|
|
165
|
+
"""Increment the in-flight retrieve counter for the session.
|
|
166
|
+
|
|
167
|
+
Called CPU-synchronously before the GPU retrieve is enqueued, ensuring
|
|
168
|
+
the counter is non-zero before ``MP_SESSION_END`` can arrive.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
event: ``MP_RETRIEVE_SUBMITTED`` event.
|
|
172
|
+
"""
|
|
173
|
+
if not _HAS_OTEL:
|
|
174
|
+
return
|
|
175
|
+
sid = event.session_id
|
|
176
|
+
self._pending_retrieve_count[sid] = self._pending_retrieve_count.get(sid, 0) + 1
|
|
177
|
+
|
|
178
|
+
def _on_session_end(self, event: Event) -> None:
|
|
179
|
+
"""Close the root span, or defer if GPU stores/retrieves are still in flight.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
event: ``MP_SESSION_END`` event carrying the logical end timestamp.
|
|
183
|
+
"""
|
|
184
|
+
if not _HAS_OTEL:
|
|
185
|
+
return
|
|
186
|
+
sid = event.session_id
|
|
187
|
+
if (
|
|
188
|
+
self._pending_store_count.get(sid, 0) == 0
|
|
189
|
+
and self._pending_retrieve_count.get(sid, 0) == 0
|
|
190
|
+
):
|
|
191
|
+
self._close_request_span(sid, event.timestamp)
|
|
192
|
+
else:
|
|
193
|
+
# Stores/retrieves still in flight — save timestamp; the last
|
|
194
|
+
# MP_STORE_END / MP_RETRIEVE_END closes the root span
|
|
195
|
+
self._deferred_session_end_ts[sid] = event.timestamp
|
|
196
|
+
|
|
197
|
+
# ------------------------------------------------------------------
|
|
198
|
+
# Child span handlers
|
|
199
|
+
# ------------------------------------------------------------------
|
|
200
|
+
|
|
201
|
+
def _on_start(self, event: Event) -> None:
|
|
202
|
+
"""Create a child span nested under the root span.
|
|
203
|
+
|
|
204
|
+
Falls back to ``_get_or_create_request_span`` if ``MP_REQUEST_START`` was
|
|
205
|
+
never emitted (e.g. store-only path with no lookup).
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
event: One of ``MP_STORE_START``, ``MP_RETRIEVE_START``, or
|
|
209
|
+
``MP_LOOKUP_PREFETCH_START``.
|
|
210
|
+
"""
|
|
211
|
+
if not _HAS_OTEL:
|
|
212
|
+
return
|
|
213
|
+
sid = event.session_id
|
|
214
|
+
_, root_ctx = self._get_or_create_request_span(sid, event.timestamp)
|
|
215
|
+
|
|
216
|
+
span_name = self._SPAN_NAMES[event.event_type]
|
|
217
|
+
span = _tracer.start_span(
|
|
218
|
+
span_name,
|
|
219
|
+
context=root_ctx,
|
|
220
|
+
start_time=int(event.timestamp * 1e9),
|
|
221
|
+
)
|
|
222
|
+
for k, v in event.metadata.items():
|
|
223
|
+
span.set_attribute(k, str(v))
|
|
224
|
+
span.set_attribute("session_id", sid)
|
|
225
|
+
|
|
226
|
+
key = f"{sid}:{event.event_type.value}"
|
|
227
|
+
self._pending[key] = (span, event.event_type)
|
|
228
|
+
|
|
229
|
+
# Also register in the shared registry so other subscribers can use
|
|
230
|
+
# this span as a parent context for nested sub-spans.
|
|
231
|
+
logical = self._LOGICAL_NAMES.get(event.event_type)
|
|
232
|
+
if logical:
|
|
233
|
+
self._registry.open(sid, logical, span, trace.set_span_in_context(span))
|
|
234
|
+
|
|
235
|
+
def _on_end(self, event: Event) -> None:
|
|
236
|
+
"""Close a pending child span and handle store-count deferral.
|
|
237
|
+
|
|
238
|
+
On ``MP_STORE_END``: decrements the store counter; if it reaches zero
|
|
239
|
+
and a deferred session-end timestamp exists, closes the root span.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
event: One of ``MP_STORE_END``, ``MP_RETRIEVE_END``, or
|
|
243
|
+
``MP_LOOKUP_PREFETCH_END``.
|
|
244
|
+
"""
|
|
245
|
+
if not _HAS_OTEL:
|
|
246
|
+
return
|
|
247
|
+
sid = event.session_id
|
|
248
|
+
start_type = self._END_TO_START[event.event_type]
|
|
249
|
+
key = f"{sid}:{start_type.value}"
|
|
250
|
+
entry = self._pending.pop(key, None)
|
|
251
|
+
if entry is None:
|
|
252
|
+
logger.debug(
|
|
253
|
+
"No pending span for %s session=%s",
|
|
254
|
+
event.event_type.value,
|
|
255
|
+
sid,
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
span, _ = entry
|
|
259
|
+
for k, v in event.metadata.items():
|
|
260
|
+
span.set_attribute(k, str(v))
|
|
261
|
+
span.end(end_time=int(event.timestamp * 1e9))
|
|
262
|
+
|
|
263
|
+
# Remove the child span from the shared registry now that it is closed.
|
|
264
|
+
logical = self._LOGICAL_NAMES.get(start_type)
|
|
265
|
+
if logical:
|
|
266
|
+
self._registry.pop(sid, logical)
|
|
267
|
+
|
|
268
|
+
if event.event_type == EventType.MP_STORE_END:
|
|
269
|
+
if (count := self._pending_store_count.get(sid, 0)) > 0:
|
|
270
|
+
self._pending_store_count[sid] = count - 1
|
|
271
|
+
elif event.event_type == EventType.MP_RETRIEVE_END:
|
|
272
|
+
if (count := self._pending_retrieve_count.get(sid, 0)) > 0:
|
|
273
|
+
self._pending_retrieve_count[sid] = count - 1
|
|
274
|
+
|
|
275
|
+
if (
|
|
276
|
+
sid in self._deferred_session_end_ts
|
|
277
|
+
and self._pending_store_count.get(sid, 0) == 0
|
|
278
|
+
and self._pending_retrieve_count.get(sid, 0) == 0
|
|
279
|
+
):
|
|
280
|
+
deferred_ts = self._deferred_session_end_ts.pop(sid)
|
|
281
|
+
self._close_request_span(sid, deferred_ts)
|
|
282
|
+
|
|
283
|
+
# ------------------------------------------------------------------
|
|
284
|
+
# Private helpers
|
|
285
|
+
# ------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
def _get_or_create_request_span(
|
|
288
|
+
self, session_id: str, ts: float
|
|
289
|
+
) -> tuple[Any, Any]:
|
|
290
|
+
"""Return the root span and its OTel context, creating them if absent.
|
|
291
|
+
|
|
292
|
+
The span is stored in the registry under ``span_name="request"`` so
|
|
293
|
+
that other subscribers sharing the same :class:`SpanRegistry` can
|
|
294
|
+
retrieve the parent context via
|
|
295
|
+
``registry.get_context(session_id, "request")``.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
session_id: The request session identifier.
|
|
299
|
+
ts: Wall-clock timestamp (``time.time()``) to use as span start
|
|
300
|
+
if the root is created now.
|
|
301
|
+
|
|
302
|
+
Returns:
|
|
303
|
+
``(root_span, root_otel_context)`` tuple.
|
|
304
|
+
"""
|
|
305
|
+
entry = self._registry.get(session_id, "request")
|
|
306
|
+
if entry is not None:
|
|
307
|
+
return entry
|
|
308
|
+
root_span = _tracer.start_span(
|
|
309
|
+
"request",
|
|
310
|
+
start_time=int(ts * 1e9),
|
|
311
|
+
)
|
|
312
|
+
root_span.set_attribute("session_id", session_id)
|
|
313
|
+
root_ctx = trace.set_span_in_context(root_span)
|
|
314
|
+
self._registry.open(session_id, "request", root_span, root_ctx)
|
|
315
|
+
return root_span, root_ctx
|
|
316
|
+
|
|
317
|
+
def _close_request_span(self, session_id: str, end_ts: float) -> None:
|
|
318
|
+
"""End the root span and clean up all per-session state.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
session_id: The request session identifier.
|
|
322
|
+
end_ts: Wall-clock timestamp to stamp as the span end time.
|
|
323
|
+
"""
|
|
324
|
+
entry = self._registry.pop(session_id, "request")
|
|
325
|
+
if entry is not None:
|
|
326
|
+
root_span, _ = entry
|
|
327
|
+
try:
|
|
328
|
+
root_span.end(end_time=int(end_ts * 1e9))
|
|
329
|
+
except Exception:
|
|
330
|
+
pass
|
|
331
|
+
self._pending_store_count.pop(session_id, None)
|
|
332
|
+
self._pending_retrieve_count.pop(session_id, None)
|
|
333
|
+
self._deferred_session_end_ts.pop(session_id, None)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Shared OTel span registry for cross-subscriber context propagation.
|
|
4
|
+
|
|
5
|
+
:class:`SpanRegistry` stores active spans keyed by ``(session_id, span_name)``
|
|
6
|
+
so that multiple subscribers sharing the same instance can look up parent
|
|
7
|
+
span contexts without tight coupling.
|
|
8
|
+
|
|
9
|
+
All reads and writes happen on the single EventBus drain thread — no locking
|
|
10
|
+
is needed.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
# Future
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
# Standard
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
# First Party
|
|
20
|
+
from lmcache.logging import init_logger
|
|
21
|
+
|
|
22
|
+
logger = init_logger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SpanRegistry:
|
|
26
|
+
"""Registry of active OTel spans, keyed by ``(session_id, span_name)``.
|
|
27
|
+
|
|
28
|
+
``span_name`` is a logical identifier (e.g. ``"request"``, ``"retrieve"``,
|
|
29
|
+
``"store"``) that decouples parent-lookup from the concrete OTel span name.
|
|
30
|
+
|
|
31
|
+
All methods must be called from the EventBus drain thread; no locking is
|
|
32
|
+
performed.
|
|
33
|
+
|
|
34
|
+
Example usage across two subscribers that share one registry::
|
|
35
|
+
|
|
36
|
+
registry = SpanRegistry()
|
|
37
|
+
bus.register_subscriber(MPServerTracingSubscriber(registry))
|
|
38
|
+
bus.register_subscriber(MyL1TracingSubscriber(registry))
|
|
39
|
+
|
|
40
|
+
# Inside MyL1TracingSubscriber._on_l1_start:
|
|
41
|
+
parent_ctx = registry.get_context(session_id, "retrieve") \\
|
|
42
|
+
or registry.get_context(session_id, "request")
|
|
43
|
+
span = tracer.start_span("l1.read", context=parent_ctx, ...)
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self) -> None:
|
|
47
|
+
# (session_id, span_name) -> (span, otel_context)
|
|
48
|
+
self._active: dict[tuple[str, str], tuple[Any, Any]] = {}
|
|
49
|
+
|
|
50
|
+
def open(self, session_id: str, span_name: str, span: Any, ctx: Any) -> None:
|
|
51
|
+
"""Register an open span and its OTel context.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
session_id: The request session identifier.
|
|
55
|
+
span_name: Logical name for the span (e.g. ``"request"``,
|
|
56
|
+
``"retrieve"``).
|
|
57
|
+
span: The OTel span object.
|
|
58
|
+
ctx: The OTel context containing the span (from
|
|
59
|
+
``trace.set_span_in_context(span)``).
|
|
60
|
+
"""
|
|
61
|
+
self._active[(session_id, span_name)] = (span, ctx)
|
|
62
|
+
|
|
63
|
+
def get(self, session_id: str, span_name: str) -> tuple[Any, Any] | None:
|
|
64
|
+
"""Return ``(span, context)`` for an open span, or ``None``.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
session_id: The request session identifier.
|
|
68
|
+
span_name: Logical span name to look up.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
``(span, otel_context)`` if the span is registered, else ``None``.
|
|
72
|
+
"""
|
|
73
|
+
return self._active.get((session_id, span_name))
|
|
74
|
+
|
|
75
|
+
def get_context(self, session_id: str, span_name: str) -> Any | None:
|
|
76
|
+
"""Return the OTel context for an open span, or ``None``.
|
|
77
|
+
|
|
78
|
+
Convenience wrapper over :meth:`get` for callers that only need the
|
|
79
|
+
context to pass as a ``context=`` argument when starting a child span.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
session_id: The request session identifier.
|
|
83
|
+
span_name: Logical span name to look up.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
The OTel context, or ``None`` if the span is not registered.
|
|
87
|
+
"""
|
|
88
|
+
entry = self._active.get((session_id, span_name))
|
|
89
|
+
return entry[1] if entry is not None else None
|
|
90
|
+
|
|
91
|
+
def pop(self, session_id: str, span_name: str) -> tuple[Any, Any] | None:
|
|
92
|
+
"""Remove and return ``(span, context)``, or ``None`` if absent.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
session_id: The request session identifier.
|
|
96
|
+
span_name: Logical span name to remove.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
``(span, otel_context)`` if the span was registered, else ``None``.
|
|
100
|
+
"""
|
|
101
|
+
return self._active.pop((session_id, span_name), None)
|
|
102
|
+
|
|
103
|
+
def all_session_ids(self) -> set[str]:
|
|
104
|
+
"""Return the set of session IDs that have at least one open span.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
Set of session ID strings currently tracked in the registry.
|
|
108
|
+
"""
|
|
109
|
+
return {k[0] for k in self._active}
|
|
110
|
+
|
|
111
|
+
def clear_session(self, session_id: str) -> None:
|
|
112
|
+
"""End all open spans for *session_id* and remove them.
|
|
113
|
+
|
|
114
|
+
Intended for use in ``shutdown()`` to clean up leaked spans when the
|
|
115
|
+
EventBus stops unexpectedly.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
session_id: The request session identifier to clean up.
|
|
119
|
+
"""
|
|
120
|
+
keys = [k for k in self._active if k[0] == session_id]
|
|
121
|
+
for k in keys:
|
|
122
|
+
span, _ = self._active.pop(k)
|
|
123
|
+
try:
|
|
124
|
+
span.end()
|
|
125
|
+
except Exception:
|
|
126
|
+
logger.debug(
|
|
127
|
+
"SpanRegistry: error ending leaked span for session %s key %s",
|
|
128
|
+
session_id,
|
|
129
|
+
k[1],
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
_registry: SpanRegistry | None = None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def get_span_registry() -> SpanRegistry:
|
|
137
|
+
"""Return the process-level singleton :class:`SpanRegistry`.
|
|
138
|
+
|
|
139
|
+
The registry is created on first call. Pass a fresh :class:`SpanRegistry`
|
|
140
|
+
directly to subscriber constructors when test isolation is needed.
|
|
141
|
+
|
|
142
|
+
Returns:
|
|
143
|
+
The shared :class:`SpanRegistry` instance.
|
|
144
|
+
"""
|
|
145
|
+
global _registry
|
|
146
|
+
if _registry is None:
|
|
147
|
+
_registry = SpanRegistry()
|
|
148
|
+
return _registry
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Trace recording subsystem.
|
|
4
|
+
|
|
5
|
+
This package implements the **capture** half of the ``lmcache trace``
|
|
6
|
+
feature. Public API:
|
|
7
|
+
|
|
8
|
+
* :func:`~lmcache.v1.mp_observability.trace.decorator.enable_tracing` —
|
|
9
|
+
decorator that publishes a unified ``TRACE_CALL`` event on each call
|
|
10
|
+
to a decorated function (entry-only, inputs only).
|
|
11
|
+
* :class:`~lmcache.v1.mp_observability.trace.recorder.StorageTraceRecorder`
|
|
12
|
+
— :class:`EventSubscriber` that writes ``TRACE_CALL`` events to a
|
|
13
|
+
binary trace file.
|
|
14
|
+
* :class:`~lmcache.v1.mp_observability.trace.reader.TraceReader` —
|
|
15
|
+
streaming reader for inspection (replay lives in a separate package).
|
|
16
|
+
|
|
17
|
+
When tracing is disabled (the default), the decorator is a thin
|
|
18
|
+
wrapper around the original function; only a single boolean attribute
|
|
19
|
+
load is added per call.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
# First Party
|
|
23
|
+
from lmcache.v1.mp_observability.trace.decorator import (
|
|
24
|
+
enable_tracing,
|
|
25
|
+
is_tracing_enabled,
|
|
26
|
+
publish_call_event,
|
|
27
|
+
set_tracing_enabled,
|
|
28
|
+
)
|
|
29
|
+
from lmcache.v1.mp_observability.trace.format import Header, Record
|
|
30
|
+
from lmcache.v1.mp_observability.trace.lifecycle import (
|
|
31
|
+
maybe_initialize_trace_recorder,
|
|
32
|
+
)
|
|
33
|
+
from lmcache.v1.mp_observability.trace.reader import TraceReader
|
|
34
|
+
from lmcache.v1.mp_observability.trace.recorder import (
|
|
35
|
+
StorageTraceRecorder,
|
|
36
|
+
TraceRecorder,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"Header",
|
|
41
|
+
"Record",
|
|
42
|
+
"StorageTraceRecorder",
|
|
43
|
+
"TraceReader",
|
|
44
|
+
"TraceRecorder",
|
|
45
|
+
"enable_tracing",
|
|
46
|
+
"is_tracing_enabled",
|
|
47
|
+
"maybe_initialize_trace_recorder",
|
|
48
|
+
"publish_call_event",
|
|
49
|
+
"set_tracing_enabled",
|
|
50
|
+
]
|