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,757 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ # Standard
3
+ from enum import IntEnum, auto
4
+ from typing import List, Optional, Union, no_type_check
5
+ import asyncio
6
+ import ctypes
7
+ import os
8
+ import random
9
+ import string
10
+ import time
11
+
12
+ # Third Party
13
+ import eic
14
+ import yaml
15
+
16
+ # First Party
17
+ from lmcache.logging import init_logger
18
+ from lmcache.utils import CacheEngineKey, _lmcache_nvtx_annotate
19
+ from lmcache.v1.memory_management import MemoryObj, MixedMemoryAllocator
20
+ from lmcache.v1.protocol import RemoteMetadata
21
+ from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
22
+ from lmcache.v1.storage_backend.job_executor.pq_executor import AsyncPQExecutor
23
+ from lmcache.v1.storage_backend.local_cpu_backend import LocalCPUBackend
24
+
25
+ logger = init_logger(__name__)
26
+
27
+
28
+ class Priorities(IntEnum):
29
+ PEEK = auto()
30
+ PREFETCH = auto()
31
+ GET = auto()
32
+ PUT = auto()
33
+
34
+
35
+ class PerformanceTimer:
36
+ def __init__(self, key, op):
37
+ self.key = key
38
+ self.size = 0
39
+ self.op = op
40
+ self.start_times = {}
41
+ self.elapsed_times = {}
42
+
43
+ def set_size(self, size):
44
+ self.size = size
45
+
46
+ def start(self, operation_name):
47
+ self.start_times[operation_name] = time.perf_counter()
48
+
49
+ # unit: us
50
+ def stop(self, operation_name):
51
+ if operation_name not in self.start_times:
52
+ raise ValueError(f"operation {operation_name} not found")
53
+
54
+ end_time = time.perf_counter()
55
+ start_time = self.start_times[operation_name]
56
+ elapsed_time = end_time - start_time
57
+ self.elapsed_times[operation_name] = elapsed_time * 1000000
58
+ del self.start_times[operation_name]
59
+ return elapsed_time
60
+
61
+ def get_elapsed_time(self, operation_name):
62
+ return self.elapsed_times.get(operation_name)
63
+
64
+ def get_all_elapsed_times(self):
65
+ return self.elapsed_times
66
+
67
+ def debug_all_elapsed_times(self):
68
+ logger.debug(f"== Perf key:{self.key} =========")
69
+ logger.debug(f"== Perf op {self.op} size {self.size} =========")
70
+ for op, item in self.elapsed_times.items():
71
+ logger.debug(f"Step: {op} cost {item:.2f} us")
72
+ logger.debug(f"== Perf op {self.op} size {self.size} =========")
73
+
74
+
75
+ class FlexibleDRAMMemoryPool:
76
+ def __init__(self, conn):
77
+ self._init = False
78
+ self.connection = conn
79
+ self.used_mem = {}
80
+
81
+ def allocate(self, size):
82
+ ptr = self.connection.allocate_managed_buffer(size)
83
+ if ptr == 0:
84
+ logger.error(f"fail to allocate dram pool, ptr {ptr}, size {size}")
85
+ return ptr
86
+ logger.debug(f"allocate dram pool: ptr {ptr}, size {size}")
87
+ self.used_mem[ptr] = size
88
+ return ptr
89
+
90
+ def deallocate(self, ptr):
91
+ size = self.used_mem.get(ptr)
92
+ if size is not None:
93
+ logger.debug(f"deallocate dram pool: ptr {ptr}, size {size}")
94
+ self.connection.free_managed_buffer(ptr, size)
95
+ del self.used_mem[ptr]
96
+
97
+
98
+ def _make_dir(path: str):
99
+ try:
100
+ if not os.path.exists(path):
101
+ os.makedirs(path)
102
+ logger.info(f"create dir '{path}' success")
103
+ except OSError as e:
104
+ logger.error(f"create dir '{path}' error {e}")
105
+
106
+
107
+ class EICConnector(RemoteConnector):
108
+ """
109
+ The remote url should start with "eic://" and only have one host-port pair
110
+ """
111
+
112
+ def __init__(
113
+ self,
114
+ endpoint: str,
115
+ loop: asyncio.AbstractEventLoop,
116
+ memory_allocator: LocalCPUBackend,
117
+ ):
118
+ # initialize base class, which includes some common attributes
119
+ super().__init__(memory_allocator.config, memory_allocator.metadata)
120
+
121
+ logger.info("init EICConnector")
122
+ logger.info(f"try connect to eic: {endpoint}")
123
+
124
+ self.loop = loop
125
+ self.memory_allocator = memory_allocator
126
+
127
+ # Initialize pq_executor early to avoid AttributeError
128
+ try:
129
+ self.pq_executor = AsyncPQExecutor(loop)
130
+ logger.info("AsyncPQExecutor initialized successfully")
131
+ except Exception as e:
132
+ logger.error(f"Failed to initialize AsyncPQExecutor: {e}")
133
+ raise
134
+
135
+ self.cudaError_t = ctypes.c_int
136
+ self.cudaMemcpyDeviceToHost = 2
137
+ self.cudaMemcpyHostToDevice = 1
138
+ self.cuda_lib = ctypes.CDLL("libcudart.so")
139
+ self.cuda_lib.cudaMemcpy.argtypes = [
140
+ ctypes.c_void_p,
141
+ ctypes.c_void_p,
142
+ ctypes.c_size_t,
143
+ ctypes.c_int,
144
+ ]
145
+ self.cuda_lib.cudaMemcpy.restype = self.cudaError_t
146
+
147
+ self.enable_compare = False
148
+
149
+ config_file = os.getenv("LMCACHE_CONFIG_FILE")
150
+ if config_file is None:
151
+ raise ValueError("LMCACHE_CONFIG_FILE environment variable is not set")
152
+ with open(config_file, "r") as fin:
153
+ config = yaml.safe_load(fin)
154
+
155
+ remote_url = config.get("remote_url", None)
156
+ logger.info(f"eic remote_url: {remote_url}")
157
+
158
+ eic_instance_id = config.get("eic_instance_id", None)
159
+ logger.info(f"eic instance_id: {eic_instance_id}")
160
+
161
+ eic_thread_num = config.get("eic_thread_num", 6)
162
+ logger.info(f"eic thread_num: {eic_thread_num}")
163
+
164
+ eic_log_dir = config.get("eic_log_dir", None)
165
+ logger.info(f"eic log_dir: {eic_log_dir}")
166
+
167
+ eic_log_level = config.get("eic_log_level", 1)
168
+ logger.info(f"eic log_level: {eic_log_level}")
169
+
170
+ eic_trans_type = config.get("eic_trans_type", 3)
171
+ logger.info(f"eic trans_type: {eic_trans_type}")
172
+
173
+ self.eic_kv_ttl = config.get("eic_kv_ttl", -1)
174
+ logger.info(f"eic eic_kv_ttl: {self.eic_kv_ttl}")
175
+
176
+ self.eic_kv_ns = config.get("eic_kv_ns", "")
177
+ logger.info(f"eic eic_kv_ns: {self.eic_kv_ns}")
178
+
179
+ eic_flag_file = config.get("eic_flag_file", None)
180
+ logger.info(f"eic flag_file: {eic_flag_file}")
181
+
182
+ _make_dir(eic_log_dir)
183
+
184
+ self.connection = eic.Client()
185
+ init_option = eic.InitOption()
186
+ init_option.log_dir = eic_log_dir
187
+ init_option.log_level = eic.LogLevel(eic_log_level)
188
+ init_option.transport_type = eic.TransportType(eic_trans_type)
189
+ init_option.flag_file = eic_flag_file
190
+ endpoint = endpoint.removeprefix("eic://").removesuffix("/")
191
+ ret = self.connection.init(eic_instance_id, endpoint, init_option)
192
+ if ret != 0:
193
+ logger.error(f"fail to init eic client, ret: {ret}")
194
+ raise RuntimeError(
195
+ f"Failed to initialize eic client with error code: {ret}"
196
+ )
197
+ else:
198
+ logger.info(f"init eic client success, ret: {ret}")
199
+
200
+ self.trans_type = eic.TransportType(eic_trans_type)
201
+
202
+ # Register memory in rdma and gdr scenarios
203
+ if self.trans_type == eic.TransportType.TRANSPORT_GDR:
204
+ if not isinstance(self.memory_allocator, LocalCPUBackend):
205
+ raise RuntimeError("memory_allocator must be LocalCPUBackend")
206
+ allocator = self.memory_allocator.memory_allocator
207
+ if not isinstance(allocator, MixedMemoryAllocator):
208
+ raise RuntimeError(
209
+ "memory_allocator.memory_allocator must be MixedMemoryAllocator"
210
+ )
211
+
212
+ if hasattr(allocator, "pin_allocator") and hasattr(
213
+ allocator.pin_allocator, "buffer"
214
+ ):
215
+ mem_pool = allocator.pin_allocator.buffer
216
+ meminfo = eic.MemoryInfo()
217
+ meminfo.type = eic.MemoryType.MEMORY_CUDA
218
+ meminfo.cuda_id = 0
219
+
220
+ vals = eic.IOBuffers()
221
+ vals.append(
222
+ mem_pool.data_ptr(),
223
+ mem_pool.numel() * mem_pool.element_size(),
224
+ True,
225
+ )
226
+
227
+ if self.connection.register_memory(vals, meminfo):
228
+ logger.info("register mixed memory pin buffer success")
229
+ else:
230
+ logger.error("fail to register mixed memory pin buffer")
231
+ exit(1)
232
+ else:
233
+ logger.error("mixed memory pin buffer is None")
234
+ exit(1)
235
+ self.prebuilt_connection()
236
+
237
+ def prebuilt_connection(self) -> None:
238
+ def random_string(N):
239
+ return self.eic_kv_ns.join(
240
+ random.choices(string.ascii_uppercase + string.digits, k=N)
241
+ )
242
+
243
+ try:
244
+ for i in range(2048):
245
+ key = random_string(30)
246
+ self._exists_sync(key)
247
+ logger.info("eic prebuilt connection finish")
248
+ except Exception as e:
249
+ logger.error(f"Error in prebuilt connection thread: {e}")
250
+
251
+ def delete_sync(self, key: str) -> bool:
252
+ keys = eic.StringVector()
253
+ keys.append(key)
254
+ status_code, _ = self.connection.mdel(keys)
255
+ if status_code != eic.StatusCode.SUCCESS:
256
+ logger.debug(f"eic delete {key} failed, status_code {status_code}")
257
+ return False
258
+ return True
259
+
260
+ def _exists_sync(self, key_str: str) -> bool:
261
+ keys = eic.StringVector()
262
+ keys.append(key_str)
263
+ exist_option = eic.ExistOption()
264
+ status_code, exist_outcome = self.connection.mexist(keys, exist_option)
265
+ if status_code != eic.StatusCode.SUCCESS:
266
+ logger.debug(f"eic exists {key_str} failed, status_code {status_code}")
267
+ return False
268
+
269
+ err_code = exist_outcome.status_codes[0]
270
+ success = err_code == eic.StatusCode.SUCCESS
271
+ if success:
272
+ logger.debug(f"eic exists {key_str} success")
273
+ else:
274
+ logger.debug(
275
+ f"eic exists {key_str} failed, status_code {status_code} "
276
+ "err_code {err_code}"
277
+ )
278
+ return success
279
+
280
+ async def _exists(self, key: CacheEngineKey) -> bool:
281
+ return self._exists_sync(key.to_string())
282
+
283
+ async def exists(self, key: CacheEngineKey) -> bool:
284
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
285
+ logger.error("pq_executor is not initialized in EICConnector")
286
+ raise AttributeError("pq_executor is not initialized")
287
+
288
+ return await self.pq_executor.submit_job(
289
+ self._exists, key=key, priority=Priorities.PEEK
290
+ )
291
+
292
+ def exists_sync(self, key: CacheEngineKey) -> bool:
293
+ return self._exists_sync(key.to_string())
294
+
295
+ async def get_meta(self, key_str: str) -> Optional[RemoteMetadata]:
296
+ perf_timer = PerformanceTimer(key_str, "get_meta")
297
+ perf_timer.start("total_cost")
298
+
299
+ # Get Meta: generate meta keys and vals
300
+ meta_keys = eic.StringVector()
301
+ meta_vals = eic.IOBuffers()
302
+
303
+ # Get Meta: generate meta buffer tensor
304
+ perf_timer.start("alloc_mem")
305
+ meta_key = key_str + "_meta"
306
+ meta_size = self.remote_metadata_bytes
307
+ meta_bytes = bytearray(meta_size)
308
+ meta_bytes_ptr = self.bytes_get_ptr(meta_bytes)
309
+
310
+ meta_keys.append(meta_key)
311
+ meta_vals.append(meta_bytes_ptr, meta_size, False)
312
+
313
+ perf_timer.set_size(meta_size)
314
+ perf_timer.stop("alloc_mem")
315
+
316
+ # Get Meta: recv meta buffer tensor
317
+ perf_timer.start("eic_mget")
318
+ get_option = eic.GetOption()
319
+ get_option.ns = self.eic_kv_ns
320
+ status_code, meta_vals, get_outcome = self.connection.mget(
321
+ meta_keys, get_option, meta_vals
322
+ )
323
+ err_code = get_outcome.status_codes[0]
324
+ if status_code != eic.StatusCode.SUCCESS or err_code != eic.StatusCode.SUCCESS:
325
+ if err_code == eic.StatusCode.KEY_NOT_EXIST:
326
+ logger.debug(
327
+ f"eic mget meta {key_str} failed, status_code {status_code}"
328
+ " err_code {err_code}"
329
+ )
330
+ else:
331
+ logger.error(
332
+ f"eic mget meta {key_str} failed, status_code {status_code}"
333
+ " err_code {err_code}"
334
+ )
335
+ return None
336
+ else:
337
+ logger.debug(f"eic mget meta {key_str} success")
338
+
339
+ perf_timer.stop("eic_mget")
340
+
341
+ perf_timer.start("serialize")
342
+ meta = RemoteMetadata.deserialize(meta_bytes[:meta_size])
343
+ perf_timer.stop("serialize")
344
+
345
+ perf_timer.stop("total_cost")
346
+ perf_timer.debug_all_elapsed_times()
347
+
348
+ return meta
349
+
350
+ async def get_data(self, key_str: str, meta: RemoteMetadata) -> Optional[MemoryObj]:
351
+ perf_timer = PerformanceTimer(key_str, "get_data")
352
+ perf_timer.start("total_cost")
353
+ perf_timer.start("alloc_obj")
354
+ memory_obj = self.memory_allocator.allocate(
355
+ meta.shapes,
356
+ meta.dtypes,
357
+ meta.fmt,
358
+ )
359
+ if memory_obj is None:
360
+ logger.error(
361
+ f"fail to allocate memory during remote receive key {key_str} length"
362
+ " {meta.length}"
363
+ )
364
+ return None
365
+ perf_timer.stop("alloc_obj")
366
+
367
+ perf_timer.start("alloc_mem")
368
+ obj_size = memory_obj.get_size()
369
+ data_ptr = memory_obj.tensor.data_ptr()
370
+ data_keys = eic.StringVector()
371
+ data_vals = eic.IOBuffers()
372
+ data_keys.append(key_str)
373
+
374
+ perf_timer.set_size(obj_size)
375
+ perf_timer.stop("alloc_mem")
376
+
377
+ try:
378
+ if self.trans_type == eic.TransportType.TRANSPORT_GDR:
379
+ data_vals.append(data_ptr, obj_size, True)
380
+ else:
381
+ data_vals.append(data_ptr, obj_size, False)
382
+
383
+ perf_timer.start("eic_mget")
384
+ get_option = eic.GetOption()
385
+ get_option.ns = self.eic_kv_ns
386
+ status_code, data_vals, get_outcome = self.connection.mget(
387
+ data_keys, get_option, data_vals
388
+ )
389
+ err_code = get_outcome.status_codes[0]
390
+ if (
391
+ status_code != eic.StatusCode.SUCCESS
392
+ or err_code != eic.StatusCode.SUCCESS
393
+ ):
394
+ logger.error(
395
+ f"eic mget data {key_str} failed, status_code {status_code} "
396
+ f"err_code {err_code}"
397
+ )
398
+ memory_obj.ref_count_down()
399
+ return None
400
+ else:
401
+ logger.debug(f"eic mget data {key_str} success")
402
+ except Exception as e:
403
+ logger.error(
404
+ f"eic mget data {key_str} raised exception: {e}", exc_info=True
405
+ )
406
+ memory_obj.ref_count_down()
407
+ return None
408
+
409
+ perf_timer.stop("eic_mget")
410
+
411
+ perf_timer.stop("total_cost")
412
+ perf_timer.debug_all_elapsed_times()
413
+
414
+ return memory_obj
415
+
416
+ async def _get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
417
+ key_str = key.to_string()
418
+ meta = await self.get_meta(key_str)
419
+ if meta is None:
420
+ return None
421
+ data = await self.get_data(key_str, meta)
422
+ return data
423
+
424
+ @_lmcache_nvtx_annotate
425
+ async def get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
426
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
427
+ logger.error("pq_executor is not initialized in EICConnector")
428
+ raise AttributeError("pq_executor is not initialized")
429
+
430
+ return await self.pq_executor.submit_job(
431
+ self._get, key=key, priority=Priorities.GET
432
+ )
433
+
434
+ def bytes_get_ptr(self, mv: Union[bytearray, memoryview, bytes]) -> int:
435
+ if isinstance(mv, bytes):
436
+ pointer = ctypes.cast(ctypes.c_char_p(mv), ctypes.POINTER(ctypes.c_char))
437
+ ptr = ctypes.addressof(pointer.contents)
438
+ return ptr
439
+ return ctypes.addressof(ctypes.c_char.from_buffer(mv))
440
+
441
+ async def _put(self, key: CacheEngineKey, memory_obj: MemoryObj):
442
+ return self._put_sync(key, memory_obj)
443
+
444
+ def _put_sync(self, key: CacheEngineKey, memory_obj: MemoryObj):
445
+ key_str = key.to_string()
446
+ logger.debug(f"eic put {key_str}")
447
+
448
+ perf_timer = PerformanceTimer(key_str, "put_data")
449
+ perf_timer.start("total_cost")
450
+ kv_bytes = memory_obj.byte_array
451
+ kv_tensor = memory_obj.tensor
452
+ kv_shapes = memory_obj.get_shapes()
453
+ kv_dtypes = memory_obj.get_dtypes()
454
+ memory_format = memory_obj.get_memory_format()
455
+ value_size = memory_obj.get_physical_size()
456
+
457
+ logger.debug(
458
+ f"eic put {key_str} data len {len(kv_bytes)} value_size {value_size}"
459
+ )
460
+
461
+ if kv_tensor is None:
462
+ logger.error(f"Memory object tensor is None for key {key_str}")
463
+ return
464
+
465
+ perf_timer.start("serialize")
466
+
467
+ # generate meta bytes
468
+ remote_meta = RemoteMetadata(
469
+ self.remote_metadata_bytes, kv_shapes, kv_dtypes, memory_format
470
+ )
471
+
472
+ logger.debug(f"eic meta {key_str} remote_meta{remote_meta}")
473
+
474
+ meta_bytes = remote_meta.serialize()
475
+
476
+ perf_timer.stop("serialize")
477
+
478
+ perf_timer.start("trans_address")
479
+ # generate meta & data ptr
480
+ meta_ptr = self.bytes_get_ptr(meta_bytes)
481
+ meta_size = len(meta_bytes)
482
+ data_ptr = kv_tensor.data_ptr()
483
+ data_size = len(kv_bytes)
484
+ perf_timer.set_size(data_size)
485
+ perf_timer.stop("trans_address")
486
+
487
+ logger.debug(
488
+ f"eic put {key_str} meta ptr {meta_ptr} len {meta_size} data ptr {data_ptr}"
489
+ " len {data_size}"
490
+ )
491
+
492
+ perf_timer.start("eic_mset")
493
+ keys = eic.StringVector()
494
+ vals = eic.IOBuffers()
495
+
496
+ # set meta key & value
497
+ meta_key = key_str + "_meta"
498
+ keys.append(meta_key)
499
+ vals.append(meta_ptr, meta_size, False)
500
+
501
+ # set data key & value
502
+ keys.append(key_str)
503
+ if self.trans_type == eic.TransportType.TRANSPORT_GDR:
504
+ vals.append(data_ptr, data_size, True)
505
+ else:
506
+ vals.append(data_ptr, data_size, False)
507
+
508
+ # set options
509
+ set_option = eic.SetOption()
510
+ set_option.ns = self.eic_kv_ns
511
+ set_option.ttl_second = self.eic_kv_ttl
512
+
513
+ status_code, set_outcome = self.connection.mset(keys, vals, set_option)
514
+ if status_code != eic.StatusCode.SUCCESS:
515
+ logger.error(f"eic mset {key_str} failed, status_code {status_code}")
516
+
517
+ err_code = set_outcome.status_codes[0]
518
+ if err_code == eic.StatusCode.SUCCESS:
519
+ logger.debug(f"eic put meta key {meta_key} success")
520
+ else:
521
+ logger.error(f"eic put meta key {meta_key} failed, err_code {err_code}")
522
+
523
+ err_code = set_outcome.status_codes[1]
524
+ if err_code == eic.StatusCode.SUCCESS:
525
+ logger.debug(f"eic put data key {key_str} success")
526
+ else:
527
+ logger.error(f"eic put data key {key_str} failed, err_code {err_code}")
528
+
529
+ perf_timer.stop("eic_mset")
530
+ perf_timer.stop("total_cost")
531
+ perf_timer.debug_all_elapsed_times()
532
+
533
+ async def _batched_put(
534
+ self, keys: List[CacheEngineKey], memory_objs: List[MemoryObj]
535
+ ):
536
+ if not keys or not memory_objs:
537
+ return
538
+
539
+ # Prepare all keys and values for batch mset
540
+ eic_keys = eic.StringVector()
541
+ eic_vals = eic.IOBuffers()
542
+ # Keep references to meta_bytes to prevent dangling pointers
543
+ meta_list = []
544
+ for key, memory_obj in zip(keys, memory_objs, strict=False):
545
+ key_str = key.to_string()
546
+ logger.debug(f"eic batched_put processing {key_str}")
547
+
548
+ # Get memory object data
549
+ kv_bytes = memory_obj.byte_array
550
+ kv_tensor = memory_obj.tensor
551
+ kv_shapes = memory_obj.get_shapes()
552
+ kv_dtypes = memory_obj.get_dtypes()
553
+ memory_format = memory_obj.get_memory_format()
554
+ if kv_tensor is None:
555
+ logger.error(f"Memory object tensor is None for key {key_str}")
556
+ return
557
+
558
+ remote_meta = RemoteMetadata(
559
+ self.remote_metadata_bytes, kv_shapes, kv_dtypes, memory_format
560
+ )
561
+ meta_bytes = remote_meta.serialize()
562
+ meta_list.append(meta_bytes)
563
+ meta_ptr = self.bytes_get_ptr(meta_bytes)
564
+ meta_size = len(meta_bytes)
565
+ data_ptr = kv_tensor.data_ptr()
566
+ data_size = len(kv_bytes)
567
+
568
+ logger.info(
569
+ "eic batched_put %s, shapes: %s, dtypes: %s, fmt: %s",
570
+ key_str,
571
+ kv_shapes,
572
+ kv_dtypes,
573
+ memory_format,
574
+ )
575
+
576
+ # Add meta key & value
577
+ meta_key = key_str + "_meta"
578
+ eic_keys.append(meta_key)
579
+ eic_vals.append(meta_ptr, meta_size, False)
580
+ # Add data key & value
581
+ eic_keys.append(key_str)
582
+ if self.trans_type == eic.TransportType.TRANSPORT_GDR:
583
+ eic_vals.append(data_ptr, data_size, True)
584
+ else:
585
+ eic_vals.append(data_ptr, data_size, False)
586
+
587
+ set_option = eic.SetOption()
588
+ set_option.ns = self.eic_kv_ns
589
+ set_option.ttl_second = self.eic_kv_ttl
590
+
591
+ set_status_code, set_outcome = self.connection.mset(
592
+ eic_keys, eic_vals, set_option
593
+ )
594
+
595
+ if set_status_code != eic.StatusCode.SUCCESS:
596
+ logger.error(
597
+ f"eic batched_put mset data failed, status_code {set_status_code}"
598
+ )
599
+ return
600
+ for i, key_str in enumerate(eic_keys):
601
+ meta_key = key_str + "_meta"
602
+
603
+ outcome_err_code = set_outcome.status_codes[i]
604
+ log_key = meta_key if i % 2 == 0 else key_str
605
+ if outcome_err_code == eic.StatusCode.SUCCESS:
606
+ logger.debug(f"eic batched_put {log_key} success")
607
+ else:
608
+ logger.error(
609
+ f"eic batched_put {log_key} failed, err_code {outcome_err_code}"
610
+ )
611
+
612
+ async def put(self, key: CacheEngineKey, memory_obj: MemoryObj):
613
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
614
+ logger.error("pq_executor is not initialized in EICConnector")
615
+ raise AttributeError("pq_executor is not initialized")
616
+
617
+ return await self.pq_executor.submit_job(
618
+ self._put, key=key, memory_obj=memory_obj, priority=Priorities.PUT
619
+ )
620
+
621
+ def support_batched_put(self) -> bool:
622
+ return False
623
+
624
+ async def batched_put(
625
+ self, keys: List[CacheEngineKey], memory_objs: List[MemoryObj]
626
+ ):
627
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
628
+ logger.error("pq_executor is not initialized in EICConnector")
629
+ raise AttributeError("pq_executor is not initialized")
630
+
631
+ await self.pq_executor.submit_job(
632
+ self._batched_put,
633
+ keys=keys,
634
+ memory_objs=memory_objs,
635
+ priority=Priorities.PUT,
636
+ )
637
+
638
+ def support_batched_async_contains(self) -> bool:
639
+ return True
640
+
641
+ async def _batched_async_contains(
642
+ self,
643
+ lookup_id: str,
644
+ keys: List[CacheEngineKey],
645
+ pin: bool = False,
646
+ ) -> int:
647
+ if not keys:
648
+ return 0
649
+
650
+ # Convert all keys to strings at once
651
+ key_strings = eic.StringVector()
652
+ for key in keys:
653
+ key_strings.append(key.to_string())
654
+
655
+ # Use mexist to check all keys at once
656
+ exist_option = eic.ExistOption()
657
+ status_code, exist_outcome = self.connection.mexist(key_strings, exist_option)
658
+
659
+ if status_code != eic.StatusCode.SUCCESS:
660
+ logger.error(
661
+ f"eic batched_async_contains mexist failed, status_code {status_code}"
662
+ )
663
+ return 0
664
+
665
+ # Count consecutive hits from the beginning
666
+ num_hit_counts = 0
667
+ for i, key in enumerate(keys):
668
+ status_code = exist_outcome.status_codes[i]
669
+ if status_code != eic.StatusCode.SUCCESS:
670
+ logger.debug(
671
+ f"eic batched_async_contains {key.to_string()} miss,"
672
+ " err_code {status_code}"
673
+ )
674
+ break
675
+ num_hit_counts += 1
676
+ return num_hit_counts
677
+
678
+ async def batched_async_contains(
679
+ self,
680
+ lookup_id: str,
681
+ keys: List[CacheEngineKey],
682
+ pin: bool = False,
683
+ ) -> int:
684
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
685
+ logger.error("pq_executor is not initialized in EICConnector")
686
+ raise AttributeError("pq_executor is not initialized")
687
+
688
+ return await self.pq_executor.submit_job(
689
+ self._batched_async_contains,
690
+ lookup_id=lookup_id,
691
+ keys=keys,
692
+ pin=pin,
693
+ priority=Priorities.PEEK,
694
+ )
695
+
696
+ def support_batched_get(self) -> bool:
697
+ return True
698
+
699
+ async def _batched_get(
700
+ self, keys: List[CacheEngineKey]
701
+ ) -> List[Optional[MemoryObj]]:
702
+ # calling self.get will create a circular dependency
703
+ results = await asyncio.gather(*(self._get(key) for key in keys))
704
+ return results
705
+
706
+ async def batched_get(
707
+ self, keys: List[CacheEngineKey]
708
+ ) -> List[Optional[MemoryObj]]:
709
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
710
+ logger.error("pq_executor is not initialized in EICConnector")
711
+ raise AttributeError("pq_executor is not initialized")
712
+
713
+ return await self.pq_executor.submit_job(
714
+ self._batched_get,
715
+ keys=keys,
716
+ priority=Priorities.GET,
717
+ )
718
+
719
+ def support_batched_get_non_blocking(self) -> bool:
720
+ return True
721
+
722
+ async def _batched_get_non_blocking(
723
+ self,
724
+ lookup_id: str,
725
+ keys: List[CacheEngineKey],
726
+ ) -> List[MemoryObj]:
727
+ # calling self.get will create a circular dependency
728
+ results = await asyncio.gather(*(self._get(key) for key in keys))
729
+ return [r for r in results if r is not None]
730
+
731
+ async def batched_get_non_blocking(
732
+ self,
733
+ lookup_id: str,
734
+ keys: List[CacheEngineKey],
735
+ ) -> List[MemoryObj]:
736
+ if not hasattr(self, "pq_executor") or self.pq_executor is None:
737
+ logger.error("pq_executor is not initialized in EICConnector")
738
+ raise AttributeError("pq_executor is not initialized")
739
+
740
+ return await self.pq_executor.submit_job(
741
+ self._batched_get_non_blocking,
742
+ lookup_id=lookup_id,
743
+ keys=keys,
744
+ priority=Priorities.PREFETCH,
745
+ )
746
+
747
+ async def close(self):
748
+ if hasattr(self, "pq_executor") and self.pq_executor is not None:
749
+ await self.pq_executor.shutdown(wait=True)
750
+ if self.connection:
751
+ self.connection = None
752
+ logger.info("closed the eic connection")
753
+
754
+ # TODO
755
+ @no_type_check
756
+ async def list(self) -> List[str]:
757
+ pass