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
lmcache/observability.py
ADDED
|
@@ -0,0 +1,1958 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from contextlib import contextmanager
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
Any,
|
|
8
|
+
Dict,
|
|
9
|
+
Iterable,
|
|
10
|
+
List,
|
|
11
|
+
Optional,
|
|
12
|
+
Sequence,
|
|
13
|
+
Union,
|
|
14
|
+
)
|
|
15
|
+
import os
|
|
16
|
+
import threading
|
|
17
|
+
import time
|
|
18
|
+
|
|
19
|
+
# Third Party
|
|
20
|
+
from prometheus_client import REGISTRY
|
|
21
|
+
import prometheus_client
|
|
22
|
+
|
|
23
|
+
# First Party
|
|
24
|
+
from lmcache.logging import init_logger
|
|
25
|
+
from lmcache.usage_context import ContinuousUsageContext
|
|
26
|
+
from lmcache.utils import thread_safe
|
|
27
|
+
from lmcache.v1.metadata import LMCacheMetadata
|
|
28
|
+
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
# First Party
|
|
31
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
32
|
+
|
|
33
|
+
logger = init_logger(__name__)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class LMCacheStats:
|
|
38
|
+
# Counter (Note that these are incremental values,
|
|
39
|
+
# which will accumulate over time in Counter)
|
|
40
|
+
interval_retrieve_requests: int
|
|
41
|
+
interval_store_requests: int
|
|
42
|
+
interval_lookup_requests: int
|
|
43
|
+
interval_requested_tokens: int
|
|
44
|
+
interval_hit_tokens: int
|
|
45
|
+
interval_stored_tokens: int
|
|
46
|
+
interval_lookup_tokens: int
|
|
47
|
+
interval_lookup_hits: int
|
|
48
|
+
interval_vllm_hit_tokens: int
|
|
49
|
+
interval_prompt_tokens: int
|
|
50
|
+
|
|
51
|
+
interval_num_slow_retrieval_by_time: int
|
|
52
|
+
interval_num_slow_retrieval_by_speed: int
|
|
53
|
+
|
|
54
|
+
interval_remote_read_requests: int
|
|
55
|
+
interval_remote_read_bytes: int
|
|
56
|
+
interval_remote_write_requests: int
|
|
57
|
+
interval_remote_write_bytes: int
|
|
58
|
+
|
|
59
|
+
interval_remote_time_to_get: List[float]
|
|
60
|
+
interval_remote_time_to_put: List[float]
|
|
61
|
+
interval_remote_time_to_get_sync: List[float]
|
|
62
|
+
|
|
63
|
+
interval_remote_ping_latency: float # Ping latency in milliseconds
|
|
64
|
+
interval_remote_ping_errors: int # Number of ping errors
|
|
65
|
+
interval_remote_ping_success: int # Number of ping successes
|
|
66
|
+
interval_remote_ping_error_code: int # Latest ping error code
|
|
67
|
+
|
|
68
|
+
interval_local_cpu_evict_count: int # evict count
|
|
69
|
+
interval_local_cpu_evict_keys_count: int # evict keys count
|
|
70
|
+
interval_local_cpu_evict_failed_count: int # evict failed count
|
|
71
|
+
|
|
72
|
+
interval_forced_unpin_count: int # forced unpin count due to timeout
|
|
73
|
+
|
|
74
|
+
# Real time value measurements (will be reset after each log)
|
|
75
|
+
retrieve_hit_rate: float
|
|
76
|
+
lookup_hit_rate: float
|
|
77
|
+
|
|
78
|
+
local_cache_usage_bytes: int # Size of the used local cache in bytes
|
|
79
|
+
remote_cache_usage_bytes: int # Size of the used remote cache in bytes
|
|
80
|
+
local_storage_usage_bytes: int # Size of the used local storage in bytes
|
|
81
|
+
|
|
82
|
+
active_memory_objs_count: int # the number of active memory objects
|
|
83
|
+
pinned_memory_objs_count: int # the number of pinned memory objects
|
|
84
|
+
|
|
85
|
+
# Distribution measurements
|
|
86
|
+
time_to_retrieve: List[float]
|
|
87
|
+
time_to_store: List[float]
|
|
88
|
+
time_to_lookup: List[float]
|
|
89
|
+
retrieve_speed: List[float] # Tokens per second
|
|
90
|
+
store_speed: List[float] # Tokens per second
|
|
91
|
+
|
|
92
|
+
# Granular profiling measurements
|
|
93
|
+
retrieve_process_tokens_time: List[float]
|
|
94
|
+
retrieve_broadcast_time: List[float]
|
|
95
|
+
retrieve_to_gpu_time: List[float]
|
|
96
|
+
remote_backend_batched_get_blocking_time: List[float]
|
|
97
|
+
instrumented_connector_batched_get_time: List[float]
|
|
98
|
+
store_process_tokens_time: List[float]
|
|
99
|
+
store_from_gpu_time: List[float]
|
|
100
|
+
store_put_time: List[float]
|
|
101
|
+
|
|
102
|
+
# P2P transfer metrics
|
|
103
|
+
interval_p2p_requests: int
|
|
104
|
+
interval_p2p_transferred_tokens: int
|
|
105
|
+
p2p_time_to_transfer: List[float]
|
|
106
|
+
p2p_transfer_speed: List[float] # Tokens per second
|
|
107
|
+
|
|
108
|
+
# request lookup hit rates
|
|
109
|
+
# use bucket of interval_lookup_hit_rates to represents non-0 hit requests
|
|
110
|
+
# use interval_lookup_0_hit_requests to represents 0 hit requests
|
|
111
|
+
interval_lookup_hit_rates: List[float]
|
|
112
|
+
interval_lookup_0_hit_requests: int
|
|
113
|
+
|
|
114
|
+
interval_request_cache_lifespan: List[float] # cache lifespan in minutes
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@dataclass
|
|
118
|
+
class LookupRequestStats:
|
|
119
|
+
request_id: int
|
|
120
|
+
num_tokens: int
|
|
121
|
+
hit_tokens: int
|
|
122
|
+
is_finished: bool
|
|
123
|
+
start_time: float = 0
|
|
124
|
+
end_time: float = 0
|
|
125
|
+
|
|
126
|
+
def time_to_lookup(self):
|
|
127
|
+
if self.end_time == 0:
|
|
128
|
+
return 0
|
|
129
|
+
return self.end_time - self.start_time
|
|
130
|
+
|
|
131
|
+
def hit_rate(self):
|
|
132
|
+
if self.num_tokens == 0:
|
|
133
|
+
return 0
|
|
134
|
+
return self.hit_tokens / self.num_tokens
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
@dataclass
|
|
138
|
+
class RetrieveRequestStats:
|
|
139
|
+
request_id: int
|
|
140
|
+
num_tokens: int
|
|
141
|
+
local_hit_tokens: int
|
|
142
|
+
remote_hit_tokens: int # Not used for now
|
|
143
|
+
start_time: float
|
|
144
|
+
end_time: float
|
|
145
|
+
process_tokens_time: float = 0
|
|
146
|
+
broadcast_time: float = 0
|
|
147
|
+
to_gpu_time: float = 0
|
|
148
|
+
detailed_metrics: Dict[str, Any] = field(default_factory=dict)
|
|
149
|
+
|
|
150
|
+
def time_to_retrieve(self):
|
|
151
|
+
if self.end_time == 0:
|
|
152
|
+
return 0
|
|
153
|
+
return self.end_time - self.start_time
|
|
154
|
+
|
|
155
|
+
def retrieve_speed(self):
|
|
156
|
+
if self.time_to_retrieve() == 0:
|
|
157
|
+
return 0
|
|
158
|
+
return (
|
|
159
|
+
self.local_hit_tokens + self.remote_hit_tokens
|
|
160
|
+
) / self.time_to_retrieve()
|
|
161
|
+
|
|
162
|
+
@contextmanager
|
|
163
|
+
def profile_process_tokens(self):
|
|
164
|
+
start = time.perf_counter()
|
|
165
|
+
try:
|
|
166
|
+
yield
|
|
167
|
+
finally:
|
|
168
|
+
self.process_tokens_time += time.perf_counter() - start
|
|
169
|
+
|
|
170
|
+
@contextmanager
|
|
171
|
+
def profile_broadcast(self):
|
|
172
|
+
start = time.perf_counter()
|
|
173
|
+
try:
|
|
174
|
+
yield
|
|
175
|
+
finally:
|
|
176
|
+
self.broadcast_time += time.perf_counter() - start
|
|
177
|
+
|
|
178
|
+
@contextmanager
|
|
179
|
+
def profile_to_gpu(self):
|
|
180
|
+
start = time.perf_counter()
|
|
181
|
+
try:
|
|
182
|
+
yield
|
|
183
|
+
finally:
|
|
184
|
+
self.to_gpu_time += time.perf_counter() - start
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@dataclass
|
|
188
|
+
class StoreRequestStats:
|
|
189
|
+
request_id: int
|
|
190
|
+
num_tokens: int
|
|
191
|
+
start_time: float
|
|
192
|
+
end_time: float
|
|
193
|
+
process_tokens_time: float = 0
|
|
194
|
+
from_gpu_time: float = 0
|
|
195
|
+
put_time: float = 0
|
|
196
|
+
|
|
197
|
+
def time_to_store(self):
|
|
198
|
+
if self.end_time == 0:
|
|
199
|
+
return 0
|
|
200
|
+
return self.end_time - self.start_time
|
|
201
|
+
|
|
202
|
+
def store_speed(self):
|
|
203
|
+
if self.time_to_store() == 0:
|
|
204
|
+
return 0
|
|
205
|
+
return self.num_tokens / self.time_to_store()
|
|
206
|
+
|
|
207
|
+
@contextmanager
|
|
208
|
+
def profile_process_tokens(self):
|
|
209
|
+
start = time.perf_counter()
|
|
210
|
+
try:
|
|
211
|
+
yield
|
|
212
|
+
finally:
|
|
213
|
+
self.process_tokens_time += time.perf_counter() - start
|
|
214
|
+
|
|
215
|
+
@contextmanager
|
|
216
|
+
def profile_from_gpu(self):
|
|
217
|
+
start = time.perf_counter()
|
|
218
|
+
try:
|
|
219
|
+
yield
|
|
220
|
+
finally:
|
|
221
|
+
self.from_gpu_time += time.perf_counter() - start
|
|
222
|
+
|
|
223
|
+
@contextmanager
|
|
224
|
+
def profile_put(self):
|
|
225
|
+
start = time.perf_counter()
|
|
226
|
+
try:
|
|
227
|
+
yield
|
|
228
|
+
finally:
|
|
229
|
+
self.put_time += time.perf_counter() - start
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
@dataclass
|
|
233
|
+
class P2PTransferRequestStats:
|
|
234
|
+
num_tokens: int
|
|
235
|
+
start_time: float
|
|
236
|
+
end_time: float
|
|
237
|
+
|
|
238
|
+
def time_to_transfer(self):
|
|
239
|
+
if self.end_time == 0:
|
|
240
|
+
return 0
|
|
241
|
+
return self.end_time - self.start_time
|
|
242
|
+
|
|
243
|
+
def transfer_speed(self):
|
|
244
|
+
if self.time_to_transfer() == 0:
|
|
245
|
+
return 0
|
|
246
|
+
return self.num_tokens / self.time_to_transfer()
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class LMCStatsMonitor:
|
|
250
|
+
def __init__(self):
|
|
251
|
+
# Interval metrics that will be reset after each log
|
|
252
|
+
# Accumulate incremental values in the Prometheus Counter
|
|
253
|
+
self.interval_retrieve_requests = 0
|
|
254
|
+
self.interval_store_requests = 0
|
|
255
|
+
self.interval_lookup_requests = 0
|
|
256
|
+
self.interval_requested_tokens = 0 # total requested tokens retrieve
|
|
257
|
+
self.interval_hit_tokens = 0 # total hit tokens retrieve
|
|
258
|
+
self.interval_stored_tokens = 0 # total tokens tored in LMCache
|
|
259
|
+
self.interval_lookup_tokens = 0 # total requested tokens lookup
|
|
260
|
+
self.interval_lookup_hits = 0 # total hit tokens lookup
|
|
261
|
+
self.interval_vllm_hit_tokens = 0 # total hit tokens in vllm
|
|
262
|
+
self.interval_prompt_tokens = 0 # total prompt tokens
|
|
263
|
+
self.interval_lookup_0_hit_requests = 0
|
|
264
|
+
|
|
265
|
+
self.interval_num_slow_retrieval_by_time = 0
|
|
266
|
+
self.interval_num_slow_retrieval_by_speed = 0
|
|
267
|
+
|
|
268
|
+
# P2P transfer metrics
|
|
269
|
+
self.interval_p2p_requests = 0
|
|
270
|
+
self.interval_p2p_transferred_tokens = 0
|
|
271
|
+
self.p2p_requests: Dict[int, P2PTransferRequestStats] = {}
|
|
272
|
+
self.p2p_request_id = 0
|
|
273
|
+
|
|
274
|
+
# remote backends read/write metrics
|
|
275
|
+
self.interval_remote_read_requests = 0
|
|
276
|
+
self.interval_remote_read_bytes = 0
|
|
277
|
+
self.interval_remote_write_requests = 0
|
|
278
|
+
self.interval_remote_write_bytes = 0
|
|
279
|
+
|
|
280
|
+
# remote backends get/put cost time metrics
|
|
281
|
+
self.interval_remote_time_to_get: List[float] = []
|
|
282
|
+
self.interval_remote_time_to_put: List[float] = []
|
|
283
|
+
# the time of get value from remote backends synchronously,
|
|
284
|
+
# which includes rpc and schedule time
|
|
285
|
+
self.interval_remote_time_to_get_sync: List[float] = []
|
|
286
|
+
|
|
287
|
+
self.interval_remote_ping_latency = 0
|
|
288
|
+
self.interval_remote_ping_errors = 0
|
|
289
|
+
self.interval_remote_ping_success = 0
|
|
290
|
+
self.interval_remote_ping_error_code = 0 # 0 means success
|
|
291
|
+
|
|
292
|
+
self.interval_local_cpu_evict_count = 0
|
|
293
|
+
self.interval_local_cpu_evict_keys_count = 0
|
|
294
|
+
self.interval_local_cpu_evict_failed_count = 0
|
|
295
|
+
|
|
296
|
+
self.interval_forced_unpin_count = 0
|
|
297
|
+
|
|
298
|
+
self.local_cache_usage_bytes = 0
|
|
299
|
+
self.remote_cache_usage_bytes = 0
|
|
300
|
+
self.local_storage_usage_bytes = 0
|
|
301
|
+
|
|
302
|
+
self.active_memory_objs_count = 0
|
|
303
|
+
self.pinned_memory_objs_count = 0
|
|
304
|
+
|
|
305
|
+
self.retrieve_requests: Dict[int, RetrieveRequestStats] = {}
|
|
306
|
+
self.store_requests: Dict[int, StoreRequestStats] = {}
|
|
307
|
+
self.lookup_requests: Dict[int, LookupRequestStats] = {}
|
|
308
|
+
|
|
309
|
+
self.retrieve_request_id = 0
|
|
310
|
+
self.store_request_id = 0
|
|
311
|
+
self.lookup_request_id = 0
|
|
312
|
+
|
|
313
|
+
self.interval_request_cache_lifespan: Dict[int, float] = {}
|
|
314
|
+
self.reuse_chunk_id = 0
|
|
315
|
+
|
|
316
|
+
self._current_retrieve_stats: Optional[RetrieveRequestStats] = None
|
|
317
|
+
self.retrieve_time_threshold: float = 1e9
|
|
318
|
+
self.retrieve_token_speed_threshold: float = -1.0
|
|
319
|
+
self.last_retrieve_warning_time: float = 0.0
|
|
320
|
+
self.skipped_retrieve_warning_count: int = 0
|
|
321
|
+
|
|
322
|
+
def set_current_retrieve_stats(self, stats: RetrieveRequestStats):
|
|
323
|
+
self._current_retrieve_stats = stats
|
|
324
|
+
|
|
325
|
+
def get_current_retrieve_stats(self) -> Optional[RetrieveRequestStats]:
|
|
326
|
+
return self._current_retrieve_stats
|
|
327
|
+
|
|
328
|
+
def clear_current_retrieve_stats(self):
|
|
329
|
+
self._current_retrieve_stats = None
|
|
330
|
+
|
|
331
|
+
@thread_safe
|
|
332
|
+
def on_lookup_request(self, num_tokens: int) -> LookupRequestStats:
|
|
333
|
+
"""
|
|
334
|
+
This function is called when a lookup request is sent to the cache.
|
|
335
|
+
It will record the number of tokens requested.
|
|
336
|
+
"""
|
|
337
|
+
curr_time = time.perf_counter()
|
|
338
|
+
lookup_stats = LookupRequestStats(
|
|
339
|
+
request_id=self.lookup_request_id,
|
|
340
|
+
num_tokens=num_tokens,
|
|
341
|
+
hit_tokens=0,
|
|
342
|
+
is_finished=False,
|
|
343
|
+
start_time=curr_time,
|
|
344
|
+
)
|
|
345
|
+
self.interval_lookup_requests += 1
|
|
346
|
+
self.interval_lookup_tokens += num_tokens
|
|
347
|
+
self.lookup_requests[self.lookup_request_id] = lookup_stats
|
|
348
|
+
self.lookup_request_id += 1
|
|
349
|
+
return lookup_stats
|
|
350
|
+
|
|
351
|
+
@thread_safe
|
|
352
|
+
def on_lookup_finished(
|
|
353
|
+
self,
|
|
354
|
+
stats: LookupRequestStats,
|
|
355
|
+
num_hit_tokens: int,
|
|
356
|
+
):
|
|
357
|
+
"""
|
|
358
|
+
This function is called when a lookup request is finished.
|
|
359
|
+
It will record the number of tokens hit and track by node type.
|
|
360
|
+
|
|
361
|
+
Args:
|
|
362
|
+
stats: LookupRequestStats object
|
|
363
|
+
num_hit_tokens: Total number of tokens found in lookup
|
|
364
|
+
"""
|
|
365
|
+
curr_time = time.perf_counter()
|
|
366
|
+
assert stats.request_id in self.lookup_requests
|
|
367
|
+
stats.hit_tokens = num_hit_tokens
|
|
368
|
+
stats.is_finished = True
|
|
369
|
+
if stats.end_time == 0:
|
|
370
|
+
stats.end_time = curr_time
|
|
371
|
+
self.interval_lookup_hits += num_hit_tokens
|
|
372
|
+
if num_hit_tokens == 0:
|
|
373
|
+
self.interval_lookup_0_hit_requests += 1
|
|
374
|
+
|
|
375
|
+
@thread_safe
|
|
376
|
+
def on_retrieve_request(self, num_tokens: int) -> RetrieveRequestStats:
|
|
377
|
+
"""
|
|
378
|
+
Returns the internal "request id" that will be used in
|
|
379
|
+
on_retrieve_finished
|
|
380
|
+
"""
|
|
381
|
+
curr_time = time.perf_counter()
|
|
382
|
+
retrieve_stats = RetrieveRequestStats(
|
|
383
|
+
request_id=self.retrieve_request_id,
|
|
384
|
+
num_tokens=num_tokens,
|
|
385
|
+
local_hit_tokens=0,
|
|
386
|
+
remote_hit_tokens=0,
|
|
387
|
+
start_time=curr_time,
|
|
388
|
+
end_time=0,
|
|
389
|
+
)
|
|
390
|
+
self.interval_requested_tokens += num_tokens
|
|
391
|
+
self.interval_retrieve_requests += 1
|
|
392
|
+
self.retrieve_requests[self.retrieve_request_id] = retrieve_stats
|
|
393
|
+
self.retrieve_request_id += 1
|
|
394
|
+
self.set_current_retrieve_stats(retrieve_stats)
|
|
395
|
+
return retrieve_stats
|
|
396
|
+
|
|
397
|
+
@thread_safe
|
|
398
|
+
def on_retrieve_finished(
|
|
399
|
+
self,
|
|
400
|
+
retrieve_stats: RetrieveRequestStats,
|
|
401
|
+
num_retrieved_tokens: int,
|
|
402
|
+
):
|
|
403
|
+
curr_time = time.perf_counter()
|
|
404
|
+
assert retrieve_stats.request_id in self.retrieve_requests
|
|
405
|
+
retrieve_stats.local_hit_tokens = num_retrieved_tokens
|
|
406
|
+
if retrieve_stats.end_time == 0:
|
|
407
|
+
retrieve_stats.end_time = curr_time
|
|
408
|
+
self.interval_hit_tokens += num_retrieved_tokens
|
|
409
|
+
self.clear_current_retrieve_stats()
|
|
410
|
+
|
|
411
|
+
time_to_retrieve = retrieve_stats.time_to_retrieve()
|
|
412
|
+
retrieve_speed = retrieve_stats.retrieve_speed()
|
|
413
|
+
if time_to_retrieve > self.retrieve_time_threshold:
|
|
414
|
+
self.interval_num_slow_retrieval_by_time += 1
|
|
415
|
+
if 0 < retrieve_speed < self.retrieve_token_speed_threshold:
|
|
416
|
+
self.interval_num_slow_retrieval_by_speed += 1
|
|
417
|
+
|
|
418
|
+
# Log a warning if the retrieval performance is below defined thresholds:
|
|
419
|
+
# 1. Total time taken (time_to_retrieve) exceeds the maximum allowed time.
|
|
420
|
+
# 2. Retrieval speed (retrieve_speed) falls below the minimum required tokens/s.
|
|
421
|
+
# The warnings are rate-limited to once every 10 seconds to avoid log flooding.
|
|
422
|
+
if (
|
|
423
|
+
time_to_retrieve > self.retrieve_time_threshold
|
|
424
|
+
or 0 < retrieve_speed < self.retrieve_token_speed_threshold
|
|
425
|
+
):
|
|
426
|
+
if curr_time - self.last_retrieve_warning_time > 10.0:
|
|
427
|
+
logger.warning(
|
|
428
|
+
"Retrieve request %d surpassed threshold: "
|
|
429
|
+
"time_to_retrieve=%.4f s (threshold=%.4f s), "
|
|
430
|
+
"retrieve_speed=%.2f tokens/s (threshold=%.2f tokens/s). "
|
|
431
|
+
"Skipped %d slow retrieval logs in the last %.1f seconds. "
|
|
432
|
+
"Detailed metrics: "
|
|
433
|
+
"num_tokens=%d, local_hit_tokens=%d, remote_hit_tokens=%d, "
|
|
434
|
+
"process_tokens_time=%.5f s, "
|
|
435
|
+
"broadcast_time=%.5f s, "
|
|
436
|
+
"to_gpu_time=%.5f s, "
|
|
437
|
+
"detailed_metrics=%s",
|
|
438
|
+
retrieve_stats.request_id,
|
|
439
|
+
time_to_retrieve,
|
|
440
|
+
self.retrieve_time_threshold,
|
|
441
|
+
retrieve_speed,
|
|
442
|
+
self.retrieve_token_speed_threshold,
|
|
443
|
+
self.skipped_retrieve_warning_count,
|
|
444
|
+
curr_time - self.last_retrieve_warning_time,
|
|
445
|
+
retrieve_stats.num_tokens,
|
|
446
|
+
retrieve_stats.local_hit_tokens,
|
|
447
|
+
retrieve_stats.remote_hit_tokens,
|
|
448
|
+
retrieve_stats.process_tokens_time,
|
|
449
|
+
retrieve_stats.broadcast_time,
|
|
450
|
+
retrieve_stats.to_gpu_time,
|
|
451
|
+
retrieve_stats.detailed_metrics,
|
|
452
|
+
)
|
|
453
|
+
self.last_retrieve_warning_time = curr_time
|
|
454
|
+
self.skipped_retrieve_warning_count = 0
|
|
455
|
+
else:
|
|
456
|
+
self.skipped_retrieve_warning_count += 1
|
|
457
|
+
|
|
458
|
+
@thread_safe
|
|
459
|
+
def on_store_request(self, num_tokens: int) -> StoreRequestStats:
|
|
460
|
+
"""
|
|
461
|
+
Returns the internal "request id" that will be used in on_store_finished
|
|
462
|
+
"""
|
|
463
|
+
curr_time = time.perf_counter()
|
|
464
|
+
store_stats = StoreRequestStats(
|
|
465
|
+
request_id=self.store_request_id,
|
|
466
|
+
num_tokens=num_tokens,
|
|
467
|
+
start_time=curr_time,
|
|
468
|
+
end_time=0,
|
|
469
|
+
)
|
|
470
|
+
self.interval_store_requests += 1
|
|
471
|
+
self.interval_stored_tokens += num_tokens
|
|
472
|
+
self.store_requests[self.store_request_id] = store_stats
|
|
473
|
+
self.store_request_id += 1
|
|
474
|
+
return store_stats
|
|
475
|
+
|
|
476
|
+
@thread_safe
|
|
477
|
+
def on_store_finished(
|
|
478
|
+
self,
|
|
479
|
+
store_stats: StoreRequestStats,
|
|
480
|
+
num_stored_tokens: int = -1,
|
|
481
|
+
):
|
|
482
|
+
curr_time = time.perf_counter()
|
|
483
|
+
assert store_stats.request_id in self.store_requests
|
|
484
|
+
if store_stats.end_time == 0:
|
|
485
|
+
store_stats.end_time = curr_time
|
|
486
|
+
if num_stored_tokens >= 0:
|
|
487
|
+
store_stats.num_tokens = num_stored_tokens
|
|
488
|
+
|
|
489
|
+
@thread_safe
|
|
490
|
+
def on_p2p_transfer_request(self, num_tokens: int) -> int:
|
|
491
|
+
curr_time = time.time()
|
|
492
|
+
self.interval_p2p_requests += 1
|
|
493
|
+
self.p2p_requests[self.p2p_request_id] = P2PTransferRequestStats(
|
|
494
|
+
num_tokens=num_tokens,
|
|
495
|
+
start_time=curr_time,
|
|
496
|
+
end_time=0,
|
|
497
|
+
)
|
|
498
|
+
self.p2p_request_id += 1
|
|
499
|
+
return self.p2p_request_id - 1
|
|
500
|
+
|
|
501
|
+
@thread_safe
|
|
502
|
+
def on_p2p_transfer_finished(self, request_id: int):
|
|
503
|
+
curr_time = time.time()
|
|
504
|
+
assert request_id in self.p2p_requests
|
|
505
|
+
p2p_stats = self.p2p_requests[request_id]
|
|
506
|
+
self.interval_p2p_transferred_tokens += p2p_stats.num_tokens
|
|
507
|
+
p2p_stats.end_time = curr_time
|
|
508
|
+
|
|
509
|
+
@thread_safe
|
|
510
|
+
def on_chunk_reuse(self, time_interval: float):
|
|
511
|
+
"""
|
|
512
|
+
time_interval: float or int, in seconds
|
|
513
|
+
"""
|
|
514
|
+
self.interval_request_cache_lifespan[self.reuse_chunk_id] = time_interval / 60.0
|
|
515
|
+
self.reuse_chunk_id += 1
|
|
516
|
+
|
|
517
|
+
@thread_safe
|
|
518
|
+
def update_local_cache_usage(self, usage: int):
|
|
519
|
+
self.local_cache_usage_bytes = usage
|
|
520
|
+
|
|
521
|
+
@thread_safe
|
|
522
|
+
def update_remote_cache_usage(self, usage: int):
|
|
523
|
+
self.remote_cache_usage_bytes = usage
|
|
524
|
+
|
|
525
|
+
@thread_safe
|
|
526
|
+
def update_local_storage_usage(self, usage: int):
|
|
527
|
+
self.local_storage_usage_bytes = usage
|
|
528
|
+
|
|
529
|
+
@thread_safe
|
|
530
|
+
def update_interval_remote_read_metrics(self, read_bytes: int):
|
|
531
|
+
self.interval_remote_read_requests += 1
|
|
532
|
+
self.interval_remote_read_bytes += read_bytes
|
|
533
|
+
|
|
534
|
+
@thread_safe
|
|
535
|
+
def update_interval_remote_write_metrics(self, write_bytes: int):
|
|
536
|
+
self.interval_remote_write_requests += 1
|
|
537
|
+
self.interval_remote_write_bytes += write_bytes
|
|
538
|
+
|
|
539
|
+
@thread_safe
|
|
540
|
+
def update_interval_remote_time_to_get(self, get_time: float):
|
|
541
|
+
self.interval_remote_time_to_get.append(get_time)
|
|
542
|
+
|
|
543
|
+
@thread_safe
|
|
544
|
+
def update_interval_remote_time_to_put(self, put_time: float):
|
|
545
|
+
self.interval_remote_time_to_put.append(put_time)
|
|
546
|
+
|
|
547
|
+
@thread_safe
|
|
548
|
+
def update_interval_remote_time_to_get_sync(self, get_time_sync: float):
|
|
549
|
+
self.interval_remote_time_to_get_sync.append(get_time_sync)
|
|
550
|
+
|
|
551
|
+
@thread_safe
|
|
552
|
+
def update_remote_ping_latency(self, latency: float):
|
|
553
|
+
self.interval_remote_ping_latency = latency
|
|
554
|
+
|
|
555
|
+
@thread_safe
|
|
556
|
+
def update_remote_ping_error_code(self, error_code: int):
|
|
557
|
+
"""Update ping error code"""
|
|
558
|
+
self.interval_remote_ping_error_code = error_code
|
|
559
|
+
if error_code != 0:
|
|
560
|
+
self.interval_remote_ping_errors += 1
|
|
561
|
+
else:
|
|
562
|
+
self.interval_remote_ping_success += 1
|
|
563
|
+
|
|
564
|
+
@thread_safe
|
|
565
|
+
def update_local_cpu_evict_metrics(self, evict_keys_count: int):
|
|
566
|
+
self.interval_local_cpu_evict_count += 1
|
|
567
|
+
self.interval_local_cpu_evict_keys_count += evict_keys_count
|
|
568
|
+
|
|
569
|
+
@thread_safe
|
|
570
|
+
def update_local_cpu_evict_failed_count(self, evict_failed_count: int):
|
|
571
|
+
self.interval_local_cpu_evict_failed_count += evict_failed_count
|
|
572
|
+
|
|
573
|
+
@thread_safe
|
|
574
|
+
def update_forced_unpin_count(self, delta: int):
|
|
575
|
+
self.interval_forced_unpin_count += delta
|
|
576
|
+
|
|
577
|
+
@thread_safe
|
|
578
|
+
def update_active_memory_objs_count(self, active_memory_objs_count: int):
|
|
579
|
+
self.active_memory_objs_count = active_memory_objs_count
|
|
580
|
+
|
|
581
|
+
@thread_safe
|
|
582
|
+
def update_pinned_memory_objs_count(self, delta: int):
|
|
583
|
+
self.pinned_memory_objs_count += delta
|
|
584
|
+
|
|
585
|
+
@thread_safe
|
|
586
|
+
def update_interval_vllm_hit_tokens(self, delta: int):
|
|
587
|
+
self.interval_vllm_hit_tokens += delta
|
|
588
|
+
|
|
589
|
+
@thread_safe
|
|
590
|
+
def update_interval_prompt_tokens(self, delta: int):
|
|
591
|
+
self.interval_prompt_tokens += delta
|
|
592
|
+
|
|
593
|
+
def _clear(self):
|
|
594
|
+
"""
|
|
595
|
+
Clear all the distribution stats
|
|
596
|
+
"""
|
|
597
|
+
self.interval_retrieve_requests = 0
|
|
598
|
+
self.interval_store_requests = 0
|
|
599
|
+
self.interval_lookup_requests = 0
|
|
600
|
+
|
|
601
|
+
self.interval_requested_tokens = 0
|
|
602
|
+
self.interval_hit_tokens = 0
|
|
603
|
+
self.interval_stored_tokens = 0
|
|
604
|
+
self.interval_lookup_tokens = 0
|
|
605
|
+
self.interval_lookup_hits = 0
|
|
606
|
+
self.interval_vllm_hit_tokens = 0
|
|
607
|
+
self.interval_prompt_tokens = 0
|
|
608
|
+
|
|
609
|
+
self.interval_num_slow_retrieval_by_time = 0
|
|
610
|
+
self.interval_num_slow_retrieval_by_speed = 0
|
|
611
|
+
|
|
612
|
+
self.interval_remote_read_requests = 0
|
|
613
|
+
self.interval_remote_read_bytes = 0
|
|
614
|
+
self.interval_remote_write_requests = 0
|
|
615
|
+
self.interval_remote_write_bytes = 0
|
|
616
|
+
|
|
617
|
+
self.interval_remote_time_to_get.clear()
|
|
618
|
+
self.interval_remote_time_to_put.clear()
|
|
619
|
+
self.interval_remote_time_to_get_sync.clear()
|
|
620
|
+
|
|
621
|
+
self.interval_remote_ping_latency = 0
|
|
622
|
+
self.interval_remote_ping_errors = 0
|
|
623
|
+
self.interval_remote_ping_success = 0
|
|
624
|
+
self.interval_remote_ping_error_code = 0
|
|
625
|
+
|
|
626
|
+
self.interval_local_cpu_evict_count = 0
|
|
627
|
+
self.interval_local_cpu_evict_keys_count = 0
|
|
628
|
+
self.interval_local_cpu_evict_failed_count = 0
|
|
629
|
+
|
|
630
|
+
self.interval_forced_unpin_count = 0
|
|
631
|
+
|
|
632
|
+
self.interval_p2p_requests = 0
|
|
633
|
+
self.interval_p2p_transferred_tokens = 0
|
|
634
|
+
|
|
635
|
+
self.interval_lookup_0_hit_requests = 0
|
|
636
|
+
|
|
637
|
+
new_retrieve_requests = {}
|
|
638
|
+
for request_id, retrieve_stats in self.retrieve_requests.items():
|
|
639
|
+
if retrieve_stats.end_time == 0:
|
|
640
|
+
new_retrieve_requests[request_id] = retrieve_stats
|
|
641
|
+
self.retrieve_requests = new_retrieve_requests
|
|
642
|
+
|
|
643
|
+
new_store_requests = {}
|
|
644
|
+
for request_id, store_stats in self.store_requests.items():
|
|
645
|
+
if store_stats.end_time == 0:
|
|
646
|
+
new_store_requests[request_id] = store_stats
|
|
647
|
+
self.store_requests = new_store_requests
|
|
648
|
+
|
|
649
|
+
new_p2p_requests = {}
|
|
650
|
+
for request_id, p2p_stats in self.p2p_requests.items():
|
|
651
|
+
if p2p_stats.end_time == 0:
|
|
652
|
+
new_p2p_requests[request_id] = p2p_stats
|
|
653
|
+
self.p2p_requests = new_p2p_requests
|
|
654
|
+
|
|
655
|
+
new_lookup_requests = {}
|
|
656
|
+
for request_id, lookup_stats in self.lookup_requests.items():
|
|
657
|
+
if not lookup_stats.is_finished:
|
|
658
|
+
new_lookup_requests[request_id] = lookup_stats
|
|
659
|
+
self.lookup_requests = new_lookup_requests
|
|
660
|
+
|
|
661
|
+
self.interval_request_cache_lifespan.clear()
|
|
662
|
+
self.reuse_chunk_id = 0
|
|
663
|
+
|
|
664
|
+
@thread_safe
|
|
665
|
+
def get_stats_and_clear(self) -> LMCacheStats:
|
|
666
|
+
"""
|
|
667
|
+
This function should be called with by prometheus adapter with
|
|
668
|
+
a specific interval.
|
|
669
|
+
The function will return the latest states between the current
|
|
670
|
+
call and the previous call.
|
|
671
|
+
"""
|
|
672
|
+
# Calculate retrieve hit rate based on requests finished in this interval
|
|
673
|
+
finished_retrieve_stats = [
|
|
674
|
+
s for s in self.retrieve_requests.values() if s.end_time != 0
|
|
675
|
+
]
|
|
676
|
+
sum_finished_retrieve_requested_tokens = sum(
|
|
677
|
+
s.num_tokens for s in finished_retrieve_stats
|
|
678
|
+
)
|
|
679
|
+
sum_finished_retrieve_hit_tokens = sum(
|
|
680
|
+
s.local_hit_tokens + s.remote_hit_tokens for s in finished_retrieve_stats
|
|
681
|
+
)
|
|
682
|
+
retrieve_hit_rate = (
|
|
683
|
+
1
|
|
684
|
+
if len(finished_retrieve_stats) == 0
|
|
685
|
+
or sum_finished_retrieve_requested_tokens == 0
|
|
686
|
+
else sum_finished_retrieve_hit_tokens
|
|
687
|
+
/ sum_finished_retrieve_requested_tokens
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
# Calculate lookup hit rate based on requests finished in this interval
|
|
691
|
+
finished_lookup_stats = [
|
|
692
|
+
s for s in self.lookup_requests.values() if s.is_finished
|
|
693
|
+
]
|
|
694
|
+
sum_finished_lookup_requested = sum(s.num_tokens for s in finished_lookup_stats)
|
|
695
|
+
sum_finished_lookup_hit = sum(s.hit_tokens for s in finished_lookup_stats)
|
|
696
|
+
lookup_hit_rate = (
|
|
697
|
+
0
|
|
698
|
+
if sum_finished_lookup_requested == 0
|
|
699
|
+
else sum_finished_lookup_hit / sum_finished_lookup_requested
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
def filter_out_zeros(stats: Iterable[float]) -> List[float]:
|
|
703
|
+
return [x for x in stats if x != 0]
|
|
704
|
+
|
|
705
|
+
time_to_retrieve = filter_out_zeros(
|
|
706
|
+
stats.time_to_retrieve() for stats in self.retrieve_requests.values()
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
time_to_store = filter_out_zeros(
|
|
710
|
+
stats.time_to_store() for stats in self.store_requests.values()
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
time_to_lookup = filter_out_zeros(
|
|
714
|
+
stats.time_to_lookup() for stats in self.lookup_requests.values()
|
|
715
|
+
)
|
|
716
|
+
|
|
717
|
+
retrieve_speed = filter_out_zeros(
|
|
718
|
+
stats.retrieve_speed() for stats in self.retrieve_requests.values()
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
store_speed = filter_out_zeros(
|
|
722
|
+
stats.store_speed() for stats in self.store_requests.values()
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
# Granular profiling measurements
|
|
726
|
+
retrieve_process_tokens_time = filter_out_zeros(
|
|
727
|
+
stats.process_tokens_time for stats in self.retrieve_requests.values()
|
|
728
|
+
)
|
|
729
|
+
retrieve_broadcast_time = filter_out_zeros(
|
|
730
|
+
stats.broadcast_time for stats in self.retrieve_requests.values()
|
|
731
|
+
)
|
|
732
|
+
retrieve_to_gpu_time = filter_out_zeros(
|
|
733
|
+
stats.to_gpu_time for stats in self.retrieve_requests.values()
|
|
734
|
+
)
|
|
735
|
+
remote_backend_batched_get_blocking_time = filter_out_zeros(
|
|
736
|
+
stats.detailed_metrics.get("remote_backend_batched_get_blocking_time", 0.0)
|
|
737
|
+
for stats in self.retrieve_requests.values()
|
|
738
|
+
)
|
|
739
|
+
instrumented_connector_batched_get_time = filter_out_zeros(
|
|
740
|
+
stats.detailed_metrics.get("instrumented_connector_batched_get_time", 0.0)
|
|
741
|
+
for stats in self.retrieve_requests.values()
|
|
742
|
+
)
|
|
743
|
+
store_process_tokens_time = filter_out_zeros(
|
|
744
|
+
stats.process_tokens_time for stats in self.store_requests.values()
|
|
745
|
+
)
|
|
746
|
+
store_from_gpu_time = filter_out_zeros(
|
|
747
|
+
stats.from_gpu_time for stats in self.store_requests.values()
|
|
748
|
+
)
|
|
749
|
+
store_put_time = filter_out_zeros(
|
|
750
|
+
stats.put_time for stats in self.store_requests.values()
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
p2p_time_to_transfer = filter_out_zeros(
|
|
754
|
+
stats.time_to_transfer() for stats in self.p2p_requests.values()
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
p2p_transfer_speed = filter_out_zeros(
|
|
758
|
+
stats.transfer_speed() for stats in self.p2p_requests.values()
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
request_lookup_hit_rates = filter_out_zeros(
|
|
762
|
+
stats.hit_rate()
|
|
763
|
+
for stats in self.lookup_requests.values()
|
|
764
|
+
if stats.is_finished
|
|
765
|
+
)
|
|
766
|
+
|
|
767
|
+
request_lifespan = list(self.interval_request_cache_lifespan.values())
|
|
768
|
+
|
|
769
|
+
ret = LMCacheStats(
|
|
770
|
+
interval_retrieve_requests=self.interval_retrieve_requests,
|
|
771
|
+
interval_store_requests=self.interval_store_requests,
|
|
772
|
+
interval_lookup_requests=self.interval_lookup_requests,
|
|
773
|
+
interval_requested_tokens=self.interval_requested_tokens,
|
|
774
|
+
interval_hit_tokens=self.interval_hit_tokens,
|
|
775
|
+
interval_stored_tokens=self.interval_stored_tokens,
|
|
776
|
+
interval_lookup_tokens=self.interval_lookup_tokens,
|
|
777
|
+
interval_lookup_hits=self.interval_lookup_hits,
|
|
778
|
+
interval_remote_read_requests=self.interval_remote_read_requests,
|
|
779
|
+
interval_remote_read_bytes=self.interval_remote_read_bytes,
|
|
780
|
+
interval_remote_write_requests=self.interval_remote_write_requests,
|
|
781
|
+
interval_remote_write_bytes=self.interval_remote_write_bytes,
|
|
782
|
+
interval_remote_time_to_get=self.interval_remote_time_to_get.copy(),
|
|
783
|
+
interval_remote_time_to_put=self.interval_remote_time_to_put.copy(),
|
|
784
|
+
interval_remote_time_to_get_sync=self.interval_remote_time_to_get_sync.copy(),
|
|
785
|
+
interval_remote_ping_latency=self.interval_remote_ping_latency,
|
|
786
|
+
interval_remote_ping_errors=self.interval_remote_ping_errors,
|
|
787
|
+
interval_remote_ping_success=self.interval_remote_ping_success,
|
|
788
|
+
interval_remote_ping_error_code=self.interval_remote_ping_error_code,
|
|
789
|
+
retrieve_hit_rate=retrieve_hit_rate,
|
|
790
|
+
lookup_hit_rate=lookup_hit_rate,
|
|
791
|
+
interval_local_cpu_evict_count=self.interval_local_cpu_evict_count,
|
|
792
|
+
interval_local_cpu_evict_keys_count=self.interval_local_cpu_evict_keys_count,
|
|
793
|
+
interval_local_cpu_evict_failed_count=self.interval_local_cpu_evict_failed_count,
|
|
794
|
+
interval_forced_unpin_count=self.interval_forced_unpin_count,
|
|
795
|
+
local_cache_usage_bytes=self.local_cache_usage_bytes,
|
|
796
|
+
remote_cache_usage_bytes=self.remote_cache_usage_bytes,
|
|
797
|
+
local_storage_usage_bytes=self.local_storage_usage_bytes,
|
|
798
|
+
active_memory_objs_count=self.active_memory_objs_count,
|
|
799
|
+
pinned_memory_objs_count=self.pinned_memory_objs_count,
|
|
800
|
+
time_to_retrieve=time_to_retrieve,
|
|
801
|
+
time_to_store=time_to_store,
|
|
802
|
+
time_to_lookup=time_to_lookup,
|
|
803
|
+
retrieve_speed=retrieve_speed,
|
|
804
|
+
store_speed=store_speed,
|
|
805
|
+
retrieve_process_tokens_time=retrieve_process_tokens_time,
|
|
806
|
+
retrieve_broadcast_time=retrieve_broadcast_time,
|
|
807
|
+
retrieve_to_gpu_time=retrieve_to_gpu_time,
|
|
808
|
+
remote_backend_batched_get_blocking_time=remote_backend_batched_get_blocking_time, # noqa: E501
|
|
809
|
+
instrumented_connector_batched_get_time=instrumented_connector_batched_get_time, # noqa: E501
|
|
810
|
+
store_process_tokens_time=store_process_tokens_time,
|
|
811
|
+
store_from_gpu_time=store_from_gpu_time,
|
|
812
|
+
store_put_time=store_put_time,
|
|
813
|
+
interval_vllm_hit_tokens=self.interval_vllm_hit_tokens,
|
|
814
|
+
interval_p2p_requests=self.interval_p2p_requests,
|
|
815
|
+
interval_num_slow_retrieval_by_time=self.interval_num_slow_retrieval_by_time,
|
|
816
|
+
interval_num_slow_retrieval_by_speed=self.interval_num_slow_retrieval_by_speed,
|
|
817
|
+
interval_p2p_transferred_tokens=self.interval_p2p_transferred_tokens,
|
|
818
|
+
p2p_time_to_transfer=p2p_time_to_transfer,
|
|
819
|
+
p2p_transfer_speed=p2p_transfer_speed,
|
|
820
|
+
interval_lookup_hit_rates=request_lookup_hit_rates,
|
|
821
|
+
interval_request_cache_lifespan=request_lifespan,
|
|
822
|
+
interval_prompt_tokens=self.interval_prompt_tokens,
|
|
823
|
+
interval_lookup_0_hit_requests=self.interval_lookup_0_hit_requests,
|
|
824
|
+
)
|
|
825
|
+
self._clear()
|
|
826
|
+
return ret
|
|
827
|
+
|
|
828
|
+
_instance = None
|
|
829
|
+
|
|
830
|
+
@staticmethod
|
|
831
|
+
def GetOrCreate() -> "LMCStatsMonitor":
|
|
832
|
+
if LMCStatsMonitor._instance is None:
|
|
833
|
+
LMCStatsMonitor._instance = LMCStatsMonitor()
|
|
834
|
+
return LMCStatsMonitor._instance
|
|
835
|
+
|
|
836
|
+
@staticmethod
|
|
837
|
+
def DestroyInstance():
|
|
838
|
+
LMCStatsMonitor._instance = None
|
|
839
|
+
|
|
840
|
+
@staticmethod
|
|
841
|
+
def unregister_all_metrics():
|
|
842
|
+
collectors = list(REGISTRY._collector_to_names.keys())
|
|
843
|
+
for collector in collectors:
|
|
844
|
+
try:
|
|
845
|
+
REGISTRY.unregister(collector)
|
|
846
|
+
except KeyError:
|
|
847
|
+
pass
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
class PrometheusLogger:
|
|
851
|
+
_gauge_cls = prometheus_client.Gauge
|
|
852
|
+
_counter_cls = prometheus_client.Counter
|
|
853
|
+
_histogram_cls = prometheus_client.Histogram
|
|
854
|
+
|
|
855
|
+
def _create_counter(
|
|
856
|
+
self, name: str, documentation: str, labelnames: List[str]
|
|
857
|
+
) -> prometheus_client.Counter:
|
|
858
|
+
"""Create a Counter and register it for reset_counters()."""
|
|
859
|
+
counter = self._counter_cls(
|
|
860
|
+
name=name, documentation=documentation, labelnames=labelnames
|
|
861
|
+
)
|
|
862
|
+
self._counters.append(counter)
|
|
863
|
+
return counter
|
|
864
|
+
|
|
865
|
+
def _create_histogram(
|
|
866
|
+
self,
|
|
867
|
+
name: str,
|
|
868
|
+
documentation: str,
|
|
869
|
+
labelnames: List[str],
|
|
870
|
+
buckets: Sequence[float],
|
|
871
|
+
) -> prometheus_client.Histogram:
|
|
872
|
+
"""Create a Histogram and register it for reset_histograms().
|
|
873
|
+
|
|
874
|
+
If the ``config`` object's extra_config contains a key
|
|
875
|
+
``histogram_bucket_<short_name>`` (where ``<short_name>``
|
|
876
|
+
is the metric name without the ``lmcache:`` prefix),
|
|
877
|
+
the value will be used as the bucket list, overriding
|
|
878
|
+
the default *buckets* argument.
|
|
879
|
+
"""
|
|
880
|
+
short_name = name.split(":", 1)[-1]
|
|
881
|
+
config_key = "histogram_bucket_%s" % short_name
|
|
882
|
+
custom = (
|
|
883
|
+
self.config.get_extra_config_value(config_key)
|
|
884
|
+
if self.config is not None
|
|
885
|
+
else None
|
|
886
|
+
)
|
|
887
|
+
if custom is not None:
|
|
888
|
+
buckets = custom
|
|
889
|
+
logger.info(
|
|
890
|
+
"Using custom buckets for histogram %s from extra_config key '%s'",
|
|
891
|
+
name,
|
|
892
|
+
config_key,
|
|
893
|
+
)
|
|
894
|
+
histogram = self._histogram_cls(
|
|
895
|
+
name=name,
|
|
896
|
+
documentation=documentation,
|
|
897
|
+
labelnames=labelnames,
|
|
898
|
+
buckets=buckets,
|
|
899
|
+
)
|
|
900
|
+
self._histograms.append(histogram)
|
|
901
|
+
return histogram
|
|
902
|
+
|
|
903
|
+
def __init__(
|
|
904
|
+
self,
|
|
905
|
+
metadata: LMCacheMetadata,
|
|
906
|
+
config: Optional["LMCacheEngineConfig"] = None,
|
|
907
|
+
):
|
|
908
|
+
# Ensure PROMETHEUS_MULTIPROC_DIR is set before any metric registration
|
|
909
|
+
if "PROMETHEUS_MULTIPROC_DIR" not in os.environ:
|
|
910
|
+
default_dir = "/tmp/lmcache_prometheus"
|
|
911
|
+
os.environ["PROMETHEUS_MULTIPROC_DIR"] = default_dir
|
|
912
|
+
if not os.path.exists(default_dir):
|
|
913
|
+
os.makedirs(default_dir, exist_ok=True)
|
|
914
|
+
|
|
915
|
+
self.metadata = metadata
|
|
916
|
+
self.config = config
|
|
917
|
+
|
|
918
|
+
self.labels = self._metadata_to_labels(metadata)
|
|
919
|
+
labelnames = list(self.labels.keys())
|
|
920
|
+
|
|
921
|
+
# List to track all counters/histograms for reset methods
|
|
922
|
+
self._counters: List[prometheus_client.Counter] = []
|
|
923
|
+
self._histograms: List[prometheus_client.Histogram] = []
|
|
924
|
+
|
|
925
|
+
self.counter_num_retrieve_requests = self._create_counter(
|
|
926
|
+
name="lmcache:num_retrieve_requests",
|
|
927
|
+
documentation="Total number of retrieve requests sent to lmcache",
|
|
928
|
+
labelnames=labelnames,
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
self.counter_num_store_requests = self._create_counter(
|
|
932
|
+
name="lmcache:num_store_requests",
|
|
933
|
+
documentation="Total number of store requests sent to lmcache",
|
|
934
|
+
labelnames=labelnames,
|
|
935
|
+
)
|
|
936
|
+
|
|
937
|
+
self.counter_num_lookup_requests = self._create_counter(
|
|
938
|
+
name="lmcache:num_lookup_requests",
|
|
939
|
+
documentation="Total number of lookup requests sent to lmcache",
|
|
940
|
+
labelnames=labelnames,
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
self.counter_num_requested_tokens = self._create_counter(
|
|
944
|
+
name="lmcache:num_requested_tokens",
|
|
945
|
+
documentation="Total number of tokens requested from lmcache",
|
|
946
|
+
labelnames=labelnames,
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
self.counter_num_hit_tokens = self._create_counter(
|
|
950
|
+
name="lmcache:num_hit_tokens",
|
|
951
|
+
documentation="Total number of tokens hit in lmcache",
|
|
952
|
+
labelnames=labelnames,
|
|
953
|
+
)
|
|
954
|
+
|
|
955
|
+
self.counter_num_stored_tokens = self._create_counter(
|
|
956
|
+
name="lmcache:num_stored_tokens",
|
|
957
|
+
documentation=(
|
|
958
|
+
"Total number of tokens stored in lmcache including evicted ones"
|
|
959
|
+
),
|
|
960
|
+
labelnames=labelnames,
|
|
961
|
+
)
|
|
962
|
+
|
|
963
|
+
self.counter_num_lookup_tokens = self._create_counter(
|
|
964
|
+
name="lmcache:num_lookup_tokens",
|
|
965
|
+
documentation="Total number of tokens requested in lookup from lmcache",
|
|
966
|
+
labelnames=labelnames,
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
self.counter_num_lookup_hits = self._create_counter(
|
|
970
|
+
name="lmcache:num_lookup_hits",
|
|
971
|
+
documentation="Total number of tokens hit in lookup from lmcache",
|
|
972
|
+
labelnames=labelnames,
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
self.counter_num_vllm_hit_tokens = self._create_counter(
|
|
976
|
+
name="lmcache:num_vllm_hit_tokens",
|
|
977
|
+
documentation="Number of hit tokens in vllm",
|
|
978
|
+
labelnames=labelnames,
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
self.counter_num_prompt_tokens = self._create_counter(
|
|
982
|
+
name="lmcache:num_prompt_tokens",
|
|
983
|
+
documentation="Number of prompt tokens in lmcache",
|
|
984
|
+
labelnames=labelnames,
|
|
985
|
+
)
|
|
986
|
+
|
|
987
|
+
self.counter_num_remote_read_requests = self._create_counter(
|
|
988
|
+
name="lmcache:num_remote_read_requests",
|
|
989
|
+
documentation="Total number of requests read from "
|
|
990
|
+
"remote backends in lmcache",
|
|
991
|
+
labelnames=labelnames,
|
|
992
|
+
)
|
|
993
|
+
|
|
994
|
+
self.counter_num_remote_read_bytes = self._create_counter(
|
|
995
|
+
name="lmcache:num_remote_read_bytes",
|
|
996
|
+
documentation="Total number of bytes read from remote backends in lmcache",
|
|
997
|
+
labelnames=labelnames,
|
|
998
|
+
)
|
|
999
|
+
|
|
1000
|
+
self.counter_num_remote_write_requests = self._create_counter(
|
|
1001
|
+
name="lmcache:num_remote_write_requests",
|
|
1002
|
+
documentation="Total number of requests write to "
|
|
1003
|
+
"remote backends in lmcache",
|
|
1004
|
+
labelnames=labelnames,
|
|
1005
|
+
)
|
|
1006
|
+
|
|
1007
|
+
self.counter_num_remote_write_bytes = self._create_counter(
|
|
1008
|
+
name="lmcache:num_remote_write_bytes",
|
|
1009
|
+
documentation="Total number of bytes write to remote backends in lmcache",
|
|
1010
|
+
labelnames=labelnames,
|
|
1011
|
+
)
|
|
1012
|
+
|
|
1013
|
+
self.counter_local_cpu_evict_count = self._create_counter(
|
|
1014
|
+
name="lmcache:local_cpu_evict_count",
|
|
1015
|
+
documentation="Total number of evict in local cpu backend",
|
|
1016
|
+
labelnames=labelnames,
|
|
1017
|
+
)
|
|
1018
|
+
|
|
1019
|
+
self.counter_local_cpu_evict_keys_count = self._create_counter(
|
|
1020
|
+
name="lmcache:local_cpu_evict_keys_count",
|
|
1021
|
+
documentation="Total number of evict keys in local cpu backend",
|
|
1022
|
+
labelnames=labelnames,
|
|
1023
|
+
)
|
|
1024
|
+
|
|
1025
|
+
self.counter_local_cpu_evict_failed_count = self._create_counter(
|
|
1026
|
+
name="lmcache:local_cpu_evict_failed_count",
|
|
1027
|
+
documentation="Total number of failed eviction in local cpu backend",
|
|
1028
|
+
labelnames=labelnames,
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
self.counter_forced_unpin_count = self._create_counter(
|
|
1032
|
+
name="lmcache:forced_unpin_count",
|
|
1033
|
+
documentation="Total number of forced unpin due to timeout",
|
|
1034
|
+
labelnames=labelnames,
|
|
1035
|
+
)
|
|
1036
|
+
|
|
1037
|
+
self.counter_lookup_0_hit_requests = self._create_counter(
|
|
1038
|
+
name="lmcache:lookup_0_hit_requests",
|
|
1039
|
+
documentation="Total number of 0 hit lookup requests",
|
|
1040
|
+
labelnames=labelnames,
|
|
1041
|
+
)
|
|
1042
|
+
|
|
1043
|
+
self.counter_num_slow_retrieval_by_time = self._create_counter(
|
|
1044
|
+
name="lmcache:num_slow_retrieval_by_time",
|
|
1045
|
+
documentation="Total number of slow retrievals by time threshold",
|
|
1046
|
+
labelnames=labelnames,
|
|
1047
|
+
)
|
|
1048
|
+
|
|
1049
|
+
self.counter_num_slow_retrieval_by_speed = self._create_counter(
|
|
1050
|
+
name="lmcache:num_slow_retrieval_by_speed",
|
|
1051
|
+
documentation="Total number of slow retrievals by speed threshold",
|
|
1052
|
+
labelnames=labelnames,
|
|
1053
|
+
)
|
|
1054
|
+
|
|
1055
|
+
self.gauge_retrieve_hit_rate = self._gauge_cls(
|
|
1056
|
+
name="lmcache:retrieve_hit_rate",
|
|
1057
|
+
documentation="Hit rate of lmcache retrieve requests since last log",
|
|
1058
|
+
labelnames=labelnames,
|
|
1059
|
+
multiprocess_mode="livemostrecent",
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
self.gauge_lookup_hit_rate = self._gauge_cls(
|
|
1063
|
+
name="lmcache:lookup_hit_rate",
|
|
1064
|
+
documentation="Hit rate of lmcache lookup requests since last log",
|
|
1065
|
+
labelnames=labelnames,
|
|
1066
|
+
multiprocess_mode="livemostrecent",
|
|
1067
|
+
)
|
|
1068
|
+
|
|
1069
|
+
self.gauge_local_cache_usage = self._gauge_cls(
|
|
1070
|
+
name="lmcache:local_cache_usage",
|
|
1071
|
+
documentation="Local cache usage (bytes) of lmcache",
|
|
1072
|
+
labelnames=labelnames,
|
|
1073
|
+
multiprocess_mode="sum",
|
|
1074
|
+
)
|
|
1075
|
+
|
|
1076
|
+
self.gauge_remote_cache_usage = self._gauge_cls(
|
|
1077
|
+
name="lmcache:remote_cache_usage",
|
|
1078
|
+
documentation="Remote cache usage (bytes) of lmcache",
|
|
1079
|
+
labelnames=labelnames,
|
|
1080
|
+
multiprocess_mode="sum",
|
|
1081
|
+
)
|
|
1082
|
+
|
|
1083
|
+
self.gauge_local_storage_usage = self._gauge_cls(
|
|
1084
|
+
name="lmcache:local_storage_usage",
|
|
1085
|
+
documentation="Local storage usage (bytes) of lmcache",
|
|
1086
|
+
labelnames=labelnames,
|
|
1087
|
+
multiprocess_mode="sum",
|
|
1088
|
+
)
|
|
1089
|
+
|
|
1090
|
+
self.gauge_active_memory_objs_count = self._gauge_cls(
|
|
1091
|
+
name="lmcache:active_memory_objs_count",
|
|
1092
|
+
documentation="The number of active memory objects",
|
|
1093
|
+
labelnames=labelnames,
|
|
1094
|
+
multiprocess_mode="sum",
|
|
1095
|
+
)
|
|
1096
|
+
|
|
1097
|
+
self.gauge_pinned_memory_objs_count = self._gauge_cls(
|
|
1098
|
+
name="lmcache:pinned_memory_objs_count",
|
|
1099
|
+
documentation="The number of pinned memory objects",
|
|
1100
|
+
labelnames=labelnames,
|
|
1101
|
+
multiprocess_mode="sum",
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
time_to_retrieve_buckets = [
|
|
1105
|
+
0.001,
|
|
1106
|
+
0.005,
|
|
1107
|
+
0.01,
|
|
1108
|
+
0.02,
|
|
1109
|
+
0.04,
|
|
1110
|
+
0.06,
|
|
1111
|
+
0.08,
|
|
1112
|
+
0.1,
|
|
1113
|
+
0.25,
|
|
1114
|
+
0.5,
|
|
1115
|
+
0.75,
|
|
1116
|
+
1.0,
|
|
1117
|
+
2.5,
|
|
1118
|
+
5.0,
|
|
1119
|
+
7.5,
|
|
1120
|
+
10.0,
|
|
1121
|
+
]
|
|
1122
|
+
self.histogram_time_to_retrieve = self._create_histogram(
|
|
1123
|
+
name="lmcache:time_to_retrieve",
|
|
1124
|
+
documentation="Time to retrieve from lmcache (seconds)",
|
|
1125
|
+
labelnames=labelnames,
|
|
1126
|
+
buckets=time_to_retrieve_buckets,
|
|
1127
|
+
)
|
|
1128
|
+
|
|
1129
|
+
time_to_store_buckets = [
|
|
1130
|
+
0.001,
|
|
1131
|
+
0.005,
|
|
1132
|
+
0.01,
|
|
1133
|
+
0.02,
|
|
1134
|
+
0.04,
|
|
1135
|
+
0.06,
|
|
1136
|
+
0.08,
|
|
1137
|
+
0.1,
|
|
1138
|
+
0.25,
|
|
1139
|
+
0.5,
|
|
1140
|
+
0.75,
|
|
1141
|
+
1.0,
|
|
1142
|
+
2.5,
|
|
1143
|
+
5.0,
|
|
1144
|
+
7.5,
|
|
1145
|
+
10.0,
|
|
1146
|
+
]
|
|
1147
|
+
self.histogram_time_to_store = self._create_histogram(
|
|
1148
|
+
name="lmcache:time_to_store",
|
|
1149
|
+
documentation="Time to store to lmcache (seconds)",
|
|
1150
|
+
labelnames=labelnames,
|
|
1151
|
+
buckets=time_to_store_buckets,
|
|
1152
|
+
)
|
|
1153
|
+
|
|
1154
|
+
time_to_lookup_buckets = [
|
|
1155
|
+
0.00001 * 2**i for i in range(20)
|
|
1156
|
+
] # 0.01 ms to 5000 ms
|
|
1157
|
+
self.histogram_time_to_lookup = self._create_histogram(
|
|
1158
|
+
name="lmcache:time_to_lookup",
|
|
1159
|
+
documentation="Time to lookup in lmcache (seconds)",
|
|
1160
|
+
labelnames=labelnames,
|
|
1161
|
+
buckets=time_to_lookup_buckets,
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
profiling_buckets = [0.00001 * 2**i for i in range(20)] # 0.01 ms to 5000 ms
|
|
1165
|
+
self.histogram_retrieve_process_tokens_time = self._create_histogram(
|
|
1166
|
+
name="lmcache:retrieve_process_tokens_time",
|
|
1167
|
+
documentation="Time to process tokens in retrieve (seconds)",
|
|
1168
|
+
labelnames=labelnames,
|
|
1169
|
+
buckets=profiling_buckets,
|
|
1170
|
+
)
|
|
1171
|
+
self.histogram_retrieve_broadcast_time = self._create_histogram(
|
|
1172
|
+
name="lmcache:retrieve_broadcast_time",
|
|
1173
|
+
documentation="Time to broadcast memory objects in retrieve (seconds)",
|
|
1174
|
+
labelnames=labelnames,
|
|
1175
|
+
buckets=profiling_buckets,
|
|
1176
|
+
)
|
|
1177
|
+
self.histogram_retrieve_to_gpu_time = self._create_histogram(
|
|
1178
|
+
name="lmcache:retrieve_to_gpu_time",
|
|
1179
|
+
documentation="Time to move data to GPU in retrieve (seconds)",
|
|
1180
|
+
labelnames=labelnames,
|
|
1181
|
+
buckets=profiling_buckets,
|
|
1182
|
+
)
|
|
1183
|
+
self.histogram_remote_backend_batched_get_blocking_time = (
|
|
1184
|
+
self._create_histogram(
|
|
1185
|
+
name="lmcache:remote_backend_batched_get_blocking_time",
|
|
1186
|
+
documentation="Time to get data from remote backend (seconds)",
|
|
1187
|
+
labelnames=labelnames,
|
|
1188
|
+
buckets=profiling_buckets,
|
|
1189
|
+
)
|
|
1190
|
+
)
|
|
1191
|
+
self.histogram_instrumented_connector_batched_get_time = self._create_histogram(
|
|
1192
|
+
name="lmcache:instrumented_connector_batched_get_time",
|
|
1193
|
+
documentation="Time used by the connector (seconds)",
|
|
1194
|
+
labelnames=labelnames,
|
|
1195
|
+
buckets=profiling_buckets,
|
|
1196
|
+
)
|
|
1197
|
+
self.histogram_store_process_tokens_time = self._create_histogram(
|
|
1198
|
+
name="lmcache:store_process_tokens_time",
|
|
1199
|
+
documentation="Time to process tokens in store (seconds)",
|
|
1200
|
+
labelnames=labelnames,
|
|
1201
|
+
buckets=profiling_buckets,
|
|
1202
|
+
)
|
|
1203
|
+
self.histogram_store_from_gpu_time = self._create_histogram(
|
|
1204
|
+
name="lmcache:store_from_gpu_time",
|
|
1205
|
+
documentation="Time to move data from GPU in store (seconds)",
|
|
1206
|
+
labelnames=labelnames,
|
|
1207
|
+
buckets=profiling_buckets,
|
|
1208
|
+
)
|
|
1209
|
+
self.histogram_store_put_time = self._create_histogram(
|
|
1210
|
+
name="lmcache:store_put_time",
|
|
1211
|
+
documentation="Time to put data to storage in store (seconds)",
|
|
1212
|
+
labelnames=labelnames,
|
|
1213
|
+
buckets=profiling_buckets,
|
|
1214
|
+
)
|
|
1215
|
+
|
|
1216
|
+
retrieve_speed_buckets = [
|
|
1217
|
+
1,
|
|
1218
|
+
8,
|
|
1219
|
+
16,
|
|
1220
|
+
32,
|
|
1221
|
+
64,
|
|
1222
|
+
128,
|
|
1223
|
+
256,
|
|
1224
|
+
512,
|
|
1225
|
+
1024,
|
|
1226
|
+
2048,
|
|
1227
|
+
4096,
|
|
1228
|
+
8192,
|
|
1229
|
+
16384,
|
|
1230
|
+
32768,
|
|
1231
|
+
65536,
|
|
1232
|
+
]
|
|
1233
|
+
self.histogram_retrieve_speed = self._create_histogram(
|
|
1234
|
+
name="lmcache:retrieve_speed",
|
|
1235
|
+
documentation="Retrieve speed of lmcache (tokens per second)",
|
|
1236
|
+
labelnames=labelnames,
|
|
1237
|
+
buckets=retrieve_speed_buckets,
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
store_speed_buckets = [
|
|
1241
|
+
1,
|
|
1242
|
+
8,
|
|
1243
|
+
16,
|
|
1244
|
+
32,
|
|
1245
|
+
64,
|
|
1246
|
+
128,
|
|
1247
|
+
256,
|
|
1248
|
+
512,
|
|
1249
|
+
1024,
|
|
1250
|
+
2048,
|
|
1251
|
+
4096,
|
|
1252
|
+
8192,
|
|
1253
|
+
16384,
|
|
1254
|
+
32768,
|
|
1255
|
+
65536,
|
|
1256
|
+
]
|
|
1257
|
+
self.histogram_store_speed = self._create_histogram(
|
|
1258
|
+
name="lmcache:store_speed",
|
|
1259
|
+
documentation="Store speed of lmcache (tokens per second)",
|
|
1260
|
+
labelnames=labelnames,
|
|
1261
|
+
buckets=store_speed_buckets,
|
|
1262
|
+
)
|
|
1263
|
+
|
|
1264
|
+
# P2P transfer metrics
|
|
1265
|
+
p2p_time_buckets = [
|
|
1266
|
+
0.001, # 1ms
|
|
1267
|
+
0.005, # 5ms
|
|
1268
|
+
0.01, # 10ms
|
|
1269
|
+
0.02, # 20ms
|
|
1270
|
+
0.04, # 40ms
|
|
1271
|
+
0.06, # 60ms
|
|
1272
|
+
0.08, # 80ms
|
|
1273
|
+
0.1, # 100ms
|
|
1274
|
+
0.25, # 250ms
|
|
1275
|
+
0.5, # 500ms
|
|
1276
|
+
0.75, # 750ms
|
|
1277
|
+
1.0, # 1s
|
|
1278
|
+
2.5, # 2.5s
|
|
1279
|
+
5.0, # 5s
|
|
1280
|
+
7.5, # 7.5s
|
|
1281
|
+
10.0, # 10s
|
|
1282
|
+
]
|
|
1283
|
+
self.histogram_p2p_time_to_transfer = self._create_histogram(
|
|
1284
|
+
name="lmcache:p2p_time_to_transfer",
|
|
1285
|
+
documentation="Time to transfer via P2P (seconds)",
|
|
1286
|
+
labelnames=labelnames,
|
|
1287
|
+
buckets=p2p_time_buckets,
|
|
1288
|
+
)
|
|
1289
|
+
|
|
1290
|
+
p2p_speed_buckets = [
|
|
1291
|
+
1,
|
|
1292
|
+
8,
|
|
1293
|
+
16,
|
|
1294
|
+
32,
|
|
1295
|
+
64,
|
|
1296
|
+
128,
|
|
1297
|
+
256,
|
|
1298
|
+
512,
|
|
1299
|
+
1024,
|
|
1300
|
+
2048,
|
|
1301
|
+
4096,
|
|
1302
|
+
8192,
|
|
1303
|
+
16384,
|
|
1304
|
+
32768,
|
|
1305
|
+
65536,
|
|
1306
|
+
]
|
|
1307
|
+
self.histogram_p2p_transfer_speed = self._create_histogram(
|
|
1308
|
+
name="lmcache:p2p_transfer_speed",
|
|
1309
|
+
documentation="P2P transfer speed (tokens per second)",
|
|
1310
|
+
labelnames=labelnames,
|
|
1311
|
+
buckets=p2p_speed_buckets,
|
|
1312
|
+
)
|
|
1313
|
+
|
|
1314
|
+
remote_time_to_get = [
|
|
1315
|
+
1,
|
|
1316
|
+
5,
|
|
1317
|
+
10,
|
|
1318
|
+
20,
|
|
1319
|
+
40,
|
|
1320
|
+
60,
|
|
1321
|
+
80,
|
|
1322
|
+
100,
|
|
1323
|
+
250,
|
|
1324
|
+
500,
|
|
1325
|
+
750,
|
|
1326
|
+
1000,
|
|
1327
|
+
2500,
|
|
1328
|
+
5000,
|
|
1329
|
+
7500,
|
|
1330
|
+
10000,
|
|
1331
|
+
]
|
|
1332
|
+
self.histogram_remote_time_to_get = self._create_histogram(
|
|
1333
|
+
name="lmcache:remote_time_to_get",
|
|
1334
|
+
documentation="Time to get from remote backends (ms)",
|
|
1335
|
+
labelnames=labelnames,
|
|
1336
|
+
buckets=remote_time_to_get,
|
|
1337
|
+
)
|
|
1338
|
+
|
|
1339
|
+
remote_time_to_put = [
|
|
1340
|
+
1,
|
|
1341
|
+
5,
|
|
1342
|
+
10,
|
|
1343
|
+
20,
|
|
1344
|
+
40,
|
|
1345
|
+
60,
|
|
1346
|
+
80,
|
|
1347
|
+
100,
|
|
1348
|
+
250,
|
|
1349
|
+
500,
|
|
1350
|
+
750,
|
|
1351
|
+
1000,
|
|
1352
|
+
2500,
|
|
1353
|
+
5000,
|
|
1354
|
+
7500,
|
|
1355
|
+
10000,
|
|
1356
|
+
]
|
|
1357
|
+
self.histogram_remote_time_to_put = self._create_histogram(
|
|
1358
|
+
name="lmcache:remote_time_to_put",
|
|
1359
|
+
documentation="Time to put to remote backends (ms)",
|
|
1360
|
+
labelnames=labelnames,
|
|
1361
|
+
buckets=remote_time_to_put,
|
|
1362
|
+
)
|
|
1363
|
+
|
|
1364
|
+
remote_time_to_get_sync = [
|
|
1365
|
+
1,
|
|
1366
|
+
5,
|
|
1367
|
+
10,
|
|
1368
|
+
20,
|
|
1369
|
+
40,
|
|
1370
|
+
60,
|
|
1371
|
+
80,
|
|
1372
|
+
100,
|
|
1373
|
+
250,
|
|
1374
|
+
500,
|
|
1375
|
+
750,
|
|
1376
|
+
1000,
|
|
1377
|
+
2500,
|
|
1378
|
+
5000,
|
|
1379
|
+
7500,
|
|
1380
|
+
10000,
|
|
1381
|
+
]
|
|
1382
|
+
self.histogram_remote_time_to_get_sync = self._create_histogram(
|
|
1383
|
+
name="lmcache:remote_time_to_get_sync",
|
|
1384
|
+
documentation="Time to get from remote backends synchronously(ms)",
|
|
1385
|
+
labelnames=labelnames,
|
|
1386
|
+
buckets=remote_time_to_get_sync,
|
|
1387
|
+
)
|
|
1388
|
+
|
|
1389
|
+
request_cache_hit_rate = [
|
|
1390
|
+
0.1,
|
|
1391
|
+
0.2,
|
|
1392
|
+
0.3,
|
|
1393
|
+
0.4,
|
|
1394
|
+
0.5,
|
|
1395
|
+
0.6,
|
|
1396
|
+
0.7,
|
|
1397
|
+
0.8,
|
|
1398
|
+
0.9,
|
|
1399
|
+
1.0,
|
|
1400
|
+
]
|
|
1401
|
+
self.histogram_request_cache_hit_rate = self._create_histogram(
|
|
1402
|
+
name="lmcache:request_cache_hit_rate",
|
|
1403
|
+
documentation="Request cache hit rate",
|
|
1404
|
+
labelnames=labelnames,
|
|
1405
|
+
buckets=request_cache_hit_rate,
|
|
1406
|
+
)
|
|
1407
|
+
|
|
1408
|
+
request_cache_lifespan_buckets = [
|
|
1409
|
+
0,
|
|
1410
|
+
1,
|
|
1411
|
+
5,
|
|
1412
|
+
10,
|
|
1413
|
+
20,
|
|
1414
|
+
40,
|
|
1415
|
+
60,
|
|
1416
|
+
80,
|
|
1417
|
+
100,
|
|
1418
|
+
250,
|
|
1419
|
+
500,
|
|
1420
|
+
750,
|
|
1421
|
+
1000,
|
|
1422
|
+
2500,
|
|
1423
|
+
5000,
|
|
1424
|
+
]
|
|
1425
|
+
self.histogram_request_cache_lifespan = self._create_histogram(
|
|
1426
|
+
name="lmcache:request_cache_lifespan",
|
|
1427
|
+
documentation="Request cache lifespan in minutes",
|
|
1428
|
+
labelnames=labelnames,
|
|
1429
|
+
buckets=request_cache_lifespan_buckets,
|
|
1430
|
+
)
|
|
1431
|
+
|
|
1432
|
+
# Ping latency metrics: use a gauge to record the latest ping latency
|
|
1433
|
+
self.gauge_remote_ping_latency = self._gauge_cls(
|
|
1434
|
+
name="lmcache:remote_ping_latency",
|
|
1435
|
+
documentation="Latest ping latency to remote backends (ms)",
|
|
1436
|
+
labelnames=labelnames,
|
|
1437
|
+
multiprocess_mode="livemostrecent",
|
|
1438
|
+
)
|
|
1439
|
+
self.counter_remote_ping_errors = self._create_counter(
|
|
1440
|
+
name="lmcache:remote_ping_errors",
|
|
1441
|
+
documentation="Number of ping errors to remote backends",
|
|
1442
|
+
labelnames=labelnames,
|
|
1443
|
+
)
|
|
1444
|
+
self.counter_remote_ping_successes = self._create_counter(
|
|
1445
|
+
name="lmcache:remote_ping_successes",
|
|
1446
|
+
documentation="Number of ping successes to remote backends",
|
|
1447
|
+
labelnames=labelnames,
|
|
1448
|
+
)
|
|
1449
|
+
self.gauge_remote_ping_error_code = self._gauge_cls(
|
|
1450
|
+
name="lmcache:remote_ping_error_code",
|
|
1451
|
+
documentation="Latest ping error code to remote backends",
|
|
1452
|
+
labelnames=labelnames,
|
|
1453
|
+
multiprocess_mode="livemostrecent",
|
|
1454
|
+
)
|
|
1455
|
+
self._dynamic_metrics(labelnames)
|
|
1456
|
+
|
|
1457
|
+
def _dynamic_metrics(self, labelnames):
|
|
1458
|
+
"""
|
|
1459
|
+
Dynamically get value by lambda function while capture
|
|
1460
|
+
"""
|
|
1461
|
+
self.local_cpu_hot_cache_count = self._gauge_cls(
|
|
1462
|
+
name="lmcache:local_cpu_hot_cache_count",
|
|
1463
|
+
documentation="The size of the hot_cache",
|
|
1464
|
+
labelnames=labelnames,
|
|
1465
|
+
multiprocess_mode="livemostrecent",
|
|
1466
|
+
).labels(**self.labels)
|
|
1467
|
+
self.local_cpu_keys_in_request_count = self._gauge_cls(
|
|
1468
|
+
name="lmcache:local_cpu_keys_in_request_count",
|
|
1469
|
+
documentation="The size of the keys_in_request",
|
|
1470
|
+
labelnames=labelnames,
|
|
1471
|
+
multiprocess_mode="livemostrecent",
|
|
1472
|
+
).labels(**self.labels)
|
|
1473
|
+
self.kv_msg_queue_size = self._gauge_cls(
|
|
1474
|
+
name="lmcache:kv_msg_queue_size",
|
|
1475
|
+
documentation="The size of the KV message queue in BatchedMessageSender",
|
|
1476
|
+
labelnames=labelnames,
|
|
1477
|
+
multiprocess_mode="livemostrecent",
|
|
1478
|
+
).labels(**self.labels)
|
|
1479
|
+
self.remote_put_task_num = self._gauge_cls(
|
|
1480
|
+
name="lmcache:remote_put_task_num",
|
|
1481
|
+
documentation="The number of remote put tasks",
|
|
1482
|
+
labelnames=labelnames,
|
|
1483
|
+
multiprocess_mode="livemostrecent",
|
|
1484
|
+
).labels(**self.labels)
|
|
1485
|
+
self.pin_monitor_pinned_objects_count = self._gauge_cls(
|
|
1486
|
+
name="lmcache:pin_monitor_pinned_objects_count",
|
|
1487
|
+
documentation="The number of pinned objects in PinMonitor",
|
|
1488
|
+
labelnames=labelnames,
|
|
1489
|
+
multiprocess_mode="livemostrecent",
|
|
1490
|
+
).labels(**self.labels)
|
|
1491
|
+
self.lmcache_is_healthy = self._gauge_cls(
|
|
1492
|
+
name="lmcache:lmcache_is_healthy",
|
|
1493
|
+
documentation="The health status of LMCache (1=healthy, 0=unhealthy)",
|
|
1494
|
+
labelnames=labelnames,
|
|
1495
|
+
multiprocess_mode="livemostrecent",
|
|
1496
|
+
).labels(**self.labels)
|
|
1497
|
+
self.get_blocking_failed_count = self._gauge_cls(
|
|
1498
|
+
name="lmcache:get_blocking_failed_count",
|
|
1499
|
+
documentation="The number of get blocking failed",
|
|
1500
|
+
labelnames=labelnames,
|
|
1501
|
+
multiprocess_mode="livemostrecent",
|
|
1502
|
+
).labels(**self.labels)
|
|
1503
|
+
self.put_failed_count = self._gauge_cls(
|
|
1504
|
+
name="lmcache:put_failed_count",
|
|
1505
|
+
documentation="The number of put failed",
|
|
1506
|
+
labelnames=labelnames,
|
|
1507
|
+
multiprocess_mode="livemostrecent",
|
|
1508
|
+
).labels(**self.labels)
|
|
1509
|
+
|
|
1510
|
+
event_statuses = ["ongoing", "done", "not_found"]
|
|
1511
|
+
for status in event_statuses:
|
|
1512
|
+
metric_name = f"storage_events_{status}_count"
|
|
1513
|
+
gauge = self._gauge_cls(
|
|
1514
|
+
name=f"lmcache:{metric_name}",
|
|
1515
|
+
documentation=f"The number of {status.replace('_', ' ')} events",
|
|
1516
|
+
labelnames=labelnames,
|
|
1517
|
+
multiprocess_mode="sum",
|
|
1518
|
+
).labels(**self.labels)
|
|
1519
|
+
setattr(self, metric_name, gauge)
|
|
1520
|
+
|
|
1521
|
+
# Chunk statistics metrics (dynamic)
|
|
1522
|
+
self.chunk_statistics_enabled = self._gauge_cls(
|
|
1523
|
+
name="lmcache:chunk_statistics_enabled",
|
|
1524
|
+
documentation="Whether chunk statistics collection is enabled",
|
|
1525
|
+
labelnames=labelnames,
|
|
1526
|
+
multiprocess_mode="livemostrecent",
|
|
1527
|
+
).labels(**self.labels)
|
|
1528
|
+
self.chunk_statistics_total_requests = self._gauge_cls(
|
|
1529
|
+
name="lmcache:chunk_statistics_total_requests",
|
|
1530
|
+
documentation="Total number of requests processed by chunk statistics",
|
|
1531
|
+
labelnames=labelnames,
|
|
1532
|
+
multiprocess_mode="livemostrecent",
|
|
1533
|
+
).labels(**self.labels)
|
|
1534
|
+
self.chunk_statistics_total_chunks = self._gauge_cls(
|
|
1535
|
+
name="lmcache:chunk_statistics_total_chunks",
|
|
1536
|
+
documentation="Total number of chunks processed",
|
|
1537
|
+
labelnames=labelnames,
|
|
1538
|
+
multiprocess_mode="livemostrecent",
|
|
1539
|
+
).labels(**self.labels)
|
|
1540
|
+
self.chunk_statistics_unique_chunks = self._gauge_cls(
|
|
1541
|
+
name="lmcache:chunk_statistics_unique_chunks",
|
|
1542
|
+
documentation="Number of unique chunks (estimated)",
|
|
1543
|
+
labelnames=labelnames,
|
|
1544
|
+
multiprocess_mode="livemostrecent",
|
|
1545
|
+
).labels(**self.labels)
|
|
1546
|
+
self.chunk_statistics_reuse_rate = self._gauge_cls(
|
|
1547
|
+
name="lmcache:chunk_statistics_reuse_rate",
|
|
1548
|
+
documentation="Chunk reuse rate (0.0 to 1.0)",
|
|
1549
|
+
labelnames=labelnames,
|
|
1550
|
+
multiprocess_mode="livemostrecent",
|
|
1551
|
+
).labels(**self.labels)
|
|
1552
|
+
self.chunk_statistics_bloom_filter_size_mb = self._gauge_cls(
|
|
1553
|
+
name="lmcache:chunk_statistics_bloom_filter_size_mb",
|
|
1554
|
+
documentation="Bloom Filter memory usage in MB",
|
|
1555
|
+
labelnames=labelnames,
|
|
1556
|
+
multiprocess_mode="livemostrecent",
|
|
1557
|
+
).labels(**self.labels)
|
|
1558
|
+
self.chunk_statistics_bloom_filter_fill_rate = self._gauge_cls(
|
|
1559
|
+
name="lmcache:chunk_statistics_bloom_filter_fill_rate",
|
|
1560
|
+
documentation="Bloom Filter fill rate (0.0 to 1.0)",
|
|
1561
|
+
labelnames=labelnames,
|
|
1562
|
+
multiprocess_mode="livemostrecent",
|
|
1563
|
+
).labels(**self.labels)
|
|
1564
|
+
self.chunk_statistics_file_count = self._gauge_cls(
|
|
1565
|
+
name="lmcache:chunk_statistics_file_count",
|
|
1566
|
+
documentation="Number of files created for file_hash strategy",
|
|
1567
|
+
labelnames=labelnames,
|
|
1568
|
+
multiprocess_mode="livemostrecent",
|
|
1569
|
+
).labels(**self.labels)
|
|
1570
|
+
self.chunk_statistics_current_file_size = self._gauge_cls(
|
|
1571
|
+
name="lmcache:chunk_statistics_current_file_size",
|
|
1572
|
+
documentation="Current file size in bytes for file_hash strategy",
|
|
1573
|
+
labelnames=labelnames,
|
|
1574
|
+
multiprocess_mode="livemostrecent",
|
|
1575
|
+
).labels(**self.labels)
|
|
1576
|
+
|
|
1577
|
+
# Connector metrics
|
|
1578
|
+
connector_metrics = [
|
|
1579
|
+
"scheduler_unfinished_requests_count",
|
|
1580
|
+
"connector_load_specs_count",
|
|
1581
|
+
"connector_request_trackers_count",
|
|
1582
|
+
"connector_kv_caches_count",
|
|
1583
|
+
"connector_layerwise_retrievers_count",
|
|
1584
|
+
"connector_invalid_block_ids_count",
|
|
1585
|
+
"connector_requests_priority_count",
|
|
1586
|
+
]
|
|
1587
|
+
|
|
1588
|
+
for metric_name in connector_metrics:
|
|
1589
|
+
gauge = self._gauge_cls(
|
|
1590
|
+
name=f"lmcache:{metric_name}",
|
|
1591
|
+
documentation=f"The count of {metric_name.replace('_', ' ')}",
|
|
1592
|
+
labelnames=labelnames,
|
|
1593
|
+
multiprocess_mode="livemostrecent",
|
|
1594
|
+
).labels(**self.labels)
|
|
1595
|
+
setattr(self, metric_name, gauge)
|
|
1596
|
+
|
|
1597
|
+
# PeriodicThread metrics
|
|
1598
|
+
self.periodic_threads_total_count = self._gauge_cls(
|
|
1599
|
+
name="lmcache:periodic_threads_total_count",
|
|
1600
|
+
documentation="Total number of registered periodic threads",
|
|
1601
|
+
labelnames=labelnames,
|
|
1602
|
+
multiprocess_mode="livemostrecent",
|
|
1603
|
+
).labels(**self.labels)
|
|
1604
|
+
self.periodic_threads_running_count = self._gauge_cls(
|
|
1605
|
+
name="lmcache:periodic_threads_running_count",
|
|
1606
|
+
documentation="Number of running periodic threads",
|
|
1607
|
+
labelnames=labelnames,
|
|
1608
|
+
multiprocess_mode="livemostrecent",
|
|
1609
|
+
).labels(**self.labels)
|
|
1610
|
+
self.periodic_threads_active_count = self._gauge_cls(
|
|
1611
|
+
name="lmcache:periodic_threads_active_count",
|
|
1612
|
+
documentation="Number of active periodic threads (recently executed)",
|
|
1613
|
+
labelnames=labelnames,
|
|
1614
|
+
multiprocess_mode="livemostrecent",
|
|
1615
|
+
).labels(**self.labels)
|
|
1616
|
+
|
|
1617
|
+
# Per-level metrics for periodic threads
|
|
1618
|
+
for level_name in ["critical", "high", "medium", "low"]:
|
|
1619
|
+
total_gauge = self._gauge_cls(
|
|
1620
|
+
name=f"lmcache:periodic_threads_{level_name}_total",
|
|
1621
|
+
documentation=f"Total number of {level_name} level periodic threads",
|
|
1622
|
+
labelnames=labelnames,
|
|
1623
|
+
multiprocess_mode="livemostrecent",
|
|
1624
|
+
).labels(**self.labels)
|
|
1625
|
+
setattr(self, f"periodic_threads_{level_name}_total", total_gauge)
|
|
1626
|
+
|
|
1627
|
+
running_gauge = self._gauge_cls(
|
|
1628
|
+
name=f"lmcache:periodic_threads_{level_name}_running",
|
|
1629
|
+
documentation=f"Number of running {level_name} level periodic threads",
|
|
1630
|
+
labelnames=labelnames,
|
|
1631
|
+
multiprocess_mode="livemostrecent",
|
|
1632
|
+
).labels(**self.labels)
|
|
1633
|
+
setattr(self, f"periodic_threads_{level_name}_running", running_gauge)
|
|
1634
|
+
|
|
1635
|
+
active_gauge = self._gauge_cls(
|
|
1636
|
+
name=f"lmcache:periodic_threads_{level_name}_active",
|
|
1637
|
+
documentation=f"Number of active {level_name} level periodic threads",
|
|
1638
|
+
labelnames=labelnames,
|
|
1639
|
+
multiprocess_mode="livemostrecent",
|
|
1640
|
+
).labels(**self.labels)
|
|
1641
|
+
setattr(self, f"periodic_threads_{level_name}_active", active_gauge)
|
|
1642
|
+
|
|
1643
|
+
def _log_gauge(self, gauge, data: Union[int, float]) -> None:
|
|
1644
|
+
# Convenience function for logging to gauge.
|
|
1645
|
+
gauge.labels(**self.labels).set(data)
|
|
1646
|
+
|
|
1647
|
+
def _log_counter(self, counter, data: Union[int, float]) -> None:
|
|
1648
|
+
# Convenience function for logging to counter.
|
|
1649
|
+
# Prevent ValueError from negative increment
|
|
1650
|
+
if data < 0:
|
|
1651
|
+
return
|
|
1652
|
+
counter.labels(**self.labels).inc(data)
|
|
1653
|
+
|
|
1654
|
+
def _log_histogram(self, histogram, data: Union[List[int], List[float]]) -> None:
|
|
1655
|
+
# Convenience function for logging to histogram.
|
|
1656
|
+
for value in data:
|
|
1657
|
+
histogram.labels(**self.labels).observe(value)
|
|
1658
|
+
|
|
1659
|
+
def log_prometheus(self, stats: LMCacheStats):
|
|
1660
|
+
self._log_counter(
|
|
1661
|
+
self.counter_num_retrieve_requests, stats.interval_retrieve_requests
|
|
1662
|
+
)
|
|
1663
|
+
self._log_counter(
|
|
1664
|
+
self.counter_num_store_requests, stats.interval_store_requests
|
|
1665
|
+
)
|
|
1666
|
+
self._log_counter(
|
|
1667
|
+
self.counter_num_lookup_requests, stats.interval_lookup_requests
|
|
1668
|
+
)
|
|
1669
|
+
|
|
1670
|
+
self._log_counter(
|
|
1671
|
+
self.counter_num_requested_tokens, stats.interval_requested_tokens
|
|
1672
|
+
)
|
|
1673
|
+
self._log_counter(self.counter_num_hit_tokens, stats.interval_hit_tokens)
|
|
1674
|
+
self._log_counter(self.counter_num_stored_tokens, stats.interval_stored_tokens)
|
|
1675
|
+
self._log_counter(self.counter_num_lookup_tokens, stats.interval_lookup_tokens)
|
|
1676
|
+
self._log_counter(self.counter_num_lookup_hits, stats.interval_lookup_hits)
|
|
1677
|
+
self._log_counter(self.counter_num_prompt_tokens, stats.interval_prompt_tokens)
|
|
1678
|
+
self._log_counter(
|
|
1679
|
+
self.counter_num_vllm_hit_tokens, stats.interval_vllm_hit_tokens
|
|
1680
|
+
)
|
|
1681
|
+
|
|
1682
|
+
self._log_counter(
|
|
1683
|
+
self.counter_num_remote_read_requests,
|
|
1684
|
+
stats.interval_remote_read_requests,
|
|
1685
|
+
)
|
|
1686
|
+
self._log_counter(
|
|
1687
|
+
self.counter_num_remote_read_bytes, stats.interval_remote_read_bytes
|
|
1688
|
+
)
|
|
1689
|
+
self._log_counter(
|
|
1690
|
+
self.counter_num_remote_write_requests,
|
|
1691
|
+
stats.interval_remote_write_requests,
|
|
1692
|
+
)
|
|
1693
|
+
self._log_counter(
|
|
1694
|
+
self.counter_num_remote_write_bytes,
|
|
1695
|
+
stats.interval_remote_write_bytes,
|
|
1696
|
+
)
|
|
1697
|
+
self._log_counter(
|
|
1698
|
+
self.counter_local_cpu_evict_count,
|
|
1699
|
+
stats.interval_local_cpu_evict_count,
|
|
1700
|
+
)
|
|
1701
|
+
self._log_counter(
|
|
1702
|
+
self.counter_local_cpu_evict_keys_count,
|
|
1703
|
+
stats.interval_local_cpu_evict_keys_count,
|
|
1704
|
+
)
|
|
1705
|
+
self._log_counter(
|
|
1706
|
+
self.counter_local_cpu_evict_failed_count,
|
|
1707
|
+
stats.interval_local_cpu_evict_failed_count,
|
|
1708
|
+
)
|
|
1709
|
+
self._log_counter(
|
|
1710
|
+
self.counter_forced_unpin_count,
|
|
1711
|
+
stats.interval_forced_unpin_count,
|
|
1712
|
+
)
|
|
1713
|
+
self._log_counter(
|
|
1714
|
+
self.counter_lookup_0_hit_requests,
|
|
1715
|
+
stats.interval_lookup_0_hit_requests,
|
|
1716
|
+
)
|
|
1717
|
+
self._log_counter(
|
|
1718
|
+
self.counter_num_slow_retrieval_by_time,
|
|
1719
|
+
stats.interval_num_slow_retrieval_by_time,
|
|
1720
|
+
)
|
|
1721
|
+
self._log_counter(
|
|
1722
|
+
self.counter_num_slow_retrieval_by_speed,
|
|
1723
|
+
stats.interval_num_slow_retrieval_by_speed,
|
|
1724
|
+
)
|
|
1725
|
+
|
|
1726
|
+
self._log_gauge(self.gauge_retrieve_hit_rate, stats.retrieve_hit_rate)
|
|
1727
|
+
|
|
1728
|
+
self._log_gauge(self.gauge_lookup_hit_rate, stats.lookup_hit_rate)
|
|
1729
|
+
|
|
1730
|
+
self._log_gauge(self.gauge_local_cache_usage, stats.local_cache_usage_bytes)
|
|
1731
|
+
|
|
1732
|
+
self._log_gauge(self.gauge_remote_cache_usage, stats.remote_cache_usage_bytes)
|
|
1733
|
+
|
|
1734
|
+
self._log_gauge(self.gauge_local_storage_usage, stats.local_storage_usage_bytes)
|
|
1735
|
+
|
|
1736
|
+
self._log_histogram(self.histogram_time_to_retrieve, stats.time_to_retrieve)
|
|
1737
|
+
|
|
1738
|
+
self._log_histogram(self.histogram_time_to_store, stats.time_to_store)
|
|
1739
|
+
|
|
1740
|
+
self._log_histogram(self.histogram_time_to_lookup, stats.time_to_lookup)
|
|
1741
|
+
|
|
1742
|
+
self._log_histogram(self.histogram_retrieve_speed, stats.retrieve_speed)
|
|
1743
|
+
|
|
1744
|
+
self._log_histogram(self.histogram_store_speed, stats.store_speed)
|
|
1745
|
+
|
|
1746
|
+
self._log_histogram(
|
|
1747
|
+
self.histogram_retrieve_process_tokens_time,
|
|
1748
|
+
stats.retrieve_process_tokens_time,
|
|
1749
|
+
)
|
|
1750
|
+
self._log_histogram(
|
|
1751
|
+
self.histogram_retrieve_broadcast_time, stats.retrieve_broadcast_time
|
|
1752
|
+
)
|
|
1753
|
+
self._log_histogram(
|
|
1754
|
+
self.histogram_retrieve_to_gpu_time, stats.retrieve_to_gpu_time
|
|
1755
|
+
)
|
|
1756
|
+
self._log_histogram(
|
|
1757
|
+
self.histogram_remote_backend_batched_get_blocking_time,
|
|
1758
|
+
stats.remote_backend_batched_get_blocking_time,
|
|
1759
|
+
)
|
|
1760
|
+
self._log_histogram(
|
|
1761
|
+
self.histogram_instrumented_connector_batched_get_time,
|
|
1762
|
+
stats.instrumented_connector_batched_get_time,
|
|
1763
|
+
)
|
|
1764
|
+
self._log_histogram(
|
|
1765
|
+
self.histogram_store_process_tokens_time, stats.store_process_tokens_time
|
|
1766
|
+
)
|
|
1767
|
+
self._log_histogram(
|
|
1768
|
+
self.histogram_store_from_gpu_time, stats.store_from_gpu_time
|
|
1769
|
+
)
|
|
1770
|
+
self._log_histogram(self.histogram_store_put_time, stats.store_put_time)
|
|
1771
|
+
|
|
1772
|
+
self._log_histogram(
|
|
1773
|
+
self.histogram_p2p_time_to_transfer, stats.p2p_time_to_transfer
|
|
1774
|
+
)
|
|
1775
|
+
|
|
1776
|
+
self._log_histogram(self.histogram_p2p_transfer_speed, stats.p2p_transfer_speed)
|
|
1777
|
+
|
|
1778
|
+
self._log_histogram(
|
|
1779
|
+
self.histogram_remote_time_to_get, stats.interval_remote_time_to_get
|
|
1780
|
+
)
|
|
1781
|
+
self._log_histogram(
|
|
1782
|
+
self.histogram_remote_time_to_put, stats.interval_remote_time_to_put
|
|
1783
|
+
)
|
|
1784
|
+
self._log_histogram(
|
|
1785
|
+
self.histogram_remote_time_to_get_sync,
|
|
1786
|
+
stats.interval_remote_time_to_get_sync,
|
|
1787
|
+
)
|
|
1788
|
+
self._log_histogram(
|
|
1789
|
+
self.histogram_request_cache_hit_rate, stats.interval_lookup_hit_rates
|
|
1790
|
+
)
|
|
1791
|
+
self._log_histogram(
|
|
1792
|
+
self.histogram_request_cache_lifespan, stats.interval_request_cache_lifespan
|
|
1793
|
+
)
|
|
1794
|
+
self._log_gauge(
|
|
1795
|
+
self.gauge_remote_ping_latency, stats.interval_remote_ping_latency
|
|
1796
|
+
)
|
|
1797
|
+
self._log_counter(
|
|
1798
|
+
self.counter_remote_ping_errors, stats.interval_remote_ping_errors
|
|
1799
|
+
)
|
|
1800
|
+
self._log_counter(
|
|
1801
|
+
self.counter_remote_ping_successes, stats.interval_remote_ping_success
|
|
1802
|
+
)
|
|
1803
|
+
self._log_gauge(
|
|
1804
|
+
self.gauge_remote_ping_error_code, stats.interval_remote_ping_error_code
|
|
1805
|
+
)
|
|
1806
|
+
self._log_gauge(
|
|
1807
|
+
self.gauge_active_memory_objs_count, stats.active_memory_objs_count
|
|
1808
|
+
)
|
|
1809
|
+
self._log_gauge(
|
|
1810
|
+
self.gauge_pinned_memory_objs_count, stats.pinned_memory_objs_count
|
|
1811
|
+
)
|
|
1812
|
+
|
|
1813
|
+
@staticmethod
|
|
1814
|
+
def _metadata_to_labels(metadata: LMCacheMetadata):
|
|
1815
|
+
labels = {
|
|
1816
|
+
"model_name": metadata.model_name,
|
|
1817
|
+
"worker_id": metadata.worker_id,
|
|
1818
|
+
"role": metadata.role,
|
|
1819
|
+
}
|
|
1820
|
+
if metadata.served_model_name:
|
|
1821
|
+
labels["served_model_name"] = metadata.served_model_name
|
|
1822
|
+
return labels
|
|
1823
|
+
|
|
1824
|
+
_instance = None
|
|
1825
|
+
|
|
1826
|
+
@staticmethod
|
|
1827
|
+
def GetOrCreate(
|
|
1828
|
+
metadata: LMCacheMetadata,
|
|
1829
|
+
config: Optional["LMCacheEngineConfig"] = None,
|
|
1830
|
+
) -> "PrometheusLogger":
|
|
1831
|
+
if PrometheusLogger._instance is None:
|
|
1832
|
+
PrometheusLogger._instance = PrometheusLogger(metadata, config=config)
|
|
1833
|
+
# assert PrometheusLogger._instance.metadata == metadata, \
|
|
1834
|
+
# "PrometheusLogger instance already created with different metadata"
|
|
1835
|
+
if PrometheusLogger._instance.metadata != metadata:
|
|
1836
|
+
logger.error(
|
|
1837
|
+
"PrometheusLogger instance already created with "
|
|
1838
|
+
"different metadata. This should not happen except "
|
|
1839
|
+
"in test"
|
|
1840
|
+
)
|
|
1841
|
+
return PrometheusLogger._instance
|
|
1842
|
+
|
|
1843
|
+
@staticmethod
|
|
1844
|
+
def GetInstance() -> "PrometheusLogger":
|
|
1845
|
+
assert PrometheusLogger._instance is not None, (
|
|
1846
|
+
"PrometheusLogger instance not created yet"
|
|
1847
|
+
)
|
|
1848
|
+
return PrometheusLogger._instance
|
|
1849
|
+
|
|
1850
|
+
@staticmethod
|
|
1851
|
+
def GetInstanceOrNone() -> Optional["PrometheusLogger"]:
|
|
1852
|
+
"""
|
|
1853
|
+
Returns the singleton instance of PrometheusLogger if it exists,
|
|
1854
|
+
otherwise returns None.
|
|
1855
|
+
"""
|
|
1856
|
+
return PrometheusLogger._instance
|
|
1857
|
+
|
|
1858
|
+
@thread_safe
|
|
1859
|
+
def reset_counters(self) -> None:
|
|
1860
|
+
"""
|
|
1861
|
+
Reset all Prometheus Counter metrics by calling clear().
|
|
1862
|
+
After clearing, re-initialize with labels so metrics remain visible.
|
|
1863
|
+
"""
|
|
1864
|
+
for counter in self._counters:
|
|
1865
|
+
counter.clear()
|
|
1866
|
+
# Re-initialize with labels to make metric visible again
|
|
1867
|
+
counter.labels(**self.labels)
|
|
1868
|
+
|
|
1869
|
+
@thread_safe
|
|
1870
|
+
def reset_histograms(self) -> None:
|
|
1871
|
+
"""
|
|
1872
|
+
Reset all Prometheus Histogram metrics by calling clear().
|
|
1873
|
+
After clearing, re-initialize with labels so metrics remain visible.
|
|
1874
|
+
"""
|
|
1875
|
+
for histogram in self._histograms:
|
|
1876
|
+
histogram.clear()
|
|
1877
|
+
# Re-initialize with labels to make metric visible again
|
|
1878
|
+
histogram.labels(**self.labels)
|
|
1879
|
+
|
|
1880
|
+
|
|
1881
|
+
def reset_observability_metrics() -> None:
|
|
1882
|
+
"""
|
|
1883
|
+
Reset observability metrics to their initial state.
|
|
1884
|
+
"""
|
|
1885
|
+
|
|
1886
|
+
prometheus_logger = PrometheusLogger.GetInstanceOrNone()
|
|
1887
|
+
if prometheus_logger is not None:
|
|
1888
|
+
prometheus_logger.reset_counters()
|
|
1889
|
+
prometheus_logger.reset_histograms()
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
class LMCacheStatsLogger:
|
|
1893
|
+
def __init__(
|
|
1894
|
+
self,
|
|
1895
|
+
metadata: LMCacheMetadata,
|
|
1896
|
+
log_interval: int,
|
|
1897
|
+
config: Optional["LMCacheEngineConfig"] = None,
|
|
1898
|
+
):
|
|
1899
|
+
self.metadata = metadata
|
|
1900
|
+
self.log_interval = log_interval
|
|
1901
|
+
self.monitor = LMCStatsMonitor.GetOrCreate()
|
|
1902
|
+
self.prometheus_logger = PrometheusLogger.GetOrCreate(metadata, config=config)
|
|
1903
|
+
self.lmc_usage_logger = ContinuousUsageContext.GetOrCreate(metadata)
|
|
1904
|
+
self.is_running = True
|
|
1905
|
+
# Event for interruptible sleep during shutdown
|
|
1906
|
+
self.shutdown_event = threading.Event()
|
|
1907
|
+
|
|
1908
|
+
self.thread = threading.Thread(
|
|
1909
|
+
target=self.log_worker, daemon=True, name="stats-logger-thread"
|
|
1910
|
+
)
|
|
1911
|
+
self.thread.start()
|
|
1912
|
+
|
|
1913
|
+
def log_worker(self):
|
|
1914
|
+
while self.is_running:
|
|
1915
|
+
stats = self.monitor.get_stats_and_clear()
|
|
1916
|
+
self.prometheus_logger.log_prometheus(stats)
|
|
1917
|
+
self.lmc_usage_logger.incr_or_send_stats(stats)
|
|
1918
|
+
# Use Event.wait() instead of time.sleep() for interruptible sleep
|
|
1919
|
+
# Returns True if event was set, False if timeout occurred
|
|
1920
|
+
self.shutdown_event.wait(self.log_interval)
|
|
1921
|
+
|
|
1922
|
+
def shutdown(self):
|
|
1923
|
+
"""Shutdown the stats logger gracefully with immediate wake-up"""
|
|
1924
|
+
logger.info("Shutting down LMCacheStatsLogger...")
|
|
1925
|
+
|
|
1926
|
+
# Signal the worker thread to stop
|
|
1927
|
+
self.is_running = False
|
|
1928
|
+
|
|
1929
|
+
# Signal the event to wake up the thread immediately from sleep
|
|
1930
|
+
self.shutdown_event.set()
|
|
1931
|
+
|
|
1932
|
+
# Wait for thread with a reasonable timeout
|
|
1933
|
+
if self.thread.is_alive():
|
|
1934
|
+
# Since we wake up the thread immediately, use a shorter timeout
|
|
1935
|
+
# Just enough time for the thread to finish its current iteration
|
|
1936
|
+
timeout = 5.0
|
|
1937
|
+
logger.info(
|
|
1938
|
+
f"Waiting for stats logger thread to finish (timeout: {timeout}s)..."
|
|
1939
|
+
)
|
|
1940
|
+
|
|
1941
|
+
try:
|
|
1942
|
+
self.thread.join(timeout=timeout)
|
|
1943
|
+
|
|
1944
|
+
if self.thread.is_alive():
|
|
1945
|
+
logger.warning(
|
|
1946
|
+
f"Stats logger thread did not terminate "
|
|
1947
|
+
f"within {timeout}s timeout. "
|
|
1948
|
+
"Thread may be blocked in logging operations. "
|
|
1949
|
+
"Proceeding with shutdown anyway."
|
|
1950
|
+
)
|
|
1951
|
+
else:
|
|
1952
|
+
logger.info("Stats logger thread terminated successfully")
|
|
1953
|
+
except Exception as e:
|
|
1954
|
+
logger.error(f"Error waiting for stats logger thread: {e}")
|
|
1955
|
+
else:
|
|
1956
|
+
logger.info("Stats logger thread already stopped")
|
|
1957
|
+
|
|
1958
|
+
logger.info("LMCacheStatsLogger shutdown complete")
|