lmcache-cli 0.4.5.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmcache/__init__.py +84 -0
- lmcache/_version.py +24 -0
- lmcache/cli/__init__.py +1 -0
- lmcache/cli/commands/__init__.py +34 -0
- lmcache/cli/commands/base.py +157 -0
- lmcache/cli/commands/bench/__init__.py +557 -0
- lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
- lmcache/cli/commands/bench/engine_bench/config.py +245 -0
- lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
- lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
- lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
- lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
- lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
- lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
- lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
- lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
- lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
- lmcache/cli/commands/describe.py +310 -0
- lmcache/cli/commands/kvcache.py +133 -0
- lmcache/cli/commands/mock.py +75 -0
- lmcache/cli/commands/ping.py +113 -0
- lmcache/cli/commands/query/__init__.py +155 -0
- lmcache/cli/commands/query/prompt.py +134 -0
- lmcache/cli/commands/query/request.py +357 -0
- lmcache/cli/commands/server.py +99 -0
- lmcache/cli/commands/tool/__init__.py +63 -0
- lmcache/cli/commands/tool/cache_simulator.py +113 -0
- lmcache/cli/commands/trace/__init__.py +505 -0
- lmcache/cli/commands/trace/dispatch.py +249 -0
- lmcache/cli/commands/trace/driver.py +372 -0
- lmcache/cli/commands/trace/stats.py +289 -0
- lmcache/cli/documents/lmcache.txt +11 -0
- lmcache/cli/main.py +42 -0
- lmcache/cli/metrics/__init__.py +29 -0
- lmcache/cli/metrics/formatter.py +171 -0
- lmcache/cli/metrics/handler.py +94 -0
- lmcache/cli/metrics/metrics.py +161 -0
- lmcache/cli/metrics/section.py +77 -0
- lmcache/connections.py +173 -0
- lmcache/integration/__init__.py +2 -0
- lmcache/integration/base_service_factory.py +165 -0
- lmcache/integration/request_telemetry/__init__.py +1 -0
- lmcache/integration/request_telemetry/base.py +51 -0
- lmcache/integration/request_telemetry/factory.py +113 -0
- lmcache/integration/request_telemetry/fastapi.py +109 -0
- lmcache/integration/request_telemetry/noop.py +35 -0
- lmcache/integration/sglang/__init__.py +2 -0
- lmcache/integration/sglang/sglang_adapter.py +326 -0
- lmcache/integration/sglang/utils.py +39 -0
- lmcache/integration/vllm/__init__.py +1 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
- lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
- lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
- lmcache/integration/vllm/utils.py +433 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
- lmcache/integration/vllm/vllm_service_factory.py +339 -0
- lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
- lmcache/logging.py +107 -0
- lmcache/native_storage_ops.pyi +230 -0
- lmcache/non_cuda_equivalents.py +1424 -0
- lmcache/observability.py +1958 -0
- lmcache/storage_backend/serde/__init__.py +1 -0
- lmcache/storage_backend/serde/cachegen_basics.py +210 -0
- lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
- lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
- lmcache/storage_backend/serde/serde.py +75 -0
- lmcache/tools/__init__.py +1 -0
- lmcache/tools/cache_simulator/README.md +392 -0
- lmcache/tools/cache_simulator/__init__.py +1 -0
- lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
- lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
- lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
- lmcache/tools/cache_simulator/lru_cache.py +124 -0
- lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
- lmcache/tools/cache_simulator/simulator.py +795 -0
- lmcache/tools/controller_benchmark/README.md +161 -0
- lmcache/tools/controller_benchmark/__init__.py +1 -0
- lmcache/tools/controller_benchmark/__main__.py +331 -0
- lmcache/tools/controller_benchmark/benchmark.py +660 -0
- lmcache/tools/controller_benchmark/config.py +44 -0
- lmcache/tools/controller_benchmark/constants.py +10 -0
- lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
- lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
- lmcache/tools/controller_benchmark/handlers/base.py +47 -0
- lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
- lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
- lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
- lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
- lmcache/tools/controller_benchmark/handlers/register.py +56 -0
- lmcache/tools/mp_status_viewer/__init__.py +1 -0
- lmcache/tools/mp_status_viewer/__main__.py +95 -0
- lmcache/usage_context.py +417 -0
- lmcache/utils.py +665 -0
- lmcache/v1/__init__.py +2 -0
- lmcache/v1/api_server/__init__.py +2 -0
- lmcache/v1/api_server/__main__.py +537 -0
- lmcache/v1/basic_check.py +112 -0
- lmcache/v1/cache_controller/__init__.py +9 -0
- lmcache/v1/cache_controller/commands/__init__.py +15 -0
- lmcache/v1/cache_controller/commands/base.py +35 -0
- lmcache/v1/cache_controller/commands/full_sync.py +49 -0
- lmcache/v1/cache_controller/config.py +176 -0
- lmcache/v1/cache_controller/controller_manager.py +535 -0
- lmcache/v1/cache_controller/controllers/__init__.py +11 -0
- lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
- lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
- lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
- lmcache/v1/cache_controller/executor.py +463 -0
- lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
- lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
- lmcache/v1/cache_controller/frontend/static/index.html +234 -0
- lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
- lmcache/v1/cache_controller/full_sync_sender.py +475 -0
- lmcache/v1/cache_controller/locks.py +149 -0
- lmcache/v1/cache_controller/message.py +828 -0
- lmcache/v1/cache_controller/observability.py +208 -0
- lmcache/v1/cache_controller/utils.py +679 -0
- lmcache/v1/cache_controller/worker.py +665 -0
- lmcache/v1/cache_engine.py +2058 -0
- lmcache/v1/cache_interface.py +19 -0
- lmcache/v1/check/__init__.py +74 -0
- lmcache/v1/check/check_mode_gen.py +86 -0
- lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
- lmcache/v1/check/check_mode_test_remote.py +155 -0
- lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
- lmcache/v1/check/utils.py +571 -0
- lmcache/v1/compute/__init__.py +2 -0
- lmcache/v1/compute/attention/__init__.py +0 -0
- lmcache/v1/compute/attention/abstract.py +39 -0
- lmcache/v1/compute/attention/flash_attn.py +129 -0
- lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
- lmcache/v1/compute/attention/metadata.py +85 -0
- lmcache/v1/compute/attention/utils.py +14 -0
- lmcache/v1/compute/blend/__init__.py +7 -0
- lmcache/v1/compute/blend/blender.py +168 -0
- lmcache/v1/compute/blend/metadata.py +34 -0
- lmcache/v1/compute/blend/utils.py +63 -0
- lmcache/v1/compute/models/__init__.py +0 -0
- lmcache/v1/compute/models/base.py +141 -0
- lmcache/v1/compute/models/llama.py +9 -0
- lmcache/v1/compute/models/qwen3.py +24 -0
- lmcache/v1/compute/models/utils.py +68 -0
- lmcache/v1/compute/positional_encoding.py +199 -0
- lmcache/v1/config.py +848 -0
- lmcache/v1/config_base.py +848 -0
- lmcache/v1/distributed/api.py +248 -0
- lmcache/v1/distributed/config.py +321 -0
- lmcache/v1/distributed/error.py +64 -0
- lmcache/v1/distributed/eviction.py +192 -0
- lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
- lmcache/v1/distributed/eviction_policy/factory.py +27 -0
- lmcache/v1/distributed/eviction_policy/lru.py +244 -0
- lmcache/v1/distributed/eviction_policy/noop.py +50 -0
- lmcache/v1/distributed/internal_api.py +170 -0
- lmcache/v1/distributed/l1_manager.py +835 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
- lmcache/v1/distributed/l2_adapters/base.py +360 -0
- lmcache/v1/distributed/l2_adapters/config.py +385 -0
- lmcache/v1/distributed/l2_adapters/factory.py +205 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
- lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
- lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
- lmcache/v1/distributed/memory_manager.py +179 -0
- lmcache/v1/distributed/storage_controller.py +39 -0
- lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
- lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
- lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
- lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
- lmcache/v1/distributed/storage_manager.py +532 -0
- lmcache/v1/event_manager.py +145 -0
- lmcache/v1/exceptions/__init__.py +16 -0
- lmcache/v1/gpu_connector/__init__.py +126 -0
- lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
- lmcache/v1/gpu_connector/gpu_ops.py +85 -0
- lmcache/v1/gpu_connector/hpu_connector.py +326 -0
- lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
- lmcache/v1/gpu_connector/utils.py +890 -0
- lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
- lmcache/v1/health_monitor/__init__.py +1 -0
- lmcache/v1/health_monitor/base.py +587 -0
- lmcache/v1/health_monitor/checks/__init__.py +1 -0
- lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
- lmcache/v1/health_monitor/constants.py +36 -0
- lmcache/v1/internal_api_server/__init__.py +0 -0
- lmcache/v1/internal_api_server/api_registry.py +59 -0
- lmcache/v1/internal_api_server/api_server.py +120 -0
- lmcache/v1/internal_api_server/common/__init__.py +1 -0
- lmcache/v1/internal_api_server/common/env_api.py +22 -0
- lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
- lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
- lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
- lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
- lmcache/v1/internal_api_server/common/thread_api.py +63 -0
- lmcache/v1/internal_api_server/controller/__init__.py +1 -0
- lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
- lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
- lmcache/v1/internal_api_server/utils.py +43 -0
- lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
- lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
- lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
- lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
- lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
- lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
- lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
- lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
- lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
- lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
- lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
- lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
- lmcache/v1/kv_layer_groups.py +267 -0
- lmcache/v1/lazy_memory_allocator.py +284 -0
- lmcache/v1/lookup_client/__init__.py +25 -0
- lmcache/v1/lookup_client/abstract_client.py +77 -0
- lmcache/v1/lookup_client/async_lookup_message.py +50 -0
- lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
- lmcache/v1/lookup_client/factory.py +251 -0
- lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
- lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
- lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
- lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
- lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
- lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
- lmcache/v1/lookup_client/record_strategies/base.py +327 -0
- lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
- lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
- lmcache/v1/manager.py +539 -0
- lmcache/v1/memory_management.py +2619 -0
- lmcache/v1/metadata.py +114 -0
- lmcache/v1/mp_observability/AGENTS.override.md +21 -0
- lmcache/v1/mp_observability/README.md +204 -0
- lmcache/v1/mp_observability/config.py +340 -0
- lmcache/v1/mp_observability/event.py +100 -0
- lmcache/v1/mp_observability/event_bus.py +313 -0
- lmcache/v1/mp_observability/otel_init.py +145 -0
- lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
- lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
- lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
- lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
- lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
- lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
- lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
- lmcache/v1/mp_observability/trace/__init__.py +50 -0
- lmcache/v1/mp_observability/trace/codecs.py +255 -0
- lmcache/v1/mp_observability/trace/decorator.py +147 -0
- lmcache/v1/mp_observability/trace/format.py +132 -0
- lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
- lmcache/v1/mp_observability/trace/reader.py +167 -0
- lmcache/v1/mp_observability/trace/recorder.py +300 -0
- lmcache/v1/multiprocess/__init__.py +0 -0
- lmcache/v1/multiprocess/affinity_pool.py +102 -0
- lmcache/v1/multiprocess/blend_server_v2.py +891 -0
- lmcache/v1/multiprocess/config.py +253 -0
- lmcache/v1/multiprocess/custom_types.py +281 -0
- lmcache/v1/multiprocess/futures.py +194 -0
- lmcache/v1/multiprocess/gpu_context.py +511 -0
- lmcache/v1/multiprocess/http_server.py +235 -0
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
- lmcache/v1/multiprocess/mq.py +732 -0
- lmcache/v1/multiprocess/protocol.py +86 -0
- lmcache/v1/multiprocess/protocols/README.md +213 -0
- lmcache/v1/multiprocess/protocols/__init__.py +127 -0
- lmcache/v1/multiprocess/protocols/base.py +89 -0
- lmcache/v1/multiprocess/protocols/blend.py +109 -0
- lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
- lmcache/v1/multiprocess/protocols/controller.py +53 -0
- lmcache/v1/multiprocess/protocols/debug.py +34 -0
- lmcache/v1/multiprocess/protocols/engine.py +146 -0
- lmcache/v1/multiprocess/protocols/observability.py +39 -0
- lmcache/v1/multiprocess/server.py +1134 -0
- lmcache/v1/multiprocess/session.py +190 -0
- lmcache/v1/multiprocess/token_hasher.py +441 -0
- lmcache/v1/offload_server/__init__.py +17 -0
- lmcache/v1/offload_server/abstract_server.py +37 -0
- lmcache/v1/offload_server/message.py +30 -0
- lmcache/v1/offload_server/zmq_server.py +122 -0
- lmcache/v1/periodic_thread.py +579 -0
- lmcache/v1/pin_monitor.py +246 -0
- lmcache/v1/plugin/__init__.py +0 -0
- lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
- lmcache/v1/protocol.py +317 -0
- lmcache/v1/rpc/__init__.py +17 -0
- lmcache/v1/rpc/transport.py +105 -0
- lmcache/v1/rpc/zmq_transport.py +213 -0
- lmcache/v1/rpc_utils.py +165 -0
- lmcache/v1/server/__init__.py +2 -0
- lmcache/v1/server/__main__.py +170 -0
- lmcache/v1/server/storage_backend/__init__.py +21 -0
- lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
- lmcache/v1/server/storage_backend/local_backend.py +75 -0
- lmcache/v1/server/utils.py +21 -0
- lmcache/v1/standalone/__init__.py +1 -0
- lmcache/v1/standalone/__main__.py +583 -0
- lmcache/v1/standalone/manager.py +80 -0
- lmcache/v1/standalone/standalone_service_factory.py +86 -0
- lmcache/v1/storage_backend/__init__.py +313 -0
- lmcache/v1/storage_backend/abstract_backend.py +445 -0
- lmcache/v1/storage_backend/audit_backend.py +233 -0
- lmcache/v1/storage_backend/batched_message_sender.py +222 -0
- lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
- lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
- lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
- lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
- lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
- lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
- lmcache/v1/storage_backend/connector/__init__.py +443 -0
- lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
- lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
- lmcache/v1/storage_backend/connector/base_connector.py +379 -0
- lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
- lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
- lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
- lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
- lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
- lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
- lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
- lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
- lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
- lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
- lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
- lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
- lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
- lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
- lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
- lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
- lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
- lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
- lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
- lmcache/v1/storage_backend/gds_backend.py +1199 -0
- lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
- lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
- lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
- lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
- lmcache/v1/storage_backend/local_disk_backend.py +656 -0
- lmcache/v1/storage_backend/maru_backend.py +734 -0
- lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
- lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
- lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
- lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
- lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
- lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
- lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
- lmcache/v1/storage_backend/p2p_backend.py +788 -0
- lmcache/v1/storage_backend/path_sharder.py +117 -0
- lmcache/v1/storage_backend/pd_backend.py +646 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
- lmcache/v1/storage_backend/remote_backend.py +624 -0
- lmcache/v1/storage_backend/resp_client.py +227 -0
- lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
- lmcache/v1/storage_backend/storage_manager.py +1352 -0
- lmcache/v1/system_detection.py +110 -0
- lmcache/v1/token_database.py +551 -0
- lmcache/v1/transfer_channel/__init__.py +83 -0
- lmcache/v1/transfer_channel/abstract.py +285 -0
- lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
- lmcache/v1/transfer_channel/nixl_channel.py +639 -0
- lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
- lmcache/v1/transfer_channel/transfer_utils.py +63 -0
- lmcache/v1/utils/__init__.py +1 -0
- lmcache/v1/utils/bloom_filter.py +109 -0
- lmcache/v1/utils/cache_utils.py +125 -0
- lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
- lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
- lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
- lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
- lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
- lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
"""
|
|
3
|
+
Unified PeriodicThread abstraction for LMCache background threads.
|
|
4
|
+
|
|
5
|
+
This module provides a standardized way to create and manage periodic
|
|
6
|
+
background threads with proper naming, monitoring, and lifecycle management.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# Standard
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
from enum import Enum
|
|
13
|
+
from typing import TYPE_CHECKING, Callable, Dict, List, Optional
|
|
14
|
+
import threading
|
|
15
|
+
import time
|
|
16
|
+
|
|
17
|
+
# First Party
|
|
18
|
+
from lmcache.logging import init_logger
|
|
19
|
+
from lmcache.v1.exceptions import IrrecoverableException
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
# First Party
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
logger = init_logger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class ThreadLevel(Enum):
|
|
29
|
+
"""
|
|
30
|
+
Thread importance level.
|
|
31
|
+
|
|
32
|
+
CRITICAL: Thread failure causes severe system degradation or data loss.
|
|
33
|
+
Examples: health-monitor-thread, PinMonitor-thread
|
|
34
|
+
|
|
35
|
+
HIGH: Thread failure significantly impacts performance or functionality.
|
|
36
|
+
Examples: storage-manager-event-loop, lookup-server threads
|
|
37
|
+
|
|
38
|
+
MEDIUM: Thread failure causes noticeable degradation but system remains functional.
|
|
39
|
+
Examples: stats-logger-thread, batched-message-sender-thread
|
|
40
|
+
|
|
41
|
+
LOW: Thread failure has minimal impact on system operation.
|
|
42
|
+
Examples: lazy-memory-expand-thread
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
CRITICAL = "critical"
|
|
46
|
+
HIGH = "high"
|
|
47
|
+
MEDIUM = "medium"
|
|
48
|
+
LOW = "low"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class ThreadRunSummary:
|
|
53
|
+
"""Summary of a single thread execution cycle."""
|
|
54
|
+
|
|
55
|
+
timestamp: float = 0.0
|
|
56
|
+
duration_ms: float = 0.0
|
|
57
|
+
success: bool = True
|
|
58
|
+
message: str = ""
|
|
59
|
+
extra_info: Dict[str, str] = field(default_factory=dict)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class PeriodicThread(ABC):
|
|
63
|
+
"""
|
|
64
|
+
Abstract base class for periodic background threads.
|
|
65
|
+
|
|
66
|
+
This class provides a standardized framework for creating periodic
|
|
67
|
+
background threads with proper naming, monitoring, and lifecycle management.
|
|
68
|
+
|
|
69
|
+
Attributes:
|
|
70
|
+
name: Human-readable name of the thread
|
|
71
|
+
interval: Time interval between executions in seconds
|
|
72
|
+
level: Importance level of the thread
|
|
73
|
+
init_wait: Initial wait time before first execution in seconds
|
|
74
|
+
|
|
75
|
+
Features:
|
|
76
|
+
- Named threads for easy identification
|
|
77
|
+
- Configurable execution interval
|
|
78
|
+
- Initial wait time before first execution
|
|
79
|
+
- Automatic tracking of last run time and summary
|
|
80
|
+
- Thread level classification for monitoring
|
|
81
|
+
- Interruptible sleep for graceful shutdown
|
|
82
|
+
|
|
83
|
+
Usage:
|
|
84
|
+
class MyPeriodicThread(PeriodicThread):
|
|
85
|
+
def __init__(self):
|
|
86
|
+
super().__init__(
|
|
87
|
+
name="my-periodic-thread",
|
|
88
|
+
interval=30.0,
|
|
89
|
+
level=ThreadLevel.MEDIUM,
|
|
90
|
+
init_wait=5.0,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def _execute(self) -> ThreadRunSummary:
|
|
94
|
+
# Perform periodic work
|
|
95
|
+
return ThreadRunSummary(
|
|
96
|
+
timestamp=time.time(),
|
|
97
|
+
success=True,
|
|
98
|
+
message="Completed successfully"
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
thread = MyPeriodicThread()
|
|
102
|
+
thread.start()
|
|
103
|
+
# ... later ...
|
|
104
|
+
thread.stop()
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
name: str,
|
|
110
|
+
interval: float,
|
|
111
|
+
level: ThreadLevel = ThreadLevel.MEDIUM,
|
|
112
|
+
init_wait: float = 0.0,
|
|
113
|
+
):
|
|
114
|
+
"""
|
|
115
|
+
Initialize a PeriodicThread.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
name: Thread name for identification
|
|
119
|
+
interval: Execution interval in seconds
|
|
120
|
+
level: Thread importance level
|
|
121
|
+
init_wait: Initial wait time before first execution in seconds
|
|
122
|
+
"""
|
|
123
|
+
self._name = name
|
|
124
|
+
self._interval = interval
|
|
125
|
+
self._level = level
|
|
126
|
+
self._init_wait = init_wait
|
|
127
|
+
|
|
128
|
+
# Thread state
|
|
129
|
+
self._thread: Optional[threading.Thread] = None
|
|
130
|
+
self._stop_event = threading.Event()
|
|
131
|
+
self._running = False
|
|
132
|
+
|
|
133
|
+
# Execution tracking
|
|
134
|
+
self._last_run_time: float = 0.0
|
|
135
|
+
self._last_summary: Optional[ThreadRunSummary] = None
|
|
136
|
+
self._total_runs: int = 0
|
|
137
|
+
self._failed_runs: int = 0
|
|
138
|
+
self._lock = threading.RLock()
|
|
139
|
+
|
|
140
|
+
# Start time (set when thread starts)
|
|
141
|
+
self._start_time: float = 0.0
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def name(self) -> str:
|
|
145
|
+
"""Get the thread name."""
|
|
146
|
+
return self._name
|
|
147
|
+
|
|
148
|
+
@property
|
|
149
|
+
def interval(self) -> float:
|
|
150
|
+
"""Get the execution interval in seconds."""
|
|
151
|
+
return self._interval
|
|
152
|
+
|
|
153
|
+
@property
|
|
154
|
+
def level(self) -> ThreadLevel:
|
|
155
|
+
"""Get the thread importance level."""
|
|
156
|
+
return self._level
|
|
157
|
+
|
|
158
|
+
@property
|
|
159
|
+
def init_wait(self) -> float:
|
|
160
|
+
"""Get the initial wait time in seconds."""
|
|
161
|
+
return self._init_wait
|
|
162
|
+
|
|
163
|
+
@property
|
|
164
|
+
def last_run_time(self) -> float:
|
|
165
|
+
"""Get the timestamp of the last execution."""
|
|
166
|
+
with self._lock:
|
|
167
|
+
return self._last_run_time
|
|
168
|
+
|
|
169
|
+
@property
|
|
170
|
+
def last_summary(self) -> Optional[ThreadRunSummary]:
|
|
171
|
+
"""Get the summary of the last execution."""
|
|
172
|
+
with self._lock:
|
|
173
|
+
return self._last_summary
|
|
174
|
+
|
|
175
|
+
@property
|
|
176
|
+
def total_runs(self) -> int:
|
|
177
|
+
"""Get the total number of executions."""
|
|
178
|
+
with self._lock:
|
|
179
|
+
return self._total_runs
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def failed_runs(self) -> int:
|
|
183
|
+
"""Get the number of failed executions."""
|
|
184
|
+
with self._lock:
|
|
185
|
+
return self._failed_runs
|
|
186
|
+
|
|
187
|
+
@property
|
|
188
|
+
def is_running(self) -> bool:
|
|
189
|
+
"""Check if the thread is currently running."""
|
|
190
|
+
return self._running and self._thread is not None and self._thread.is_alive()
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def is_active(self) -> bool:
|
|
194
|
+
"""
|
|
195
|
+
Check if the thread is active (running and recently executed).
|
|
196
|
+
|
|
197
|
+
A thread is considered active if:
|
|
198
|
+
1. It is running
|
|
199
|
+
2. The time since last run is less than 3 * interval
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
bool: True if active, False otherwise
|
|
203
|
+
"""
|
|
204
|
+
if not self.is_running:
|
|
205
|
+
return False
|
|
206
|
+
|
|
207
|
+
with self._lock:
|
|
208
|
+
if self._last_run_time == 0:
|
|
209
|
+
# Thread started but hasn't run yet
|
|
210
|
+
# Consider active if within init_wait + 3 * interval from start
|
|
211
|
+
time_since_start = time.time() - self._start_time
|
|
212
|
+
return time_since_start < self._init_wait + 3 * self._interval
|
|
213
|
+
|
|
214
|
+
time_since_last_run = time.time() - self._last_run_time
|
|
215
|
+
return time_since_last_run < 3 * self._interval
|
|
216
|
+
|
|
217
|
+
def start(self) -> Optional[threading.Thread]:
|
|
218
|
+
"""
|
|
219
|
+
Start the periodic thread.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Optional[threading.Thread]: The started thread, or None if already running
|
|
223
|
+
"""
|
|
224
|
+
if self._running:
|
|
225
|
+
logger.warning("PeriodicThread %s is already running", self._name)
|
|
226
|
+
return None
|
|
227
|
+
|
|
228
|
+
self._stop_event.clear()
|
|
229
|
+
self._running = True
|
|
230
|
+
self._start_time = time.time()
|
|
231
|
+
|
|
232
|
+
self._thread = threading.Thread(
|
|
233
|
+
target=self._run_loop,
|
|
234
|
+
daemon=True,
|
|
235
|
+
name=self._name,
|
|
236
|
+
)
|
|
237
|
+
self._thread.start()
|
|
238
|
+
|
|
239
|
+
logger.info(
|
|
240
|
+
"Started PeriodicThread: %s (level=%s, interval=%.1fs, init_wait=%.1fs)",
|
|
241
|
+
self._name,
|
|
242
|
+
self._level.value,
|
|
243
|
+
self._interval,
|
|
244
|
+
self._init_wait,
|
|
245
|
+
)
|
|
246
|
+
return self._thread
|
|
247
|
+
|
|
248
|
+
def stop(self, timeout: float = 5.0) -> None:
|
|
249
|
+
"""
|
|
250
|
+
Stop the periodic thread.
|
|
251
|
+
|
|
252
|
+
Args:
|
|
253
|
+
timeout: Maximum time to wait for thread termination in seconds
|
|
254
|
+
"""
|
|
255
|
+
if not self._running:
|
|
256
|
+
return
|
|
257
|
+
|
|
258
|
+
logger.info("Stopping PeriodicThread: %s", self._name)
|
|
259
|
+
self._running = False
|
|
260
|
+
self._stop_event.set()
|
|
261
|
+
|
|
262
|
+
if self._thread is not None and self._thread.is_alive():
|
|
263
|
+
self._thread.join(timeout=timeout)
|
|
264
|
+
if self._thread.is_alive():
|
|
265
|
+
logger.warning(
|
|
266
|
+
"PeriodicThread %s did not terminate within %.1fs timeout",
|
|
267
|
+
self._name,
|
|
268
|
+
timeout,
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
def _run_loop(self) -> None:
|
|
272
|
+
"""Main thread loop."""
|
|
273
|
+
# Initial wait
|
|
274
|
+
if self._init_wait > 0:
|
|
275
|
+
if self._stop_event.wait(timeout=self._init_wait):
|
|
276
|
+
logger.info("PeriodicThread %s stopped during init_wait", self._name)
|
|
277
|
+
return
|
|
278
|
+
|
|
279
|
+
logger.info(
|
|
280
|
+
"PeriodicThread %s entering main loop (interval=%.1fs)",
|
|
281
|
+
self._name,
|
|
282
|
+
self._interval,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
while not self._stop_event.is_set():
|
|
286
|
+
start_time = time.time()
|
|
287
|
+
|
|
288
|
+
try:
|
|
289
|
+
summary = self._execute()
|
|
290
|
+
summary.timestamp = start_time
|
|
291
|
+
summary.duration_ms = (time.time() - start_time) * 1000
|
|
292
|
+
|
|
293
|
+
with self._lock:
|
|
294
|
+
self._last_run_time = start_time
|
|
295
|
+
self._last_summary = summary
|
|
296
|
+
self._total_runs += 1
|
|
297
|
+
if not summary.success:
|
|
298
|
+
self._failed_runs += 1
|
|
299
|
+
|
|
300
|
+
except IrrecoverableException as e:
|
|
301
|
+
logger.error(
|
|
302
|
+
"IrrecoverableException in PeriodicThread %s: %s",
|
|
303
|
+
self._name,
|
|
304
|
+
e,
|
|
305
|
+
exc_info=True,
|
|
306
|
+
)
|
|
307
|
+
summary = ThreadRunSummary(
|
|
308
|
+
timestamp=start_time,
|
|
309
|
+
duration_ms=(time.time() - start_time) * 1000,
|
|
310
|
+
success=False,
|
|
311
|
+
message=str(e),
|
|
312
|
+
)
|
|
313
|
+
with self._lock:
|
|
314
|
+
self._last_run_time = start_time
|
|
315
|
+
self._last_summary = summary
|
|
316
|
+
self._total_runs += 1
|
|
317
|
+
self._failed_runs += 1
|
|
318
|
+
# Stop the loop on irrecoverable exceptions
|
|
319
|
+
logger.info(
|
|
320
|
+
"PeriodicThread %s stopping due to IrrecoverableException",
|
|
321
|
+
self._name,
|
|
322
|
+
)
|
|
323
|
+
break
|
|
324
|
+
except Exception as e:
|
|
325
|
+
logger.error(
|
|
326
|
+
"Error in PeriodicThread %s: %s", self._name, e, exc_info=True
|
|
327
|
+
)
|
|
328
|
+
summary = ThreadRunSummary(
|
|
329
|
+
timestamp=start_time,
|
|
330
|
+
duration_ms=(time.time() - start_time) * 1000,
|
|
331
|
+
success=False,
|
|
332
|
+
message=str(e),
|
|
333
|
+
)
|
|
334
|
+
with self._lock:
|
|
335
|
+
self._last_run_time = start_time
|
|
336
|
+
self._last_summary = summary
|
|
337
|
+
self._total_runs += 1
|
|
338
|
+
self._failed_runs += 1
|
|
339
|
+
|
|
340
|
+
# Wait for next interval
|
|
341
|
+
if self._stop_event.wait(timeout=self._interval):
|
|
342
|
+
break
|
|
343
|
+
|
|
344
|
+
logger.info("PeriodicThread %s loop stopped", self._name)
|
|
345
|
+
|
|
346
|
+
@abstractmethod
|
|
347
|
+
def _execute(self) -> ThreadRunSummary:
|
|
348
|
+
"""
|
|
349
|
+
Execute one cycle of the periodic task.
|
|
350
|
+
|
|
351
|
+
This method should be overridden by subclasses to implement
|
|
352
|
+
the actual periodic work.
|
|
353
|
+
|
|
354
|
+
Returns:
|
|
355
|
+
ThreadRunSummary: Summary of this execution cycle
|
|
356
|
+
"""
|
|
357
|
+
pass
|
|
358
|
+
|
|
359
|
+
def get_status(self) -> Dict:
|
|
360
|
+
"""
|
|
361
|
+
Get the current status of the thread.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
Dict: Thread status information
|
|
365
|
+
"""
|
|
366
|
+
with self._lock:
|
|
367
|
+
last_summary_dict = None
|
|
368
|
+
if self._last_summary:
|
|
369
|
+
last_summary_dict = {
|
|
370
|
+
"timestamp": self._last_summary.timestamp,
|
|
371
|
+
"duration_ms": self._last_summary.duration_ms,
|
|
372
|
+
"success": self._last_summary.success,
|
|
373
|
+
"message": self._last_summary.message,
|
|
374
|
+
"extra_info": self._last_summary.extra_info,
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return {
|
|
378
|
+
"name": self._name,
|
|
379
|
+
"level": self._level.value,
|
|
380
|
+
"interval": self._interval,
|
|
381
|
+
"init_wait": self._init_wait,
|
|
382
|
+
"is_running": self.is_running,
|
|
383
|
+
"is_active": self.is_active,
|
|
384
|
+
"last_run_time": self._last_run_time,
|
|
385
|
+
"last_run_ago": time.time() - self._last_run_time
|
|
386
|
+
if self._last_run_time > 0
|
|
387
|
+
else None,
|
|
388
|
+
"total_runs": self._total_runs,
|
|
389
|
+
"failed_runs": self._failed_runs,
|
|
390
|
+
"success_rate": (
|
|
391
|
+
(self._total_runs - self._failed_runs) / self._total_runs * 100
|
|
392
|
+
if self._total_runs > 0
|
|
393
|
+
else None
|
|
394
|
+
),
|
|
395
|
+
"last_summary": last_summary_dict,
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
class PeriodicThreadRegistry:
|
|
400
|
+
"""
|
|
401
|
+
Global registry for all PeriodicThread instances.
|
|
402
|
+
|
|
403
|
+
This class provides a centralized way to track and manage all
|
|
404
|
+
periodic threads in the system.
|
|
405
|
+
"""
|
|
406
|
+
|
|
407
|
+
_instance: Optional["PeriodicThreadRegistry"] = None
|
|
408
|
+
_lock = threading.Lock()
|
|
409
|
+
|
|
410
|
+
def __init__(self):
|
|
411
|
+
self._threads: Dict[str, PeriodicThread] = {}
|
|
412
|
+
self._registry_lock = threading.RLock()
|
|
413
|
+
|
|
414
|
+
@classmethod
|
|
415
|
+
def get_instance(cls) -> "PeriodicThreadRegistry":
|
|
416
|
+
"""Get or create the singleton instance."""
|
|
417
|
+
if cls._instance is None:
|
|
418
|
+
with cls._lock:
|
|
419
|
+
if cls._instance is None:
|
|
420
|
+
cls._instance = PeriodicThreadRegistry()
|
|
421
|
+
return cls._instance
|
|
422
|
+
|
|
423
|
+
@classmethod
|
|
424
|
+
def reset(cls) -> None:
|
|
425
|
+
"""Reset the singleton instance. Mainly for testing."""
|
|
426
|
+
with cls._lock:
|
|
427
|
+
if cls._instance is not None:
|
|
428
|
+
cls._instance.unregister_all()
|
|
429
|
+
cls._instance = None
|
|
430
|
+
|
|
431
|
+
def register(self, thread: PeriodicThread) -> None:
|
|
432
|
+
"""
|
|
433
|
+
Register a periodic thread.
|
|
434
|
+
|
|
435
|
+
Args:
|
|
436
|
+
thread: The PeriodicThread to register
|
|
437
|
+
"""
|
|
438
|
+
with self._registry_lock:
|
|
439
|
+
if thread.name in self._threads:
|
|
440
|
+
logger.warning(
|
|
441
|
+
"PeriodicThread %s is already registered, replacing",
|
|
442
|
+
thread.name,
|
|
443
|
+
)
|
|
444
|
+
self._threads[thread.name] = thread
|
|
445
|
+
logger.debug("Registered PeriodicThread: %s", thread.name)
|
|
446
|
+
|
|
447
|
+
def unregister(self, name: str) -> Optional[PeriodicThread]:
|
|
448
|
+
"""
|
|
449
|
+
Unregister a periodic thread by name.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
name: The name of the thread to unregister
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
The unregistered thread, or None if not found
|
|
456
|
+
"""
|
|
457
|
+
with self._registry_lock:
|
|
458
|
+
thread = self._threads.pop(name, None)
|
|
459
|
+
if thread:
|
|
460
|
+
logger.debug("Unregistered PeriodicThread: %s", name)
|
|
461
|
+
return thread
|
|
462
|
+
|
|
463
|
+
def unregister_all(self) -> None:
|
|
464
|
+
"""Unregister all periodic threads."""
|
|
465
|
+
with self._registry_lock:
|
|
466
|
+
self._threads.clear()
|
|
467
|
+
logger.debug("Unregistered all PeriodicThreads")
|
|
468
|
+
|
|
469
|
+
def get(self, name: str) -> Optional[PeriodicThread]:
|
|
470
|
+
"""Get a registered thread by name."""
|
|
471
|
+
with self._registry_lock:
|
|
472
|
+
return self._threads.get(name)
|
|
473
|
+
|
|
474
|
+
def get_all(self) -> List[PeriodicThread]:
|
|
475
|
+
"""Get all registered threads."""
|
|
476
|
+
with self._registry_lock:
|
|
477
|
+
return list(self._threads.values())
|
|
478
|
+
|
|
479
|
+
def get_by_level(self, level: ThreadLevel) -> List[PeriodicThread]:
|
|
480
|
+
"""Get all threads with the specified level."""
|
|
481
|
+
with self._registry_lock:
|
|
482
|
+
return [t for t in self._threads.values() if t.level == level]
|
|
483
|
+
|
|
484
|
+
def get_running_count(self) -> int:
|
|
485
|
+
"""Get the count of running threads."""
|
|
486
|
+
with self._registry_lock:
|
|
487
|
+
return sum(1 for t in self._threads.values() if t.is_running)
|
|
488
|
+
|
|
489
|
+
def get_active_count(self) -> int:
|
|
490
|
+
"""Get the count of active threads."""
|
|
491
|
+
with self._registry_lock:
|
|
492
|
+
return sum(1 for t in self._threads.values() if t.is_active)
|
|
493
|
+
|
|
494
|
+
def get_count_by_level(self, level: ThreadLevel) -> Dict[str, int]:
|
|
495
|
+
"""
|
|
496
|
+
Get counts of threads for a specific level.
|
|
497
|
+
|
|
498
|
+
Returns:
|
|
499
|
+
Dict with keys: total, running, active
|
|
500
|
+
"""
|
|
501
|
+
with self._registry_lock:
|
|
502
|
+
threads = [t for t in self._threads.values() if t.level == level]
|
|
503
|
+
return {
|
|
504
|
+
"total": len(threads),
|
|
505
|
+
"running": sum(1 for t in threads if t.is_running),
|
|
506
|
+
"active": sum(1 for t in threads if t.is_active),
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
def get_summary(self) -> Dict:
|
|
510
|
+
"""
|
|
511
|
+
Get a summary of all registered threads.
|
|
512
|
+
|
|
513
|
+
Returns:
|
|
514
|
+
Dict containing:
|
|
515
|
+
- total_count: Total number of registered threads
|
|
516
|
+
- running_count: Number of running threads
|
|
517
|
+
- active_count: Number of active threads
|
|
518
|
+
- by_level: Counts by thread level
|
|
519
|
+
- threads: List of thread statuses
|
|
520
|
+
"""
|
|
521
|
+
with self._registry_lock:
|
|
522
|
+
by_level = {}
|
|
523
|
+
for level in ThreadLevel:
|
|
524
|
+
by_level[level.value] = self.get_count_by_level(level)
|
|
525
|
+
|
|
526
|
+
return {
|
|
527
|
+
"total_count": len(self._threads),
|
|
528
|
+
"running_count": self.get_running_count(),
|
|
529
|
+
"active_count": self.get_active_count(),
|
|
530
|
+
"by_level": by_level,
|
|
531
|
+
"threads": [t.get_status() for t in self._threads.values()],
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
|
|
535
|
+
def create_periodic_thread(
|
|
536
|
+
name: str,
|
|
537
|
+
interval: float,
|
|
538
|
+
execute_fn: Callable[[], ThreadRunSummary],
|
|
539
|
+
level: ThreadLevel = ThreadLevel.MEDIUM,
|
|
540
|
+
init_wait: float = 0.0,
|
|
541
|
+
auto_register: bool = True,
|
|
542
|
+
) -> PeriodicThread:
|
|
543
|
+
"""
|
|
544
|
+
Factory function to create a simple PeriodicThread.
|
|
545
|
+
|
|
546
|
+
This is a convenience function for creating periodic threads
|
|
547
|
+
without needing to define a subclass.
|
|
548
|
+
|
|
549
|
+
Args:
|
|
550
|
+
name: Thread name
|
|
551
|
+
interval: Execution interval in seconds
|
|
552
|
+
execute_fn: Function to execute on each cycle
|
|
553
|
+
level: Thread importance level
|
|
554
|
+
init_wait: Initial wait time before first execution
|
|
555
|
+
auto_register: Whether to automatically register with the global registry
|
|
556
|
+
|
|
557
|
+
Returns:
|
|
558
|
+
PeriodicThread: The created thread instance
|
|
559
|
+
"""
|
|
560
|
+
|
|
561
|
+
class SimplePeriodicThread(PeriodicThread):
|
|
562
|
+
def __init__(self):
|
|
563
|
+
super().__init__(
|
|
564
|
+
name=name,
|
|
565
|
+
interval=interval,
|
|
566
|
+
level=level,
|
|
567
|
+
init_wait=init_wait,
|
|
568
|
+
)
|
|
569
|
+
self._execute_fn = execute_fn
|
|
570
|
+
|
|
571
|
+
def _execute(self) -> ThreadRunSummary:
|
|
572
|
+
return self._execute_fn()
|
|
573
|
+
|
|
574
|
+
thread = SimplePeriodicThread()
|
|
575
|
+
|
|
576
|
+
if auto_register:
|
|
577
|
+
PeriodicThreadRegistry.get_instance().register(thread)
|
|
578
|
+
|
|
579
|
+
return thread
|