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.
Files changed (399) hide show
  1. lmcache/__init__.py +84 -0
  2. lmcache/_version.py +24 -0
  3. lmcache/cli/__init__.py +1 -0
  4. lmcache/cli/commands/__init__.py +34 -0
  5. lmcache/cli/commands/base.py +157 -0
  6. lmcache/cli/commands/bench/__init__.py +557 -0
  7. lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
  8. lmcache/cli/commands/bench/engine_bench/config.py +245 -0
  9. lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
  10. lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
  11. lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
  12. lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
  13. lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
  14. lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
  15. lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
  16. lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
  17. lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
  18. lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
  19. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
  20. lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
  21. lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
  22. lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
  23. lmcache/cli/commands/describe.py +310 -0
  24. lmcache/cli/commands/kvcache.py +133 -0
  25. lmcache/cli/commands/mock.py +75 -0
  26. lmcache/cli/commands/ping.py +113 -0
  27. lmcache/cli/commands/query/__init__.py +155 -0
  28. lmcache/cli/commands/query/prompt.py +134 -0
  29. lmcache/cli/commands/query/request.py +357 -0
  30. lmcache/cli/commands/server.py +99 -0
  31. lmcache/cli/commands/tool/__init__.py +63 -0
  32. lmcache/cli/commands/tool/cache_simulator.py +113 -0
  33. lmcache/cli/commands/trace/__init__.py +505 -0
  34. lmcache/cli/commands/trace/dispatch.py +249 -0
  35. lmcache/cli/commands/trace/driver.py +372 -0
  36. lmcache/cli/commands/trace/stats.py +289 -0
  37. lmcache/cli/documents/lmcache.txt +11 -0
  38. lmcache/cli/main.py +42 -0
  39. lmcache/cli/metrics/__init__.py +29 -0
  40. lmcache/cli/metrics/formatter.py +171 -0
  41. lmcache/cli/metrics/handler.py +94 -0
  42. lmcache/cli/metrics/metrics.py +161 -0
  43. lmcache/cli/metrics/section.py +77 -0
  44. lmcache/connections.py +173 -0
  45. lmcache/integration/__init__.py +2 -0
  46. lmcache/integration/base_service_factory.py +165 -0
  47. lmcache/integration/request_telemetry/__init__.py +1 -0
  48. lmcache/integration/request_telemetry/base.py +51 -0
  49. lmcache/integration/request_telemetry/factory.py +113 -0
  50. lmcache/integration/request_telemetry/fastapi.py +109 -0
  51. lmcache/integration/request_telemetry/noop.py +35 -0
  52. lmcache/integration/sglang/__init__.py +2 -0
  53. lmcache/integration/sglang/sglang_adapter.py +326 -0
  54. lmcache/integration/sglang/utils.py +39 -0
  55. lmcache/integration/vllm/__init__.py +1 -0
  56. lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
  57. lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
  58. lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
  59. lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
  60. lmcache/integration/vllm/utils.py +433 -0
  61. lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
  62. lmcache/integration/vllm/vllm_service_factory.py +339 -0
  63. lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
  64. lmcache/logging.py +107 -0
  65. lmcache/native_storage_ops.pyi +230 -0
  66. lmcache/non_cuda_equivalents.py +1424 -0
  67. lmcache/observability.py +1958 -0
  68. lmcache/storage_backend/serde/__init__.py +1 -0
  69. lmcache/storage_backend/serde/cachegen_basics.py +210 -0
  70. lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
  71. lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
  72. lmcache/storage_backend/serde/serde.py +75 -0
  73. lmcache/tools/__init__.py +1 -0
  74. lmcache/tools/cache_simulator/README.md +392 -0
  75. lmcache/tools/cache_simulator/__init__.py +1 -0
  76. lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
  77. lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
  78. lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
  79. lmcache/tools/cache_simulator/lru_cache.py +124 -0
  80. lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
  81. lmcache/tools/cache_simulator/simulator.py +795 -0
  82. lmcache/tools/controller_benchmark/README.md +161 -0
  83. lmcache/tools/controller_benchmark/__init__.py +1 -0
  84. lmcache/tools/controller_benchmark/__main__.py +331 -0
  85. lmcache/tools/controller_benchmark/benchmark.py +660 -0
  86. lmcache/tools/controller_benchmark/config.py +44 -0
  87. lmcache/tools/controller_benchmark/constants.py +10 -0
  88. lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
  89. lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
  90. lmcache/tools/controller_benchmark/handlers/base.py +47 -0
  91. lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
  92. lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
  93. lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
  94. lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
  95. lmcache/tools/controller_benchmark/handlers/register.py +56 -0
  96. lmcache/tools/mp_status_viewer/__init__.py +1 -0
  97. lmcache/tools/mp_status_viewer/__main__.py +95 -0
  98. lmcache/usage_context.py +417 -0
  99. lmcache/utils.py +665 -0
  100. lmcache/v1/__init__.py +2 -0
  101. lmcache/v1/api_server/__init__.py +2 -0
  102. lmcache/v1/api_server/__main__.py +537 -0
  103. lmcache/v1/basic_check.py +112 -0
  104. lmcache/v1/cache_controller/__init__.py +9 -0
  105. lmcache/v1/cache_controller/commands/__init__.py +15 -0
  106. lmcache/v1/cache_controller/commands/base.py +35 -0
  107. lmcache/v1/cache_controller/commands/full_sync.py +49 -0
  108. lmcache/v1/cache_controller/config.py +176 -0
  109. lmcache/v1/cache_controller/controller_manager.py +535 -0
  110. lmcache/v1/cache_controller/controllers/__init__.py +11 -0
  111. lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
  112. lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
  113. lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
  114. lmcache/v1/cache_controller/executor.py +463 -0
  115. lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
  116. lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
  117. lmcache/v1/cache_controller/frontend/static/index.html +234 -0
  118. lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
  119. lmcache/v1/cache_controller/full_sync_sender.py +475 -0
  120. lmcache/v1/cache_controller/locks.py +149 -0
  121. lmcache/v1/cache_controller/message.py +828 -0
  122. lmcache/v1/cache_controller/observability.py +208 -0
  123. lmcache/v1/cache_controller/utils.py +679 -0
  124. lmcache/v1/cache_controller/worker.py +665 -0
  125. lmcache/v1/cache_engine.py +2058 -0
  126. lmcache/v1/cache_interface.py +19 -0
  127. lmcache/v1/check/__init__.py +74 -0
  128. lmcache/v1/check/check_mode_gen.py +86 -0
  129. lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
  130. lmcache/v1/check/check_mode_test_remote.py +155 -0
  131. lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
  132. lmcache/v1/check/utils.py +571 -0
  133. lmcache/v1/compute/__init__.py +2 -0
  134. lmcache/v1/compute/attention/__init__.py +0 -0
  135. lmcache/v1/compute/attention/abstract.py +39 -0
  136. lmcache/v1/compute/attention/flash_attn.py +129 -0
  137. lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
  138. lmcache/v1/compute/attention/metadata.py +85 -0
  139. lmcache/v1/compute/attention/utils.py +14 -0
  140. lmcache/v1/compute/blend/__init__.py +7 -0
  141. lmcache/v1/compute/blend/blender.py +168 -0
  142. lmcache/v1/compute/blend/metadata.py +34 -0
  143. lmcache/v1/compute/blend/utils.py +63 -0
  144. lmcache/v1/compute/models/__init__.py +0 -0
  145. lmcache/v1/compute/models/base.py +141 -0
  146. lmcache/v1/compute/models/llama.py +9 -0
  147. lmcache/v1/compute/models/qwen3.py +24 -0
  148. lmcache/v1/compute/models/utils.py +68 -0
  149. lmcache/v1/compute/positional_encoding.py +199 -0
  150. lmcache/v1/config.py +848 -0
  151. lmcache/v1/config_base.py +848 -0
  152. lmcache/v1/distributed/api.py +248 -0
  153. lmcache/v1/distributed/config.py +321 -0
  154. lmcache/v1/distributed/error.py +64 -0
  155. lmcache/v1/distributed/eviction.py +192 -0
  156. lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
  157. lmcache/v1/distributed/eviction_policy/factory.py +27 -0
  158. lmcache/v1/distributed/eviction_policy/lru.py +244 -0
  159. lmcache/v1/distributed/eviction_policy/noop.py +50 -0
  160. lmcache/v1/distributed/internal_api.py +170 -0
  161. lmcache/v1/distributed/l1_manager.py +835 -0
  162. lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
  163. lmcache/v1/distributed/l2_adapters/base.py +360 -0
  164. lmcache/v1/distributed/l2_adapters/config.py +385 -0
  165. lmcache/v1/distributed/l2_adapters/factory.py +205 -0
  166. lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
  167. lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
  168. lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
  169. lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
  170. lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
  171. lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
  172. lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
  173. lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
  174. lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
  175. lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
  176. lmcache/v1/distributed/memory_manager.py +179 -0
  177. lmcache/v1/distributed/storage_controller.py +39 -0
  178. lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
  179. lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
  180. lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
  181. lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
  182. lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
  183. lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
  184. lmcache/v1/distributed/storage_manager.py +532 -0
  185. lmcache/v1/event_manager.py +145 -0
  186. lmcache/v1/exceptions/__init__.py +16 -0
  187. lmcache/v1/gpu_connector/__init__.py +126 -0
  188. lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
  189. lmcache/v1/gpu_connector/gpu_ops.py +85 -0
  190. lmcache/v1/gpu_connector/hpu_connector.py +326 -0
  191. lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
  192. lmcache/v1/gpu_connector/utils.py +890 -0
  193. lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
  194. lmcache/v1/health_monitor/__init__.py +1 -0
  195. lmcache/v1/health_monitor/base.py +587 -0
  196. lmcache/v1/health_monitor/checks/__init__.py +1 -0
  197. lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
  198. lmcache/v1/health_monitor/constants.py +36 -0
  199. lmcache/v1/internal_api_server/__init__.py +0 -0
  200. lmcache/v1/internal_api_server/api_registry.py +59 -0
  201. lmcache/v1/internal_api_server/api_server.py +120 -0
  202. lmcache/v1/internal_api_server/common/__init__.py +1 -0
  203. lmcache/v1/internal_api_server/common/env_api.py +22 -0
  204. lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
  205. lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
  206. lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
  207. lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
  208. lmcache/v1/internal_api_server/common/thread_api.py +63 -0
  209. lmcache/v1/internal_api_server/controller/__init__.py +1 -0
  210. lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
  211. lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
  212. lmcache/v1/internal_api_server/utils.py +43 -0
  213. lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
  214. lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
  215. lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
  216. lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
  217. lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
  218. lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
  219. lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
  220. lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
  221. lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
  222. lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
  223. lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
  224. lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
  225. lmcache/v1/kv_layer_groups.py +267 -0
  226. lmcache/v1/lazy_memory_allocator.py +284 -0
  227. lmcache/v1/lookup_client/__init__.py +25 -0
  228. lmcache/v1/lookup_client/abstract_client.py +77 -0
  229. lmcache/v1/lookup_client/async_lookup_message.py +50 -0
  230. lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
  231. lmcache/v1/lookup_client/factory.py +251 -0
  232. lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
  233. lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
  234. lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
  235. lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
  236. lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
  237. lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
  238. lmcache/v1/lookup_client/record_strategies/base.py +327 -0
  239. lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
  240. lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
  241. lmcache/v1/manager.py +539 -0
  242. lmcache/v1/memory_management.py +2619 -0
  243. lmcache/v1/metadata.py +114 -0
  244. lmcache/v1/mp_observability/AGENTS.override.md +21 -0
  245. lmcache/v1/mp_observability/README.md +204 -0
  246. lmcache/v1/mp_observability/config.py +340 -0
  247. lmcache/v1/mp_observability/event.py +100 -0
  248. lmcache/v1/mp_observability/event_bus.py +313 -0
  249. lmcache/v1/mp_observability/otel_init.py +145 -0
  250. lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
  251. lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
  252. lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
  253. lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
  254. lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
  255. lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
  256. lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
  257. lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
  258. lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
  259. lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
  260. lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
  261. lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
  262. lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
  263. lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
  264. lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
  265. lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
  266. lmcache/v1/mp_observability/trace/__init__.py +50 -0
  267. lmcache/v1/mp_observability/trace/codecs.py +255 -0
  268. lmcache/v1/mp_observability/trace/decorator.py +147 -0
  269. lmcache/v1/mp_observability/trace/format.py +132 -0
  270. lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
  271. lmcache/v1/mp_observability/trace/reader.py +167 -0
  272. lmcache/v1/mp_observability/trace/recorder.py +300 -0
  273. lmcache/v1/multiprocess/__init__.py +0 -0
  274. lmcache/v1/multiprocess/affinity_pool.py +102 -0
  275. lmcache/v1/multiprocess/blend_server_v2.py +891 -0
  276. lmcache/v1/multiprocess/config.py +253 -0
  277. lmcache/v1/multiprocess/custom_types.py +281 -0
  278. lmcache/v1/multiprocess/futures.py +194 -0
  279. lmcache/v1/multiprocess/gpu_context.py +511 -0
  280. lmcache/v1/multiprocess/http_server.py +235 -0
  281. lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
  282. lmcache/v1/multiprocess/mq.py +732 -0
  283. lmcache/v1/multiprocess/protocol.py +86 -0
  284. lmcache/v1/multiprocess/protocols/README.md +213 -0
  285. lmcache/v1/multiprocess/protocols/__init__.py +127 -0
  286. lmcache/v1/multiprocess/protocols/base.py +89 -0
  287. lmcache/v1/multiprocess/protocols/blend.py +109 -0
  288. lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
  289. lmcache/v1/multiprocess/protocols/controller.py +53 -0
  290. lmcache/v1/multiprocess/protocols/debug.py +34 -0
  291. lmcache/v1/multiprocess/protocols/engine.py +146 -0
  292. lmcache/v1/multiprocess/protocols/observability.py +39 -0
  293. lmcache/v1/multiprocess/server.py +1134 -0
  294. lmcache/v1/multiprocess/session.py +190 -0
  295. lmcache/v1/multiprocess/token_hasher.py +441 -0
  296. lmcache/v1/offload_server/__init__.py +17 -0
  297. lmcache/v1/offload_server/abstract_server.py +37 -0
  298. lmcache/v1/offload_server/message.py +30 -0
  299. lmcache/v1/offload_server/zmq_server.py +122 -0
  300. lmcache/v1/periodic_thread.py +579 -0
  301. lmcache/v1/pin_monitor.py +246 -0
  302. lmcache/v1/plugin/__init__.py +0 -0
  303. lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
  304. lmcache/v1/protocol.py +317 -0
  305. lmcache/v1/rpc/__init__.py +17 -0
  306. lmcache/v1/rpc/transport.py +105 -0
  307. lmcache/v1/rpc/zmq_transport.py +213 -0
  308. lmcache/v1/rpc_utils.py +165 -0
  309. lmcache/v1/server/__init__.py +2 -0
  310. lmcache/v1/server/__main__.py +170 -0
  311. lmcache/v1/server/storage_backend/__init__.py +21 -0
  312. lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
  313. lmcache/v1/server/storage_backend/local_backend.py +75 -0
  314. lmcache/v1/server/utils.py +21 -0
  315. lmcache/v1/standalone/__init__.py +1 -0
  316. lmcache/v1/standalone/__main__.py +583 -0
  317. lmcache/v1/standalone/manager.py +80 -0
  318. lmcache/v1/standalone/standalone_service_factory.py +86 -0
  319. lmcache/v1/storage_backend/__init__.py +313 -0
  320. lmcache/v1/storage_backend/abstract_backend.py +445 -0
  321. lmcache/v1/storage_backend/audit_backend.py +233 -0
  322. lmcache/v1/storage_backend/batched_message_sender.py +222 -0
  323. lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
  324. lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
  325. lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
  326. lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
  327. lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
  328. lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
  329. lmcache/v1/storage_backend/connector/__init__.py +443 -0
  330. lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
  331. lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
  332. lmcache/v1/storage_backend/connector/base_connector.py +379 -0
  333. lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
  334. lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
  335. lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
  336. lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
  337. lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
  338. lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
  339. lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
  340. lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
  341. lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
  342. lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
  343. lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
  344. lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
  345. lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
  346. lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
  347. lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
  348. lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
  349. lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
  350. lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
  351. lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
  352. lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
  353. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
  354. lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
  355. lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
  356. lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
  357. lmcache/v1/storage_backend/gds_backend.py +1199 -0
  358. lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
  359. lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
  360. lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
  361. lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
  362. lmcache/v1/storage_backend/local_disk_backend.py +656 -0
  363. lmcache/v1/storage_backend/maru_backend.py +734 -0
  364. lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
  365. lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
  366. lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
  367. lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
  368. lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
  369. lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
  370. lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
  371. lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
  372. lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
  373. lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
  374. lmcache/v1/storage_backend/p2p_backend.py +788 -0
  375. lmcache/v1/storage_backend/path_sharder.py +117 -0
  376. lmcache/v1/storage_backend/pd_backend.py +646 -0
  377. lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
  378. lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
  379. lmcache/v1/storage_backend/remote_backend.py +624 -0
  380. lmcache/v1/storage_backend/resp_client.py +227 -0
  381. lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
  382. lmcache/v1/storage_backend/storage_manager.py +1352 -0
  383. lmcache/v1/system_detection.py +110 -0
  384. lmcache/v1/token_database.py +551 -0
  385. lmcache/v1/transfer_channel/__init__.py +83 -0
  386. lmcache/v1/transfer_channel/abstract.py +285 -0
  387. lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
  388. lmcache/v1/transfer_channel/nixl_channel.py +639 -0
  389. lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
  390. lmcache/v1/transfer_channel/transfer_utils.py +63 -0
  391. lmcache/v1/utils/__init__.py +1 -0
  392. lmcache/v1/utils/bloom_filter.py +109 -0
  393. lmcache/v1/utils/cache_utils.py +125 -0
  394. lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
  395. lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
  396. lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
  397. lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
  398. lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
  399. lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,532 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ Distributed multi-tier storage manager for MP mode
