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,788 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from dataclasses import dataclass
4
+ from typing import TYPE_CHECKING, Any, Callable, List, Optional, Sequence, Union
5
+ import asyncio
6
+ import enum
7
+
8
+ # Third Party
9
+ import msgspec
10
+ import torch
11
+ import zmq
12
+ import zmq.asyncio
13
+
14
+ # First Party
15
+ from lmcache.logging import init_logger
16
+ from lmcache.observability import LMCStatsMonitor
17
+ from lmcache.utils import CacheEngineKey
18
+ from lmcache.v1.cache_controller.message import (
19
+ BatchedP2PLookupMsg,
20
+ BatchedP2PLookupRetMsg,
21
+ ErrorMsg,
22
+ )
23
+ from lmcache.v1.config import LMCacheEngineConfig
24
+ from lmcache.v1.memory_management import (
25
+ MemoryFormat,
26
+ MemoryObj,
27
+ PagedCpuGpuMemoryAllocator,
28
+ )
29
+ from lmcache.v1.metadata import LMCacheMetadata
30
+ from lmcache.v1.rpc_utils import (
31
+ DEFAULT_SOCKET_RECV_TIMEOUT_MS,
32
+ DEFAULT_SOCKET_SEND_TIMEOUT_MS,
33
+ get_zmq_context,
34
+ get_zmq_socket_with_timeout,
35
+ )
36
+ from lmcache.v1.storage_backend.abstract_backend import StorageBackendInterface
37
+ from lmcache.v1.storage_backend.local_cpu_backend import LocalCPUBackend
38
+ from lmcache.v1.transfer_channel import CreateTransferChannel
39
+ from lmcache.v1.transfer_channel.transfer_utils import (
40
+ P2PInitSideMsg,
41
+ P2PInitSideRetMsg,
42
+ )
43
+
44
+ if TYPE_CHECKING:
45
+ # First Party
46
+ from lmcache.v1.cache_controller import LMCacheWorker
47
+
48
+ logger = init_logger(__name__)
49
+
50
+
51
+ class P2PMsgBase(msgspec.Struct, tag=True):
52
+ """Base class for all P2P-related messages"""
53
+
54
+ pass
55
+
56
+
57
+ class BatchedLookupAndGetMsg(P2PMsgBase):
58
+ """Lookup and retrieve message"""
59
+
60
+ lookup_id: str
61
+
62
+ receiver_id: str
63
+
64
+ # CacheEngineKey in string form
65
+ keys: list[str]
66
+
67
+ # Indexes (remote) of allocated memory objects (to be written)
68
+ mem_indexes: list[int]
69
+
70
+
71
+ class BatchedLookupAndGetRetMsg(P2PMsgBase):
72
+ """Lookup and retrieve message"""
73
+
74
+ # Number of hit chunks
75
+ num_hit_chunks: int
76
+
77
+
78
+ class BatchedLookupAndPutMsg(P2PMsgBase):
79
+ """Lookup and retrieve message"""
80
+
81
+ sender_id: str
82
+
83
+ # CacheEngineKey in string form
84
+ keys: list[str]
85
+
86
+ # Number of tokens for each chunk
87
+ offsets: list[int]
88
+
89
+ # Indexes (remote) of allocated memory objects (to be read)
90
+ mem_indexes: list[int]
91
+
92
+
93
+ class BatchedLookupAndPutRetMsg(P2PMsgBase):
94
+ """Lookup and retrieve message"""
95
+
96
+ # Number of read chunks
97
+ num_read_chunks: int
98
+
99
+
100
+ class P2PErrorCode(enum.Enum):
101
+ """P2P error codes enumeration"""
102
+
103
+ P2P_SERVER_ERROR = enum.auto()
104
+ UNKNOWN_MSG_TYPE = enum.auto()
105
+ REMOTE_XFER_HANDLER_NOT_INITIALIZED = enum.auto()
106
+
107
+
108
+ class P2PErrorMsg(P2PMsgBase):
109
+ """
110
+ Error message, return error code to client.
111
+
112
+ -1 represents unknown msg type;
113
+ -2 represents remote xfer handler not initialized,
114
+ call `_ensure_peer_connection` first;
115
+ -3 represents p2p peer_request_handler error;
116
+ """
117
+
118
+ error_code: P2PErrorCode
119
+
120
+
121
+ P2PMsg = Union[
122
+ BatchedLookupAndGetMsg,
123
+ BatchedLookupAndGetRetMsg,
124
+ BatchedLookupAndPutMsg,
125
+ BatchedLookupAndPutRetMsg,
126
+ P2PErrorMsg,
127
+ ]
128
+
129
+
130
+ @dataclass
131
+ class PeerInfo:
132
+ """Peer information"""
133
+
134
+ peer_init_url: str # peer id
135
+ peer_lookup_url: str
136
+ lookup_lock: asyncio.Lock
137
+ lookup_socket: zmq.asyncio.Socket
138
+
139
+ def update_peer_lookup_url(self, new_peer_lookup_url: str):
140
+ if self.peer_lookup_url != new_peer_lookup_url:
141
+ logger.info(
142
+ "Target peer %s lookup url changed from %s to %s",
143
+ self.peer_init_url,
144
+ self.peer_lookup_url,
145
+ new_peer_lookup_url,
146
+ )
147
+ self.peer_lookup_url = new_peer_lookup_url
148
+
149
+ def update_lookup_socket(self, new_lookup_socket: zmq.asyncio.Socket):
150
+ try:
151
+ self.lookup_socket.close(linger=0)
152
+ except Exception as e:
153
+ logger.error("Failed to close peer %s lookup socket", self.peer_init_url, e)
154
+ self.lookup_socket = new_lookup_socket
155
+
156
+
157
+ # TODO(Jiayi): handle asymmetric TP.
158
+ class P2PBackend(StorageBackendInterface):
159
+ def __init__(
160
+ self,
161
+ config: LMCacheEngineConfig,
162
+ metadata: LMCacheMetadata,
163
+ loop: asyncio.AbstractEventLoop,
164
+ local_cpu_backend: LocalCPUBackend,
165
+ lmcache_worker: "LMCacheWorker",
166
+ ):
167
+ self.config = config
168
+ self.loop = loop
169
+ self.lmcache_worker = lmcache_worker
170
+ self.stats_monitor = LMCStatsMonitor.GetOrCreate()
171
+ assert config.p2p_host is not None, "p2p_host must be specified"
172
+ assert config.p2p_init_ports is not None, "p2p_init_ports must be specified"
173
+ assert config.p2p_lookup_ports is not None, "p2p_lookup_ports must be specified"
174
+
175
+ # Load timeout configurations from extra_config (in milliseconds)
176
+ self.socket_recv_timeout_ms = config.get_extra_config_value(
177
+ "p2p_socket_recv_timeout_ms", DEFAULT_SOCKET_RECV_TIMEOUT_MS
178
+ )
179
+ self.socket_send_timeout_ms = config.get_extra_config_value(
180
+ "p2p_socket_send_timeout_ms", DEFAULT_SOCKET_SEND_TIMEOUT_MS
181
+ )
182
+
183
+ # Load max retry count from extra_config
184
+ self.max_retry_count = config.get_extra_config_value("p2p_max_retry_count", 3)
185
+
186
+ # tp rank is worker id for now
187
+ self.tp_rank = metadata.worker_id
188
+
189
+ self.peer_host = config.p2p_host
190
+ self.peer_init_port = config.p2p_init_ports[self.tp_rank]
191
+ self.peer_init_url = f"{self.peer_host}:{self.peer_init_port}"
192
+
193
+ self.peer_lookup_port = config.p2p_lookup_ports[self.tp_rank]
194
+ self.peer_lookup_url = f"{self.peer_host}:{self.peer_lookup_port}"
195
+
196
+ self.lmcache_instance_id = config.lmcache_instance_id
197
+
198
+ # A CacheEngineKey (in int form) -> a list of
199
+ # (peer_init_url, peer_lookup_url, location)
200
+ self.local_lookup_cache: dict[int, tuple[str, str, str]] = {}
201
+ # the target peer info mapping
202
+ self.target_peer_info_mapping: dict[str, PeerInfo] = {}
203
+ # the lock for updating target peer info mapping
204
+ self.update_peer_lock = asyncio.Lock()
205
+
206
+ # A lookup_id -> (peer_init_url, location)
207
+ # TODO(chunxiaozheng): location is not used for now
208
+ self.lookup_id_to_peer_mapping: dict[str, tuple[str, str]] = {}
209
+
210
+ # TODO(Jiayi): support gpu and local storage p2p as well.
211
+ self.local_cpu_backend = local_cpu_backend
212
+ self.memory_allocator = local_cpu_backend.get_memory_allocator()
213
+ assert isinstance(self.memory_allocator, PagedCpuGpuMemoryAllocator)
214
+
215
+ self.full_size_shapes = self.memory_allocator.cpu_allocator.shapes
216
+ self.dtypes = self.memory_allocator.cpu_allocator.dtypes
217
+ self.fmt: MemoryFormat = (
218
+ MemoryFormat.KV_MLA_FMT if metadata.use_mla else MemoryFormat.KV_2LTD
219
+ )
220
+ self.chunk_size = config.chunk_size
221
+
222
+ device_type = (
223
+ "cpu" if config.nixl_buffer_device is None else config.nixl_buffer_device
224
+ )
225
+ self.transfer_channel = CreateTransferChannel(
226
+ channel_type=config.transfer_channel,
227
+ async_mode=True,
228
+ role="both",
229
+ buffer_ptr=self.memory_allocator.cpu_allocator.buffer_ptr,
230
+ buffer_size=self.memory_allocator.cpu_allocator.buffer_size,
231
+ align_bytes=self.memory_allocator.cpu_allocator.align_bytes,
232
+ tp_rank=self.tp_rank,
233
+ peer_init_url=self.peer_init_url,
234
+ peer_lookup_url=self.peer_lookup_url,
235
+ backends=config.nixl_backends,
236
+ event_loop=loop,
237
+ device=device_type,
238
+ )
239
+
240
+ self.running = asyncio.Event()
241
+ self.running.set()
242
+ self.async_context: Optional[zmq.asyncio.Context] = None
243
+ self.async_peer_socket: Optional[zmq.asyncio.Socket] = None
244
+ asyncio.run_coroutine_threadsafe(
245
+ self._run_peer_request_handler_with_recovery(), loop
246
+ )
247
+
248
+ def __str__(self) -> str:
249
+ return "P2PBackend"
250
+
251
+ async def _run_peer_request_handler_with_recovery(self) -> None:
252
+ """
253
+ Wrapper method that runs _handle_peer_requests with exception handling.
254
+ This ensures the handler keeps running even if unexpected errors occur.
255
+ """
256
+ while self.running.is_set():
257
+ try:
258
+ await self._handle_peer_requests()
259
+ # If _handle_peer_requests exits normally, break the loop
260
+ break
261
+ except asyncio.CancelledError:
262
+ logger.info("Peer request handler cancelled, shutting down")
263
+ break
264
+ except Exception as e:
265
+ logger.error(
266
+ "Peer request handler crashed: %s",
267
+ e,
268
+ exc_info=True,
269
+ )
270
+ # Fast failure: log error but continue running
271
+ # Add small delay to prevent tight error loop
272
+ await asyncio.sleep(0.1)
273
+ if self.async_peer_socket is not None:
274
+ logger.warning("Closing async peer socket.")
275
+ try:
276
+ self.async_peer_socket.close(linger=0)
277
+ except Exception as e:
278
+ logger.warning(
279
+ "Failed to close peer socket: %s",
280
+ e,
281
+ exc_info=True,
282
+ )
283
+
284
+ async def batched_async_contains(
285
+ self,
286
+ lookup_id: str,
287
+ keys: List[CacheEngineKey],
288
+ pin: bool = False,
289
+ ) -> int:
290
+ # Convert to hashes (int form)
291
+ hashes = [key.chunk_hash for key in keys]
292
+
293
+ # Tier 1 lookup: local lookup cache
294
+ # TODO(Jiayi): Please implement the local lookup cache.
295
+
296
+ # Tier 2 lookup in controller
297
+ msg = BatchedP2PLookupMsg(
298
+ instance_id=self.lmcache_instance_id,
299
+ worker_id=self.tp_rank,
300
+ hashes=hashes,
301
+ )
302
+ ret_msg = await self.lmcache_worker.async_put_and_wait_msg(msg)
303
+
304
+ if isinstance(ret_msg, ErrorMsg):
305
+ logger.error(
306
+ "Controller returned error for batched P2P lookup: %s",
307
+ ret_msg.error,
308
+ )
309
+ return 0
310
+
311
+ assert isinstance(ret_msg, BatchedP2PLookupRetMsg), (
312
+ f"Expected BatchedP2PLookupRetMsg, got {type(ret_msg)}"
313
+ )
314
+
315
+ # NOTE(Jiayi): For now we only support one peer hit.
316
+ layout_info = ret_msg.layout_info[0]
317
+ _, location, num_hit_chunks, target_peer_init_url = layout_info
318
+
319
+ logger.info(f"Got layout info from controller: {layout_info}")
320
+
321
+ if num_hit_chunks > 0:
322
+ try:
323
+ await self._ensure_peer_connection(target_peer_init_url)
324
+ self.lookup_id_to_peer_mapping[lookup_id] = (
325
+ target_peer_init_url,
326
+ location,
327
+ )
328
+ except Exception as e:
329
+ logger.error(
330
+ "Failed to ensure peer connection for lookup_id %s: %s",
331
+ lookup_id,
332
+ e,
333
+ exc_info=True,
334
+ )
335
+ return 0
336
+
337
+ # TODO(Jiayi): We could potentially update the local cache here.
338
+ # Or we can update after tier 3 lookup.
339
+
340
+ # NOTE(Jiayi): Tier 3 lookup is batched together with get
341
+ # in function `batched_get_non_blocking`.
342
+
343
+ return num_hit_chunks
344
+
345
+ async def _handle_peer_requests(self):
346
+ """
347
+ Handle `BatchedLookupAndGetMsg` issued by peers in `batched_get_non_blocking`.
348
+ """
349
+
350
+ logger.info(
351
+ "Starting P2P backend batched get handler at %s", self.peer_lookup_url
352
+ )
353
+ self.async_context = get_zmq_context()
354
+ self.async_peer_socket = get_zmq_socket_with_timeout(
355
+ self.async_context,
356
+ self.peer_lookup_url,
357
+ "tcp",
358
+ zmq.REP,
359
+ "bind",
360
+ self.socket_recv_timeout_ms,
361
+ self.socket_send_timeout_ms,
362
+ )
363
+
364
+ while self.running.is_set():
365
+ msg_bytes = await self.async_peer_socket.recv()
366
+ msg = msgspec.msgpack.decode(msg_bytes, type=P2PMsg)
367
+
368
+ num_tokens = len(msg.mem_indexes) * self.chunk_size
369
+ monitor_req_id = self.stats_monitor.on_p2p_transfer_request(num_tokens)
370
+
371
+ if isinstance(msg, BatchedLookupAndGetMsg):
372
+ ret_msg = await self._handle_batched_lookup_and_get(msg)
373
+ elif isinstance(msg, BatchedLookupAndPutMsg):
374
+ ret_msg = await self._handle_batched_lookup_and_put(msg)
375
+ else:
376
+ logger.error("Unknown message type: %s", type(msg))
377
+ ret_msg = P2PErrorMsg(error_code=P2PErrorCode.UNKNOWN_MSG_TYPE)
378
+
379
+ logger.info(f"P2P transfer finished for request {monitor_req_id}")
380
+ self.stats_monitor.on_p2p_transfer_finished(monitor_req_id)
381
+
382
+ await self.async_peer_socket.send(msgspec.msgpack.encode(ret_msg))
383
+
384
+ async def _handle_batched_lookup_and_get(
385
+ self, msg: BatchedLookupAndGetMsg
386
+ ) -> P2PMsgBase:
387
+ lookup_id = msg.lookup_id
388
+ mem_objs = None
389
+ try:
390
+ logger.info(
391
+ "Received P2P batched lookup and get msg, lookup_id: %s", lookup_id
392
+ )
393
+ receiver_id = msg.receiver_id
394
+ if not self.transfer_channel.remote_xfer_handler_exists(receiver_id):
395
+ logger.error(
396
+ "Receiver %s does not exist in transfer channel",
397
+ receiver_id,
398
+ )
399
+ return P2PErrorMsg(
400
+ error_code=P2PErrorCode.REMOTE_XFER_HANDLER_NOT_INITIALIZED
401
+ )
402
+
403
+ remote_mem_indexes = msg.mem_indexes
404
+ keys = [CacheEngineKey.from_string(key) for key in msg.keys]
405
+
406
+ # TODO(Jiayi): Optimally, there's no need to use async call
407
+ # for some backends (e.g., local cpu) as there's overhead for
408
+ # async function call.
409
+ num_hit_chunks = await self.local_cpu_backend.batched_async_contains(
410
+ lookup_id=lookup_id,
411
+ keys=keys,
412
+ pin=True,
413
+ )
414
+
415
+ mem_objs = await self.local_cpu_backend.batched_get_non_blocking(
416
+ lookup_id=lookup_id,
417
+ keys=keys[:num_hit_chunks],
418
+ )
419
+
420
+ channel_transfer_spec = {
421
+ "receiver_id": receiver_id,
422
+ "remote_indexes": remote_mem_indexes[:num_hit_chunks],
423
+ }
424
+ await self.transfer_channel.async_batched_write(
425
+ objects=mem_objs,
426
+ transfer_spec=channel_transfer_spec,
427
+ )
428
+
429
+ return BatchedLookupAndGetRetMsg(num_hit_chunks=num_hit_chunks)
430
+ except Exception as e:
431
+ logger.error(
432
+ "Error during P2P batched lookup and get operation "
433
+ "for lookup_id %s: %s",
434
+ lookup_id,
435
+ e,
436
+ exc_info=True,
437
+ )
438
+ return P2PErrorMsg(error_code=P2PErrorCode.P2P_SERVER_ERROR)
439
+ finally:
440
+ if mem_objs is not None:
441
+ for mem_obj in mem_objs:
442
+ mem_obj.ref_count_down()
443
+ mem_obj.unpin()
444
+
445
+ async def _handle_batched_lookup_and_put(
446
+ self, msg: BatchedLookupAndPutMsg
447
+ ) -> BatchedLookupAndPutRetMsg:
448
+ try:
449
+ logger.info("Received P2P batched lookup and put msg")
450
+ sender_id = msg.sender_id
451
+ r_mem_indexes = msg.mem_indexes
452
+ keys = [CacheEngineKey.from_string(key) for key in msg.keys]
453
+ offsets = msg.offsets
454
+
455
+ # TODO(Jiayi): Need to support more backend
456
+ r_mem_indexes_to_read = []
457
+ keys_to_read = []
458
+ local_mem_objs = []
459
+ keys_len = len(keys)
460
+ for idx, key in enumerate(keys):
461
+ if self.local_cpu_backend.contains(key, pin=False):
462
+ continue
463
+ r_mem_indexes_to_read.append(r_mem_indexes[idx])
464
+ if not self.config.save_unfull_chunk or idx < keys_len - 1:
465
+ shapes = self.full_size_shapes
466
+ else:
467
+ shapes = self._get_unfull_chunk_shapes(offsets[idx])
468
+ local_mem_obj = self.local_cpu_backend.allocate(
469
+ shapes, self.dtypes, self.fmt
470
+ )
471
+ local_mem_objs.append(local_mem_obj)
472
+ keys_to_read.append(key)
473
+
474
+ channel_transfer_spec = {
475
+ "sender_id": sender_id,
476
+ "remote_indexes": r_mem_indexes_to_read,
477
+ }
478
+ await self.transfer_channel.async_batched_read(
479
+ buffers=local_mem_objs,
480
+ transfer_spec=channel_transfer_spec,
481
+ )
482
+
483
+ self.local_cpu_backend.batched_submit_put_task(
484
+ keys=keys_to_read,
485
+ memory_objs=local_mem_objs,
486
+ )
487
+
488
+ return BatchedLookupAndPutRetMsg(num_read_chunks=len(local_mem_objs))
489
+ except Exception as e:
490
+ logger.error(
491
+ "Error during P2P batched lookup and put operation: %s",
492
+ e,
493
+ exc_info=True,
494
+ )
495
+ return BatchedLookupAndPutRetMsg(num_read_chunks=0)
496
+
497
+ async def _ensure_peer_connection(
498
+ self,
499
+ target_peer_init_url: str,
500
+ force_update: bool = False,
501
+ ) -> None:
502
+ if not force_update and target_peer_init_url in self.target_peer_info_mapping:
503
+ return
504
+
505
+ async with self.update_peer_lock:
506
+ # double check
507
+ if (
508
+ not force_update
509
+ and target_peer_init_url in self.target_peer_info_mapping
510
+ ):
511
+ return
512
+
513
+ init_side_msg = P2PInitSideMsg()
514
+ init_ret_msg = await self.transfer_channel.async_lazy_init_peer_connection(
515
+ local_id=self.peer_init_url,
516
+ peer_id=target_peer_init_url,
517
+ peer_init_url=target_peer_init_url,
518
+ init_side_msg=init_side_msg,
519
+ )
520
+ assert isinstance(init_ret_msg, P2PInitSideRetMsg)
521
+
522
+ peer_lookup_url = init_ret_msg.peer_lookup_url
523
+ peer_info = self.target_peer_info_mapping.get(target_peer_init_url, None)
524
+ lookup_socket = get_zmq_socket_with_timeout(
525
+ self.async_context,
526
+ peer_lookup_url,
527
+ "tcp",
528
+ zmq.REQ,
529
+ "connect",
530
+ self.socket_recv_timeout_ms,
531
+ self.socket_send_timeout_ms,
532
+ )
533
+ if peer_info is not None:
534
+ peer_info.update_peer_lookup_url(peer_lookup_url)
535
+ peer_info.update_lookup_socket(lookup_socket)
536
+ else:
537
+ self.target_peer_info_mapping[target_peer_init_url] = PeerInfo(
538
+ peer_init_url=target_peer_init_url,
539
+ peer_lookup_url=peer_lookup_url,
540
+ lookup_lock=asyncio.Lock(),
541
+ lookup_socket=lookup_socket,
542
+ )
543
+
544
+ logger.info(
545
+ "Established connection to peer_init_url: %s, peer_lookup_url: %s",
546
+ target_peer_init_url,
547
+ peer_lookup_url,
548
+ )
549
+
550
+ async def batched_get_non_blocking(
551
+ self,
552
+ lookup_id: str,
553
+ keys: list[CacheEngineKey],
554
+ transfer_spec: Any = None,
555
+ ) -> list[MemoryObj]:
556
+ target_peer_init_url, _ = self.lookup_id_to_peer_mapping.pop(lookup_id)
557
+
558
+ assert isinstance(transfer_spec, dict)
559
+ cum_chunk_lengths = transfer_spec.get("cum_chunk_lengths", None)
560
+ assert cum_chunk_lengths is not None, "cum_chunk_lengths must be provided"
561
+ assert isinstance(cum_chunk_lengths, list), "cum_chunk_lengths must be a list"
562
+
563
+ mem_objs = []
564
+ str_keys = []
565
+ keys_len = len(keys)
566
+ for idx, key in enumerate(keys):
567
+ if not self.config.save_unfull_chunk or idx < keys_len - 1:
568
+ shapes = self.full_size_shapes
569
+ else:
570
+ shapes = self._get_unfull_chunk_shapes(
571
+ cum_chunk_lengths[idx + 1] - cum_chunk_lengths[idx]
572
+ )
573
+ mem_obj = self.local_cpu_backend.allocate(shapes, self.dtypes, self.fmt)
574
+ mem_objs.append(mem_obj)
575
+ str_keys.append(key.to_string())
576
+
577
+ local_indexes = self.transfer_channel.get_local_mem_indices(mem_objs)
578
+
579
+ # NOTE(Jiayi): Tier 3 lookup is batched with retrieval.
580
+ msg = BatchedLookupAndGetMsg(
581
+ lookup_id=lookup_id,
582
+ receiver_id=self.peer_init_url,
583
+ keys=str_keys,
584
+ mem_indexes=local_indexes,
585
+ )
586
+
587
+ retry_count = 0
588
+ while retry_count < self.max_retry_count:
589
+ peer_info = self.target_peer_info_mapping[target_peer_init_url]
590
+ lookup_lock = peer_info.lookup_lock
591
+ async with lookup_lock:
592
+ lookup_socket = peer_info.lookup_socket
593
+ try:
594
+ retry_count += 1
595
+ await lookup_socket.send(msgspec.msgpack.encode(msg))
596
+ ret_msg_bytes = await lookup_socket.recv()
597
+ ret_msg = msgspec.msgpack.decode(ret_msg_bytes, type=P2PMsg)
598
+ if (
599
+ isinstance(ret_msg, P2PErrorMsg)
600
+ and ret_msg.error_code
601
+ == P2PErrorCode.REMOTE_XFER_HANDLER_NOT_INITIALIZED
602
+ ):
603
+ logger.warning(
604
+ "Peer connection not initialized for lookup_id %s, "
605
+ "ensure peer connection first, retry count: %s",
606
+ lookup_id,
607
+ retry_count,
608
+ )
609
+ await self._ensure_peer_connection(target_peer_init_url, True)
610
+ else:
611
+ break
612
+ except zmq.ZMQError as e:
613
+ logger.error(
614
+ "ZMQ error occurred for lookup_id %s. Error: %s",
615
+ lookup_id,
616
+ e,
617
+ )
618
+ await self._ensure_peer_connection(target_peer_init_url, True)
619
+ if retry_count == self.max_retry_count:
620
+ logger.error(
621
+ "Max retry count reached for lookup_id %s",
622
+ lookup_id,
623
+ )
624
+ self._cleanup_memory_objects(mem_objs)
625
+ return []
626
+ except Exception as e:
627
+ logger.error(
628
+ "Error during P2P get operation for lookup_id %s: %s",
629
+ lookup_id,
630
+ e,
631
+ exc_info=True,
632
+ )
633
+ self._cleanup_memory_objects(mem_objs)
634
+ return []
635
+
636
+ if isinstance(ret_msg, P2PErrorMsg):
637
+ logger.error(
638
+ "P2P error for lookup_id %s, error code: %s",
639
+ lookup_id,
640
+ ret_msg.error_code,
641
+ )
642
+ num_hit_chunks = 0
643
+ else:
644
+ num_hit_chunks = ret_msg.num_hit_chunks
645
+
646
+ hit_mem_objs = mem_objs[:num_hit_chunks]
647
+ for hit_mem_obj in hit_mem_objs:
648
+ hit_mem_obj.pin()
649
+ for missed_mem_obj in mem_objs[num_hit_chunks:]:
650
+ missed_mem_obj.ref_count_down()
651
+ return hit_mem_objs
652
+
653
+ def _get_unfull_chunk_shapes(self, num_tokens: int) -> list[torch.Size]:
654
+ shapes = []
655
+ for shape in self.full_size_shapes:
656
+ shape_list = list(shape)
657
+ shape_list[self.fmt.token_dim()] = num_tokens
658
+ shapes.append(torch.Size(shape_list))
659
+ return shapes
660
+
661
+ # NOTE: put-related functions are not supported for now.
662
+ async def async_batched_submit_put_task(
663
+ self,
664
+ keys: Sequence[CacheEngineKey],
665
+ objs: List[MemoryObj],
666
+ transfer_spec: Any = None,
667
+ on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
668
+ ) -> None:
669
+ # TODO(baoloongmao): Add exception handling for socket operations
670
+ # Code path for `move` operation in controller.
671
+ assert isinstance(transfer_spec, dict)
672
+ assert "target_peer_init_url" in transfer_spec
673
+ assert "offsets" in transfer_spec
674
+
675
+ target_peer_init_url = transfer_spec["target_peer_init_url"]
676
+ offsets = transfer_spec["offsets"]
677
+
678
+ await self._ensure_peer_connection(transfer_spec["target_peer_init_url"])
679
+
680
+ str_keys = [key.to_string() for key in keys]
681
+ local_indexes = self.transfer_channel.get_local_mem_indices(objs)
682
+
683
+ msg = BatchedLookupAndPutMsg(
684
+ sender_id=self.peer_init_url,
685
+ keys=str_keys,
686
+ offsets=offsets,
687
+ mem_indexes=local_indexes,
688
+ )
689
+
690
+ peer_info = self.target_peer_info_mapping[target_peer_init_url]
691
+ lookup_lock = peer_info.lookup_lock
692
+ async with lookup_lock:
693
+ lookup_socket = peer_info.lookup_socket
694
+ await lookup_socket.send(msgspec.msgpack.encode(msg))
695
+ ret_msg_bytes = await lookup_socket.recv()
696
+ ret_msg = msgspec.msgpack.decode(ret_msg_bytes, type=P2PMsg)
697
+
698
+ return ret_msg.num_read_chunks
699
+
700
+ def get_allocator_backend(self):
701
+ return self.local_cpu_backend
702
+
703
+ def _cleanup_memory_objects(self, mem_objs: list[MemoryObj]) -> None:
704
+ """Safely cleanup memory objects by decrementing reference counts"""
705
+ for mem_obj in mem_objs:
706
+ try:
707
+ mem_obj.ref_count_down()
708
+ except Exception as e:
709
+ logger.error("Error cleaning up memory object: %s", e)
710
+
711
+ def close(
712
+ self,
713
+ ) -> None:
714
+ """
715
+ Close the P2P backend and cleanup resources.
716
+ """
717
+ logger.info("Closing P2P backend")
718
+ self.running.clear()
719
+
720
+ # Close all lookup sockets
721
+ for peer_info in self.target_peer_info_mapping.values():
722
+ try:
723
+ peer_info.lookup_socket.close(linger=0)
724
+ except Exception as e:
725
+ logger.warning("Failed to close lookup socket: %s", e)
726
+ self.target_peer_info_mapping.clear()
727
+
728
+ # Close async peer socket
729
+ if self.async_peer_socket is not None:
730
+ try:
731
+ self.async_peer_socket.close(linger=0)
732
+ except Exception as e:
733
+ logger.warning("Failed to close async peer socket: %s", e)
734
+ self.async_peer_socket = None
735
+
736
+ # Close transfer channel
737
+ if hasattr(self, "transfer_channel") and self.transfer_channel is not None:
738
+ try:
739
+ self.transfer_channel.close()
740
+ except Exception as e:
741
+ logger.warning("Failed to close transfer channel: %s", e)
742
+
743
+ ############################################################
744
+ # Not-supported functions
745
+ ############################################################
746
+
747
+ # NOTE: synchronous contain is not supported for now.
748
+ def contains(self, key: CacheEngineKey, pin: bool = False) -> bool:
749
+ return False
750
+
751
+ # NOTE: put-related functions are not supported for now.
752
+ def exists_in_put_tasks(self, key: CacheEngineKey) -> bool:
753
+ raise NotImplementedError
754
+
755
+ def batched_submit_put_task(
756
+ self,
757
+ keys: Sequence[CacheEngineKey],
758
+ objs: List[MemoryObj],
759
+ transfer_spec: Any = None,
760
+ on_complete_callback: Optional[Callable[[CacheEngineKey], None]] = None,
761
+ ) -> None:
762
+ """P2P backend does not support put operations."""
763
+ pass
764
+
765
+ # NOTE: Synchronous get is not supported for now.
766
+ def get_blocking(
767
+ self,
768
+ key: CacheEngineKey,
769
+ ) -> Optional[MemoryObj]:
770
+ raise NotImplementedError
771
+
772
+ # NOTE: pin is useless for P2P backend now.
773
+ def pin(
774
+ self,
775
+ key: CacheEngineKey,
776
+ ) -> bool:
777
+ return False
778
+
779
+ # NOTE: unpin is useless for P2P backend now.
780
+ def unpin(
781
+ self,
782
+ key: CacheEngineKey,
783
+ ) -> bool:
784
+ return False
785
+
786
+ # NOTE: remove is useless for P2P backend now.
787
+ def remove(self, key: CacheEngineKey, force: bool = True) -> bool:
788
+ return False