4
+ """
5
+
6
+ # Standard
7
+ from contextlib import contextmanager
8
+ from typing import Iterator, Literal
9
+ import time
10
+
11
+ # First Party
12
+ from lmcache.logging import init_logger
13
+ from lmcache.v1.distributed.api import (
14
+ MemoryLayoutDesc,
15
+ ObjectKey,
16
+ PrefetchHandle,
17
+ )
18
+ from lmcache.v1.distributed.config import StorageManagerConfig
19
+ from lmcache.v1.distributed.error import L1Error, strerror
20
+ from lmcache.v1.distributed.l1_manager import L1Manager
21
+ from lmcache.v1.distributed.l2_adapters import create_l2_adapter
22
+ from lmcache.v1.distributed.l2_adapters.base import L2AdapterInterface
23
+ from lmcache.v1.distributed.storage_controllers import (
24
+ L1EvictionController,
25
+ L2AdapterEvictionState,
26
+ L2EvictionController,
27
+ PrefetchController,
28
+ StoreController,
29
+ )
30
+ from lmcache.v1.distributed.storage_controllers.prefetch_policy import (
31
+ create_prefetch_policy,
32
+ )
33
+ from lmcache.v1.distributed.storage_controllers.store_policy import (
34
+ AdapterDescriptor,
35
+ create_store_policy,
36
+ )
37
+ from lmcache.v1.memory_management import MemoryObj
38
+ from lmcache.v1.mp_observability.event import Event, EventType
39
+ from lmcache.v1.mp_observability.event_bus import get_event_bus
40
+ from lmcache.v1.mp_observability.trace.decorator import (
41
+ enable_tracing,
42
+ is_tracing_enabled,
43
+ publish_call_event,
44
+ )
45
+
46
+ logger = init_logger(__name__)
47
+
48
+
49
+ class StorageManager:
50
+ def __init__(self, config: StorageManagerConfig):
51
+ self._l1_manager = L1Manager(config.l1_manager_config)
52
+ self._event_bus = get_event_bus()
53
+
54
+ # L1 eviction controller
55
+ self._eviction_controller = L1EvictionController(
56
+ l1_manager=self._l1_manager,
57
+ eviction_config=config.eviction_config,
58
+ )
59
+ self._eviction_controller.start()
60
+
61
+ # L2 adapters and store controller
62
+ l1_memory_desc = self._l1_manager.get_l1_memory_desc()
63
+ self._l2_adapters: list[L2AdapterInterface] = [
64
+ create_l2_adapter(ac, l1_memory_desc)
65
+ for ac in config.l2_adapter_config.adapters
66
+ ]
67
+
68
+ # Unified L2 eviction controller for all adapters with eviction config
69
+ l2_eviction_states = [
70
+ L2AdapterEvictionState(
71
+ adapter=adapter,
72
+ eviction_config=ac.eviction_config,
73
+ )
74
+ for adapter, ac in zip(
75
+ self._l2_adapters, config.l2_adapter_config.adapters, strict=False
76
+ )
77
+ if ac.eviction_config is not None
78
+ ]
79
+ self._l2_eviction_controller = L2EvictionController(l2_eviction_states)
80
+ self._l2_eviction_controller.start()
81
+
82
+ adapter_descriptors = [
83
+ AdapterDescriptor(index=i, config=ac)
84
+ for i, ac in enumerate(config.l2_adapter_config.adapters)
85
+ ]
86
+
87
+ self._store_controller = StoreController(
88
+ l1_manager=self._l1_manager,
89
+ l2_adapters=self._l2_adapters,
90
+ adapter_descriptors=adapter_descriptors,
91
+ policy=create_store_policy(config.store_policy),
92
+ )
93
+ self._store_controller.start()
94
+
95
+ # Prefetch controller
96
+ self._prefetch_controller = PrefetchController(
97
+ l1_manager=self._l1_manager,
98
+ l2_adapters=self._l2_adapters,
99
+ adapter_descriptors=adapter_descriptors,
100
+ policy=create_prefetch_policy(config.prefetch_policy),
101
+ max_in_flight=config.prefetch_max_in_flight,
102
+ )
103
+ self._prefetch_controller.start()
104
+
105
+ # External APIs for serving engine integration code to call
106
+ @enable_tracing()
107
+ def reserve_write(
108
+ self,
109
+ keys: list[ObjectKey],
110
+ layout_desc: MemoryLayoutDesc,
111
+ mode: Literal["new", "update", "all"],
112
+ ) -> dict[ObjectKey, MemoryObj]:
113
+ """
114
+ Reserve the object for writing into the storage manager.
115
+
116
+ Args:
117
+ keys (list[ObjectKey]): List of object keys to reserve for writing.
118
+ layout_desc (MemoryLayoutDesc): Description of the memory layout
119
+ for the objects to be reserved.
120
+ mode (Literal["new", "update", "all"]): Reservation mode.
121
+ - "new": Reserve only new objects that do not exist.
122
+ - "update": Reserve only existing objects for update.
123
+ - "all": Reserve all writable objects regardless of existence.
124
+
125
+ Returns:
126
+ dict[ObjectKey, MemoryObj]: A dictionary mapping object keys to their
127
+ reserved memory objects. Note that not all requested keys could be
128
+ reserved (e.g., out of memory or write conflict)
129
+ """
130
+ reserve_result = self._l1_manager.reserve_write(
131
+ keys=keys,
132
+ is_temporary=[False] * len(keys),
133
+ layout_desc=layout_desc,
134
+ mode=mode,
135
+ )
136
+
137
+ result = {k: m for k, (e, m) in reserve_result.items() if m is not None}
138
+ successful_keys = list(result.keys())
139
+ failed_keys = [k for k, (e, m) in reserve_result.items() if m is None]
140
+ self._event_bus.publish(
141
+ Event(
142
+ event_type=EventType.SM_WRITE_RESERVED,
143
+ metadata={
144
+ "succeeded_keys": successful_keys,
145
+ "failed_keys": failed_keys,
146
+ },
147
+ )
148
+ )
149
+ return result
150
+
151
+ @enable_tracing()
152
+ def finish_write(
153
+ self,
154
+ keys: list[ObjectKey],
155
+ ) -> None:
156
+ """
157
+ Finish writing the objects into the storage manager.
158
+
159
+ Args:
160
+ keys (list[ObjectKey]): List of object keys that have been written.
161
+ """
162
+ finish_result = self._l1_manager.finish_write(keys)
163
+ successful_keys = [k for k, e in finish_result.items() if e == L1Error.SUCCESS]
164
+ failed_keys = [k for k, e in finish_result.items() if e != L1Error.SUCCESS]
165
+ self._event_bus.publish(
166
+ Event(
167
+ event_type=EventType.SM_WRITE_FINISHED,
168
+ metadata={
169
+ "succeeded_keys": successful_keys,
170
+ "failed_keys": failed_keys,
171
+ },
172
+ )
173
+ )
174
+
175
+ # TODO: global key states update
176
+
177
+ @contextmanager
178
+ def read_prefetched_results(
179
+ self,
180
+ keys: list[ObjectKey],
181
+ ) -> Iterator[list[MemoryObj] | None]:
182
+ """
183
+ Read the memory objects from L1 storage that has been prefetched beforehand.
184
+ Yielding an optional list of memory objects corresponding to the requested
185
+ keys. If any the object is not found in L1, None is yielded.
186
+
187
+ Args:
188
+ keys (list[ObjectKey]): List of object keys to reserve for reading.
189
+
190
+ Returns:
191
+ Iterator[list[MemoryObj] | None]: An iterator yielding an optional list of
192
+ memory objects corresponding to the requested keys.
193
+
194
+ Note:
195
+ If any object is not found in L1 storage, None is yielded. In this case,
196
+ this function will release release the read lock of all successfully read
197
+ memory objects when exiting the context.
198
+
199
+ If the caller raised exception during the processing of the yielded memory
200
+ objects, this function will ensure that the read locks will be decreased.
201
+ """
202
+ # Manual TRACE_CALL emission for the context manager. The
203
+ # ``@enable_tracing`` decorator cannot wrap a ``@contextmanager``
204
+ # generator function (it would publish the call to the wrapper
205
+ # rather than to ``__enter__``). Emit enter/exit events
206
+ # directly, gated on the tracing flag for zero overhead when
207
+ # disabled.
208
+ if is_tracing_enabled():
209
+ publish_call_event(
210
+ "lmcache.v1.distributed.storage_manager."
211
+ "StorageManager.read_prefetched_results.__enter__",
212
+ {"keys": keys},
213
+ )
214
+ read_results = self._l1_manager.unsafe_read(keys)
215
+ good_keys: list[ObjectKey] = []
216
+ good_objs: list[MemoryObj] = []
217
+ bad_keys: list[ObjectKey] = []
218
+ all_good = True
219
+ for k, (e, o) in read_results.items():
220
+ if o is None:
221
+ logger.error(
222
+ "Failed to read prefetched object %s from L1 storage: %s",
223
+ k,
224
+ strerror(e),
225
+ )
226
+ bad_keys.append(k)
227
+ all_good = False
228
+ continue
229
+
230
+ good_keys.append(k)
231
+ good_objs.append(o)
232
+
233
+ successfully_yielded = False
234
+
235
+ try:
236
+ yield good_objs if all_good else None
237
+ successfully_yielded = True
238
+ except Exception:
239
+ logger.exception(
240
+ "Exception occurred while processing read prefetched results",
241
+ )
242
+ raise
243
+ finally:
244
+ # Decrease the read lock for all successfully read memory objects
245
+ # if None is yielded or exception occurs during caller's processing
246
+ if not all_good or not successfully_yielded:
247
+ self._l1_manager.finish_read(good_keys)
248
+ self._event_bus.publish(
249
+ Event(
250
+ event_type=EventType.SM_READ_PREFETCHED_FINISHED,
251
+ metadata={
252
+ "succeeded_keys": good_keys,
253
+ "failed_keys": bad_keys,
254
+ },
255
+ )
256
+ )
257
+ if is_tracing_enabled():
258
+ publish_call_event(
259
+ "lmcache.v1.distributed.storage_manager."
260
+ "StorageManager.read_prefetched_results.__exit__",
261
+ {"keys": keys},
262
+ )
263
+
264
+ @enable_tracing()
265
+ def finish_read_prefetched(
266
+ self,
267
+ keys: list[ObjectKey],
268
+ extra_count: int = 0,
269
+ ) -> None:
270
+ """Finish reading prefetched objects.
271
+
272
+ Args:
273
+ keys: Object keys that have been read.
274
+ extra_count: Extra read locks to release per key
275
+ (on top of the default 1).
276
+ """
277
+ finish_result = self._l1_manager.finish_read(keys, extra_count=extra_count)
278
+ successful_keys = [k for k, e in finish_result.items() if e == L1Error.SUCCESS]
279
+ failed_keys = [k for k, e in finish_result.items() if e != L1Error.SUCCESS]
280
+ self._event_bus.publish(
281
+ Event(
282
+ event_type=EventType.SM_READ_PREFETCHED_FINISHED,
283
+ metadata={
284
+ "succeeded_keys": successful_keys,
285
+ "failed_keys": failed_keys,
286
+ },
287
+ )
288
+ )
289
+
290
+ @enable_tracing()
291
+ def submit_prefetch_task(
292
+ self,
293
+ keys: list[ObjectKey],
294
+ layout_desc: MemoryLayoutDesc,
295
+ extra_count: int = 0,
296
+ external_request_id: str = "",
297
+ ) -> PrefetchHandle:
298
+ """Prefetch objects into L1 asynchronously.
299
+
300
+ Args:
301
+ keys: Object keys to prefetch.
302
+ layout_desc: Memory layout description.
303
+ extra_count: Extra workers (on top of the default
304
+ 1) that will independently retrieve the same
305
+ key. Total locks = 1 + extra_count.
306
+ external_request_id: Request ID from the caller
307
+ for end-to-end log tracing.
308
+
309
+ Returns:
310
+ PrefetchHandle to track the task.
311
+ """
312
+ # NOTE: now we only have L1, so the prefetch is essentially checking how many
313
+ # objects are already in L1, and adding read locks to them.
314
+
315
+ l1_read_result = self._l1_manager.reserve_read(keys, extra_count=extra_count)
316
+ hit_count = 0
317
+ for key in keys:
318
+ entry = l1_read_result.get(key, None)
319
+ if entry is None:
320
+ break
321
+
322
+ err, obj = entry
323
+ if err != L1Error.SUCCESS:
324
+ break
325
+
326
+ hit_count += 1
327
+
328
+ # NOTE: For L1, there will be cases that "object in the middle" is not found.
329
+ # In this case, we need to `finish_read` for the latter objects so that
330
+ # there won't be dangling read locks.
331
+ skipped_keys = []
332
+ for key in keys[hit_count:]:
333
+ if key in l1_read_result and l1_read_result[key][1] is not None:
334
+ # this key is actually reserved, need to release the read lock
335
+ skipped_keys.append(key)
336
+
337
+ if skipped_keys:
338
+ self._l1_manager.finish_read(skipped_keys, extra_count=extra_count)
339
+
340
+ self._event_bus.publish(
341
+ Event(
342
+ event_type=EventType.SM_READ_PREFETCHED,
343
+ metadata={
344
+ "succeeded_keys": keys[:hit_count],
345
+ "failed_keys": keys[hit_count:],
346
+ },
347
+ )
348
+ )
349
+
350
+ # Submit remaining keys to L2 prefetch controller
351
+ remaining_keys = keys[hit_count:]
352
+ prefetch_request_id = -1
353
+ if remaining_keys and self._l2_adapters:
354
+ prefetch_request_id = self._prefetch_controller.submit_prefetch_request(
355
+ remaining_keys,
356
+ layout_desc,
357
+ extra_count=extra_count,
358
+ )
359
+
360
+ submit_time = time.monotonic()
361
+ logger.debug(
362
+ "Prefetch request submitted: "
363
+ "%d total keys, %d L1 prefix hits, "
364
+ "%d remaining for L2 "
365
+ "(external_request_id=%s, "
366
+ "prefetch_request_id=%d)",
367
+ len(keys),
368
+ hit_count,
369
+ len(remaining_keys),
370
+ external_request_id,
371
+ prefetch_request_id,
372
+ )
373
+
374
+ return PrefetchHandle(
375
+ prefetch_request_id=prefetch_request_id,
376
+ external_request_id=external_request_id,
377
+ l1_prefix_hit_count=hit_count,
378
+ total_requested_keys=len(keys),
379
+ submit_time=submit_time,
380
+ )
381
+
382
+ def query_prefetch_lookup_hits(
383
+ self,
384
+ handle: PrefetchHandle,
385
+ ) -> int | None:
386
+ """
387
+ Query the number of prefix hit chunks for a prefetch task before
388
+ the L2 prefetching is done.
389
+
390
+ Args:
391
+ handle (PrefetchHandle): The handle of the lookup task.
392
+
393
+ Returns:
394
+ the number of prefix hit chunks if the lookup is done, None if
395
+ it's still in progress, or the prefetch task is already done.
396
+
397
+ Note:
398
+ This function is designed for the scenario where the caller wants
399
+ to check the L1 prefix hits as soon as possible without waiting for
400
+ the whole prefetch task to be done.
401
+ When the prefetch task is already done and the prefetch task result
402
+ has already been queried by `query_prefetch_status`, this function
403
+ will return None forever for the same prefetch handle.
404
+ Therefore, it's the caller’s responsibility to make sure not calling
405
+ this function after the prefetch task is done.
406
+ """
407
+ if handle.prefetch_request_id == -1:
408
+ # No L2 request, the prefix hit count is final
409
+ return handle.l1_prefix_hit_count
410
+
411
+ # Have L2 request, need to check the status from prefetch controller
412
+ l2_r = self._prefetch_controller.query_lookup_result(handle.prefetch_request_id)
413
+
414
+ if l2_r is None:
415
+ # L2 prefetch is still in progress or it's already done and
416
+ # the result has been consumed by `query_prefetch_status`
417
+ return None
418
+
419
+ # L2 lookup is done, return the total prefix hit count (L1 + L2)
420
+ return handle.l1_prefix_hit_count + l2_r
421
+
422
+ def query_prefetch_status(
423
+ self,
424
+ handle: PrefetchHandle,
425
+ ) -> int | None:
426
+ """
427
+ Query the status of the prefetch task.
428
+
429
+ Args:
430
+ handle (PrefetchHandle): The handle of the prefetch task.
431
+
432
+ Returns:
433
+ the number of prefix hit chunks if the prefetch is done, None if
434
+ it's still in progress.
435
+ """
436
+ l2_result: int = 0
437
+
438
+ # Have L2 request, need to check the result from prefetch controller
439
+ if handle.prefetch_request_id != -1:
440
+ l2_r = self._prefetch_controller.query_prefetch_result(
441
+ handle.prefetch_request_id
442
+ )
443
+
444
+ if l2_r is None:
445
+ return None
446
+ l2_result = l2_r # Just to make linter happy
447
+
448
+ total_hits = handle.l1_prefix_hit_count + l2_result
449
+ elapsed_ms = (time.monotonic() - handle.submit_time) * 1000
450
+
451
+ if total_hits > 0:
452
+ logger.info(
453
+ "Prefetch request completed (L1+L2): "
454
+ "%d/%d prefix hits (%d L1, %d L2) "
455
+ "in %.1f ms "
456
+ "(external_request_id=%s, "
457
+ "prefetch_request_id=%d)",
458
+ total_hits,
459
+ handle.total_requested_keys,
460
+ handle.l1_prefix_hit_count,
461
+ l2_result,
462
+ elapsed_ms,
463
+ handle.external_request_id,
464
+ handle.prefetch_request_id,
465
+ )
466
+ return total_hits
467
+
468
+ def touch_l1_keys(self, keys: list[ObjectKey]):
469
+ """
470
+ Touch the keys in L1 storage, marking the keys
471
+ as accessed(retrieved or stored).
472
+
473
+ Args:
474
+ keys (list[ObjectKey]): List of object keys to touch.
475
+ """
476
+ self._l1_manager.touch_keys(keys)
477
+
478
+ def clear(self, force: bool = False):
479
+ """
480
+ Clear data in the storage manager.
481
+
482
+ Args:
483
+ force: If True, clear ALL objects including locked ones.
484
+ This may corrupt in-flight store/prefetch operations.
485
+ If False (default), only clear unlocked objects, keeping
486
+ write-locked and read-locked objects intact.
487
+ """
488
+ self._l1_manager.clear(force=force)
489
+
490
+ def close(self):
491
+ """
492
+ Close the storage manager and release all resources.
493
+ """
494
+ self._prefetch_controller.stop()
495
+ self._store_controller.stop()
496
+ self._eviction_controller.stop()
497
+ self._l2_eviction_controller.stop()
498
+
499
+ for adapter in self._l2_adapters:
500
+ adapter.close()
501
+
502
+ self._l1_manager.close()
503
+
504
+ def report_status(self) -> dict:
505
+ """Return a status dict aggregating all sub-component statuses."""
506
+ l1 = self._l1_manager.report_status()
507
+ store = self._store_controller.report_status()
508
+ prefetch = self._prefetch_controller.report_status()
509
+ l1_eviction = self._eviction_controller.report_status()
510
+ l2_eviction = self._l2_eviction_controller.report_status()
511
+ adapters = [a.report_status() for a in self._l2_adapters]
512
+ children = [l1, store, prefetch, l1_eviction, l2_eviction] + adapters
513
+ return {
514
+ "is_healthy": all(c["is_healthy"] for c in children),
515
+ "l1_manager": l1,
516
+ "store_controller": store,
517
+ "prefetch_controller": prefetch,
518
+ "l1_eviction_controller": l1_eviction,
519
+ "l2_eviction_controller": l2_eviction,
520
+ "l2_adapters": adapters,
521
+ "num_l2_adapters": len(self._l2_adapters),
522
+ }
523
+
524
+ # Functions for debugging and testing
525
+ def memcheck(self) -> bool:
526
+ """
527
+ Perform memory check for all storage tiers.
528
+
529
+ Returns:
530
+ True if memory is consistent, False otherwise.
531
+ """
532
+ return self._l1_manager.memcheck()
@@ -0,0 +1,145 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from enum import Enum, auto
4
+ import asyncio
5
+ import threading
6
+
7
+
8
+ class EventType(Enum):
9
+ LOADING = auto()
10
+
11
+
12
+ class EventStatus(Enum):
13
+ ONGOING = auto()
14
+ DONE = auto()
15
+ NOT_FOUND = auto()
16
+
17
+
18
+ class EventManager:
19
+ """
20
+ A thread-safe event manager to manage asynchronous events.
21
+ Each event is identified by its type and a unique id.
22
+ Events are organized by status for efficient counting.
23
+ """
24
+
25
+ def __init__(self) -> None:
26
+ # Guard by lock
27
+ # Structure: events[event_type][event_status][event_id] = future
28
+ self.events: dict[EventType, dict[EventStatus, dict[str, asyncio.Future]]] = {
29
+ et: {es: {} for es in EventStatus} for et in EventType
30
+ }
31
+ self.lock = threading.Lock()
32
+
33
+ def add_event(
34
+ self,
35
+ event_type: EventType,
36
+ event_id: str,
37
+ future: asyncio.Future,
38
+ ) -> None:
39
+ """
40
+ Add an event with the given type and id.
41
+ """
42
+ with self.lock:
43
+ status_dict = self.events.get(event_type, None)
44
+ assert status_dict is not None, (
45
+ f"Invalid event type {event_type} in EventManager."
46
+ )
47
+ status_dict[EventStatus.ONGOING][event_id] = future
48
+
49
+ def pop_event(
50
+ self,
51
+ event_type: EventType,
52
+ event_id: str,
53
+ ) -> asyncio.Future:
54
+ """
55
+ Pop and return the event with the given type and id.
56
+ """
57
+ with self.lock:
58
+ status_dict = self.events.get(event_type, None)
59
+ assert status_dict is not None, (
60
+ f"Invalid event type {event_type} in EventManager."
61
+ )
62
+ done_events = status_dict[EventStatus.DONE]
63
+ assert event_id in done_events, (
64
+ f"Event {event_id} of type {event_type} is not done or not found."
65
+ )
66
+ return done_events.pop(event_id)
67
+
68
+ def update_event_status(
69
+ self,
70
+ event_type: EventType,
71
+ event_id: str,
72
+ status: EventStatus,
73
+ ) -> None:
74
+ """
75
+ Update the status of the event with the given type and id.
76
+ """
77
+ with self.lock:
78
+ status_dict = self.events.get(event_type, None)
79
+ assert status_dict is not None, (
80
+ f"Invalid event type {event_type} in EventManager."
81
+ )
82
+ # Find the event in any status dict
83
+ event = None
84
+ for s in EventStatus:
85
+ if event_id in status_dict[s]:
86
+ event = status_dict[s].pop(event_id)
87
+ break
88
+
89
+ if event is None:
90
+ raise KeyError(f"Event {event_id} of type {event_type} not found.")
91
+
92
+ # Move to new status dict
93
+ status_dict[status][event_id] = event
94
+
95
+ def get_event_status(
96
+ self,
97
+ event_type: EventType,
98
+ event_id: str,
99
+ ) -> EventStatus:
100
+ """
101
+ Get the status of the event with the given type and id.
102
+ """
103
+ with self.lock:
104
+ status_dict = self.events.get(event_type, None)
105
+ assert status_dict is not None, (
106
+ f"Invalid event type {event_type} in EventManager."
107
+ )
108
+ for status in EventStatus:
109
+ if event_id in status_dict[status]:
110
+ return status
111
+ return EventStatus.NOT_FOUND
112
+
113
+ def get_events_count_by_status(
114
+ self,
115
+ event_type: EventType,
116
+ status: EventStatus,
117
+ ) -> int:
118
+ """
119
+ Get the count of events for the given event type and status.
120
+ This is a lightweight O(1) operation using dict length.
121
+ """
122
+ with self.lock:
123
+ status_dict = self.events.get(event_type, None)
124
+ if status_dict is None:
125
+ return 0
126
+ return len(status_dict[status])
127
+
128
+ def get_event_future(
129
+ self,
130
+ event_type: EventType,
131
+ event_id: str,
132
+ ) -> asyncio.Future:
133
+ """
134
+ Pop and return the event with the given type and id.
135
+ """
136
+ with self.lock:
137
+ status_dict = self.events.get(event_type, None)
138
+ assert status_dict is not None, (
139
+ f"Invalid event type {event_type} in EventManager."
140
+ )
141
+ done_events = status_dict[EventStatus.DONE]
142
+ assert event_id in done_events, (
143
+ f"Event {event_id} of type {event_type} is not done or not found."
144
+ )
145
+ return done_events[event_id]
@@ -0,0 +1,16 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ Custom exceptions for LMCache v1.
4
+ """
5
+
6
+
7
+ class IrrecoverableException(Exception):
8
+ """
9
+ Exception raised when an irrecoverable error occurs.
10
+
11
+ This exception indicates that the system has encountered an error
12
+ that cannot be recovered from, and the health monitor should stop
13
+ checking and propagate the error up.
14
+ """
15
+
16
+ pass