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,167 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+ """
3
+ Filesystem native L2 adapter config and factory.
4
+
5
+ Backed by the native C++ filesystem connector wrapped with
6
+ ``NativeConnectorL2Adapter``.
7
+ """
8
+
9
+ # Future
10
+ from __future__ import annotations
11
+
12
+ # Standard
13
+ from typing import TYPE_CHECKING, Optional
14
+
15
+ if TYPE_CHECKING:
16
+ from lmcache.v1.distributed.internal_api import (
17
+ L1MemoryDesc,
18
+ )
19
+
20
+ # First Party
21
+ from lmcache.logging import init_logger
22
+ from lmcache.v1.distributed.l2_adapters.base import (
23
+ L2AdapterInterface,
24
+ )
25
+ from lmcache.v1.distributed.l2_adapters.config import (
26
+ L2AdapterConfigBase,
27
+ register_l2_adapter_type,
28
+ )
29
+ from lmcache.v1.distributed.l2_adapters.factory import (
30
+ register_l2_adapter_factory,
31
+ )
32
+
33
+ logger = init_logger(__name__)
34
+
35
+
36
+ class FSNativeL2AdapterConfig(L2AdapterConfigBase):
37
+ """
38
+ Config for an L2 adapter backed by the native C++
39
+ filesystem connector.
40
+
41
+ Fields:
42
+ - base_path: directory for storing KV cache files.
43
+ - num_workers: C++ worker threads for I/O (default 4).
44
+ - relative_tmp_dir: relative sub-dir for temp files.
45
+ - use_odirect: bypass page cache via O_DIRECT.
46
+ - read_ahead_size: trigger filesystem readahead by
47
+ reading this many bytes first (optional).
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ base_path: str,
53
+ num_workers: int = 4,
54
+ relative_tmp_dir: str = "",
55
+ use_odirect: bool = False,
56
+ read_ahead_size: Optional[int] = None,
57
+ max_capacity_gb: float = 0,
58
+ ):
59
+ self.base_path = base_path
60
+ self.num_workers = num_workers
61
+ self.relative_tmp_dir = relative_tmp_dir
62
+ self.use_odirect = use_odirect
63
+ self.read_ahead_size = read_ahead_size
64
+ self.max_capacity_gb = max_capacity_gb
65
+
66
+ @classmethod
67
+ def from_dict(cls, d: dict) -> "FSNativeL2AdapterConfig":
68
+ base_path = d.get("base_path")
69
+ if not isinstance(base_path, str) or not base_path:
70
+ raise ValueError("base_path must be a non-empty string")
71
+
72
+ num_workers = d.get("num_workers", 4)
73
+ if not isinstance(num_workers, int) or num_workers <= 0:
74
+ raise ValueError("num_workers must be a positive integer")
75
+
76
+ relative_tmp_dir = d.get("relative_tmp_dir", "")
77
+ if not isinstance(relative_tmp_dir, str):
78
+ raise ValueError("relative_tmp_dir must be a string")
79
+
80
+ use_odirect = d.get("use_odirect", False)
81
+ if not isinstance(use_odirect, bool):
82
+ raise ValueError("use_odirect must be a boolean")
83
+
84
+ read_ahead_size = d.get("read_ahead_size", None)
85
+ if read_ahead_size is not None:
86
+ if not isinstance(read_ahead_size, int) or read_ahead_size <= 0:
87
+ raise ValueError("read_ahead_size must be a positive integer")
88
+
89
+ max_capacity_gb = d.get("max_capacity_gb", 0)
90
+ if not isinstance(max_capacity_gb, (int, float)) or max_capacity_gb < 0:
91
+ raise ValueError("max_capacity_gb must be a non-negative number")
92
+
93
+ return cls(
94
+ base_path=base_path,
95
+ num_workers=num_workers,
96
+ relative_tmp_dir=str(relative_tmp_dir),
97
+ use_odirect=use_odirect,
98
+ read_ahead_size=read_ahead_size,
99
+ max_capacity_gb=float(max_capacity_gb),
100
+ )
101
+
102
+ @classmethod
103
+ def help(cls) -> str:
104
+ return (
105
+ "FS native L2 adapter config fields:\n"
106
+ "- base_path (str): directory for KV "
107
+ "cache files (required)\n"
108
+ "- num_workers (int): C++ worker threads "
109
+ "for I/O (default 4, >0)\n"
110
+ "- relative_tmp_dir (str): relative "
111
+ "sub-dir for temp files (default empty)\n"
112
+ "- use_odirect (bool): bypass page cache "
113
+ "via O_DIRECT (default false)\n"
114
+ "- read_ahead_size (int): trigger fs "
115
+ "readahead by reading this many bytes "
116
+ "first (optional)\n"
117
+ "- max_capacity_gb (float): max L2 capacity "
118
+ "in GB for usage tracking / eviction "
119
+ "(default 0 = disabled)"
120
+ )
121
+
122
+
123
+ def _create_fs_native_l2_adapter(
124
+ config: L2AdapterConfigBase,
125
+ l1_memory_desc: "Optional[L1MemoryDesc]" = None,
126
+ ) -> L2AdapterInterface:
127
+ """Create a NativeConnectorL2Adapter backed by the
128
+ C++ filesystem connector."""
129
+ try:
130
+ # First Party
131
+ from lmcache.lmcache_fs import (
132
+ LMCacheFSClient,
133
+ )
134
+ except ImportError as e:
135
+ raise RuntimeError(
136
+ "FS native L2 adapter requires the C++ FS "
137
+ "extension. Build with: pip install -e ."
138
+ ) from e
139
+
140
+ # Lazy import to avoid circular dependency
141
+ # First Party
142
+ from lmcache.v1.distributed.l2_adapters.native_connector_l2_adapter import ( # noqa: E501
143
+ NativeConnectorL2Adapter,
144
+ )
145
+
146
+ assert isinstance(config, FSNativeL2AdapterConfig)
147
+ native_client = LMCacheFSClient(
148
+ config.base_path,
149
+ config.num_workers,
150
+ config.relative_tmp_dir,
151
+ config.use_odirect,
152
+ config.read_ahead_size or 0,
153
+ )
154
+ logger.info(
155
+ "Created FS native L2 adapter: %s (workers=%d, odirect=%s, read_ahead=%s)",
156
+ config.base_path,
157
+ config.num_workers,
158
+ config.use_odirect,
159
+ config.read_ahead_size,
160
+ )
161
+ return NativeConnectorL2Adapter(
162
+ native_client, max_capacity_gb=config.max_capacity_gb
163
+ )
164
+
165
+
166
+ register_l2_adapter_type("fs_native", FSNativeL2AdapterConfig)
167
+ register_l2_adapter_factory("fs_native", _create_fs_native_l2_adapter)
@@ -0,0 +1,516 @@
1
+ # SPDX-License-Identifier: Apache-2.0
2
+
3
+ # Future
4
+ from __future__ import annotations
5
+
6
+ # Standard
7
+ from collections import defaultdict
8
+ from typing import TYPE_CHECKING, Optional
9
+ import asyncio
10
+ import copy
11
+ import os
12
+ import threading
13
+ import time
14
+
15
+ if TYPE_CHECKING:
16
+ from lmcache.v1.distributed.internal_api import (
17
+ L1MemoryDesc,
18
+ )
19
+
20
+ # First Party
21
+ from lmcache.logging import init_logger
22
+ from lmcache.native_storage_ops import Bitmap
23
+ from lmcache.v1.distributed.api import ObjectKey
24
+ from lmcache.v1.distributed.l2_adapters.base import L2AdapterInterface, L2TaskId
25
+ from lmcache.v1.distributed.l2_adapters.config import (
26
+ L2AdapterConfigBase,
27
+ register_l2_adapter_type,
28
+ )
29
+ from lmcache.v1.distributed.l2_adapters.factory import (
30
+ register_l2_adapter_factory,
31
+ )
32
+ from lmcache.v1.memory_management import MemoryObj, TensorMemoryObj
33
+
34
+ logger = init_logger(__name__)
35
+
36
+ # Helper function
37
+
38
+
39
+ def clone_tensor_memory_obj(obj: MemoryObj) -> TensorMemoryObj:
40
+ assert isinstance(obj, TensorMemoryObj), (
41
+ "Only TensorMemoryObj is supported in this mock adapter"
42
+ )
43
+ raw_tensor = obj.raw_tensor
44
+ assert raw_tensor is not None, (
45
+ "The tensor data of the object cannot be None for cloning"
46
+ )
47
+
48
+ new_obj = TensorMemoryObj(
49
+ raw_data=raw_tensor.detach().clone(),
50
+ metadata=copy.deepcopy(obj.metadata),
51
+ parent_allocator=None,
52
+ )
53
+
54
+ return new_obj
55
+
56
+
57
+ # Config class
58
+
59
+
60
+ class MockL2AdapterConfig(L2AdapterConfigBase):
61
+ """
62
+ Config for a mock L2 adapter (for testing).
63
+
64
+ Fields:
65
+ - max_size_gb: maximum size in GB.
66
+ - mock_bandwidth_gb: simulated bandwidth in GB/sec.
67
+ """
68
+
69
+ def __init__(
70
+ self,
71
+ max_size_gb: float,
72
+ mock_bandwidth_gb: float,
73
+ ):
74
+ self.max_size_gb = max_size_gb
75
+ self.mock_bandwidth_gb = mock_bandwidth_gb
76
+
77
+ @classmethod
78
+ def from_dict(cls, d: dict) -> "MockL2AdapterConfig":
79
+ max_size_gb = d.get("max_size_gb")
80
+ if not isinstance(max_size_gb, (int, float)) or max_size_gb <= 0:
81
+ raise ValueError("max_size_gb must be a positive number")
82
+
83
+ mock_bandwidth_gb = d.get("mock_bandwidth_gb")
84
+ if not isinstance(mock_bandwidth_gb, (int, float)) or mock_bandwidth_gb <= 0:
85
+ raise ValueError("mock_bandwidth_gb must be a positive number")
86
+
87
+ return cls(
88
+ max_size_gb=max_size_gb,
89
+ mock_bandwidth_gb=mock_bandwidth_gb,
90
+ )
91
+
92
+ @classmethod
93
+ def help(cls) -> str:
94
+ return (
95
+ "Mock L2 adapter config fields:\n"
96
+ "- max_size_gb (float): maximum size of "
97
+ "the adapter in GB (required, >0)\n"
98
+ "- mock_bandwidth_gb (float): simulated "
99
+ "bandwidth in GB/sec (required, >0)"
100
+ )
101
+
102
+
103
+ # Main class
104
+
105
+
106
+ class MockL2Adapter(L2AdapterInterface):
107
+ """
108
+ A mock-up L2 adapter with a specific RAM size and mocked bandwidth
109
+ """
110
+
111
+ def __init__(self, config: MockL2AdapterConfig):
112
+ super().__init__()
113
+ self._config = config
114
+ self._max_capacity_bytes = int(config.max_size_gb * (1024**3))
115
+ self._bandwidth_byte_ps = int(config.mock_bandwidth_gb * (1024**3))
116
+
117
+ self._store_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
118
+ self._lookup_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
119
+ self._load_efd = os.eventfd(0, os.EFD_NONBLOCK | os.EFD_CLOEXEC)
120
+
121
+ self._memory_objects: dict[ObjectKey, MemoryObj] = {}
122
+ self._locked_keys: dict[ObjectKey, int] = defaultdict(int)
123
+ self._current_size_bytes: int = 0
124
+
125
+ # Task ID management
126
+ self._next_task_id: L2TaskId = 0
127
+ self._completed_store_tasks: dict[L2TaskId, bool] = {}
128
+ self._completed_lookup_tasks: dict[L2TaskId, Bitmap] = {}
129
+ self._completed_load_tasks: dict[L2TaskId, Bitmap] = {}
130
+ self._lock = threading.Lock() # lock for all shared state
131
+
132
+ # Asyncio event loop running in a background thread
133
+ self._loop = asyncio.new_event_loop()
134
+ self._loop_thread = threading.Thread(target=self._run_event_loop, daemon=True)
135
+ self._loop_thread.start()
136
+
137
+ # --------------------
138
+ # Event Fd Interface
139
+ # --------------------
140
+
141
+ def get_store_event_fd(self) -> int:
142
+ return self._store_efd
143
+
144
+ def get_lookup_and_lock_event_fd(self) -> int:
145
+ return self._lookup_efd
146
+
147
+ def get_load_event_fd(self) -> int:
148
+ return self._load_efd
149
+
150
+ # --------------------
151
+ # Store Interface
152
+ # --------------------
153
+
154
+ def submit_store_task(
155
+ self,
156
+ keys: list[ObjectKey],
157
+ objects: list[MemoryObj],
158
+ ) -> L2TaskId:
159
+ """
160
+ Submit a store task to store a batch of memory objects associated with
161
+ a batch of keys.
162
+
163
+ For the mock adapter, the store operation simulates bandwidth-limited
164
+ transfer by delaying completion based on object size and configured bandwidth.
165
+
166
+ Args:
167
+ keys (list[ObjectKey]): the list of keys to be stored.
168
+ objects (list[MemoryObj]): the list of memory objects to be stored.
169
+ The length of the objects list should be the same as the length of
170
+ the keys list.
171
+
172
+ Returns:
173
+ L2TaskId: the task id of the submitted store task.
174
+ """
175
+ with self._lock:
176
+ task_id = self._get_next_task_id()
177
+
178
+ asyncio.run_coroutine_threadsafe(
179
+ self._execute_store_in_the_loop(keys, objects, task_id), self._loop
180
+ )
181
+
182
+ return task_id
183
+
184
+ def pop_completed_store_tasks(self) -> dict[L2TaskId, bool]:
185
+ """
186
+ Pop all the completed store tasks with a flag indicating
187
+ whether the task is successful or not.
188
+
189
+ Returns:
190
+ dict[L2TaskId, bool]: a dictionary mapping the task id to a boolean flag
191
+ indicating whether the task is successful or not. True means
192
+ successful, and False means failed.
193
+ """
194
+ with self._lock:
195
+ completed = self._completed_store_tasks
196
+ self._completed_store_tasks = {}
197
+ return completed
198
+
199
+ def submit_lookup_and_lock_task(self, keys: list[ObjectKey]) -> L2TaskId:
200
+ with self._lock:
201
+ task_id = self._get_next_task_id()
202
+
203
+ # Schedule the lookup operation in the event loop thread
204
+ self._loop.call_soon_threadsafe(self._execute_lookup_in_the_loop, keys, task_id)
205
+ return task_id
206
+
207
+ def query_lookup_and_lock_result(self, task_id: L2TaskId) -> Bitmap | None:
208
+ with self._lock:
209
+ return self._completed_lookup_tasks.pop(task_id, None)
210
+
211
+ def submit_unlock(self, keys: list[ObjectKey]) -> None:
212
+ def _unlock_keys(keys: list[ObjectKey]) -> None:
213
+ """
214
+ Coroutine to unlock keys in the event loop thread.
215
+ This is a helper function to avoid blocking the main thread.
216
+ """
217
+ for key in keys:
218
+ if key not in self._locked_keys:
219
+ continue
220
+ if self._locked_keys[key] <= 1:
221
+ del self._locked_keys[key]
222
+ else:
223
+ self._locked_keys[key] -= 1
224
+
225
+ # Schedule the unlock operation in the event loop thread
226
+ self._loop.call_soon_threadsafe(_unlock_keys, keys)
227
+
228
+ def submit_load_task(
229
+ self,
230
+ keys: list[ObjectKey],
231
+ objects: list[MemoryObj],
232
+ ) -> L2TaskId:
233
+ with self._lock:
234
+ task_id = self._get_next_task_id()
235
+
236
+ # Schedule the load operation in the event loop thread
237
+ asyncio.run_coroutine_threadsafe(
238
+ self._execute_load_in_loop(keys, objects, task_id), self._loop
239
+ )
240
+
241
+ return task_id
242
+
243
+ def query_load_result(self, task_id: L2TaskId) -> Bitmap | None:
244
+ with self._lock:
245
+ return self._completed_load_tasks.pop(task_id, None)
246
+
247
+ def close(self):
248
+ # Stop the event loop and wait for the thread to finish
249
+ async def _stop_tasks():
250
+ tasks = [
251
+ t
252
+ for t in asyncio.all_tasks(self._loop)
253
+ if t is not asyncio.current_task()
254
+ ]
255
+ for task in tasks:
256
+ task.cancel()
257
+ if tasks:
258
+ await asyncio.gather(*tasks, return_exceptions=True)
259
+
260
+ if self._loop.is_running():
261
+ future = asyncio.run_coroutine_threadsafe(_stop_tasks(), self._loop)
262
+ try:
263
+ future.result(timeout=5) # Wait for tasks to be cancelled
264
+ except Exception:
265
+ pass # Ignore exceptions during shutdown
266
+ self._loop.call_soon_threadsafe(self._loop.stop)
267
+
268
+ self._loop_thread.join()
269
+ self._loop.close()
270
+
271
+ os.close(self._store_efd)
272
+ os.close(self._lookup_efd)
273
+ os.close(self._load_efd)
274
+
275
+ ##################
276
+ # Debug / test-only functions
277
+ ##################
278
+
279
+ def report_status(self) -> dict:
280
+ """Return a status dict for the mock L2 adapter."""
281
+ with self._lock:
282
+ return {
283
+ "is_healthy": True,
284
+ "type": "MockL2Adapter",
285
+ "stored_object_count": len(self._memory_objects),
286
+ "locked_key_count": len(self._locked_keys),
287
+ "current_size_bytes": self._current_size_bytes,
288
+ "max_capacity_bytes": self._max_capacity_bytes,
289
+ }
290
+
291
+ def debug_get_stored_object_count(self) -> int:
292
+ """
293
+ Return the number of objects currently stored in the mock adapter.
294
+
295
+ This method is intended for testing and debugging only.
296
+
297
+ Returns:
298
+ int: Number of stored objects.
299
+ """
300
+ with self._lock:
301
+ return len(self._memory_objects)
302
+
303
+ def debug_get_locked_key_count(self) -> int:
304
+ """
305
+ Return the number of currently locked keys.
306
+
307
+ This method is intended for testing and debugging only.
308
+
309
+ Returns:
310
+ int: Number of locked keys.
311
+ """
312
+ with self._lock:
313
+ return len(self._locked_keys)
314
+
315
+ def debug_has_key(self, key: ObjectKey) -> bool:
316
+ """
317
+ Check whether a specific key is stored in the mock adapter.
318
+
319
+ This method is intended for testing and debugging only.
320
+
321
+ Args:
322
+ key: The object key to check.
323
+
324
+ Returns:
325
+ bool: True if the key is stored.
326
+ """
327
+ with self._lock:
328
+ return key in self._memory_objects
329
+
330
+ ##################
331
+ # Helper functions
332
+ ##################
333
+
334
+ def _run_event_loop(self) -> None:
335
+ """Run the asyncio event loop in a background thread."""
336
+ asyncio.set_event_loop(self._loop)
337
+ self._loop.run_forever()
338
+
339
+ def _get_next_task_id(self) -> L2TaskId:
340
+ """Get the next task ID and increment the counter."""
341
+ task_id = self._next_task_id
342
+ self._next_task_id += 1
343
+ return task_id
344
+
345
+ #####################
346
+ # Eviction Interface
347
+ #####################
348
+
349
+ def delete(self, keys: list[ObjectKey]) -> None:
350
+ """Delete a batch of objects from the mock adapter."""
351
+ deleted_keys: list[ObjectKey] = []
352
+ with self._lock:
353
+ for key in keys:
354
+ if key not in self._memory_objects:
355
+ continue
356
+ obj = self._memory_objects.pop(key)
357
+ self._current_size_bytes -= obj.get_size()
358
+ deleted_keys.append(key)
359
+ if deleted_keys:
360
+ self._notify_keys_deleted(deleted_keys)
361
+
362
+ def get_usage(self) -> tuple[float, float]:
363
+ """Return (current_usage, usage_after_ongoing_eviction) in [0, 1]."""
364
+ with self._lock:
365
+ if self._max_capacity_bytes == 0:
366
+ return (0.0, 0.0)
367
+ usage = self._current_size_bytes / self._max_capacity_bytes
368
+ return (usage, usage)
369
+
370
+ def _signal_store_event(self) -> None:
371
+ """Signal the store event fd to notify completion."""
372
+ os.eventfd_write(self._store_efd, 1)
373
+
374
+ async def _execute_store_in_the_loop(
375
+ self,
376
+ keys: list[ObjectKey],
377
+ objects: list[MemoryObj],
378
+ task_id: L2TaskId,
379
+ ) -> None:
380
+ """
381
+ Execute the store operation in the event loop thread.
382
+ This is a helper function to avoid blocking the main thread.
383
+ """
384
+ total_bytes = 0
385
+ success = True
386
+ start = time.perf_counter()
387
+
388
+ stored_keys: list[ObjectKey] = []
389
+ try:
390
+ for key, obj in zip(keys, objects, strict=False):
391
+ obj_size = obj.get_size()
392
+
393
+ # If the object is larger than max capacity, skip it
394
+ if obj_size > self._max_capacity_bytes:
395
+ continue
396
+
397
+ # If key already exists, simply skip
398
+ if key in self._memory_objects:
399
+ continue
400
+
401
+ # Skip if there is not enough capacity
402
+ if self._current_size_bytes + obj_size > self._max_capacity_bytes:
403
+ logger.warning(
404
+ "MockL2Adapter: not enough capacity to store key %s "
405
+ "(used=%d, needed=%d, max=%d); skipping.",
406
+ key,
407
+ self._current_size_bytes,
408
+ obj_size,
409
+ self._max_capacity_bytes,
410
+ )
411
+ continue
412
+
413
+ # Store the object
414
+ new_obj = clone_tensor_memory_obj(obj)
415
+ self._memory_objects[key] = new_obj
416
+ self._current_size_bytes += obj_size
417
+ total_bytes += obj_size
418
+ stored_keys.append(key)
419
+ except Exception:
420
+ success = False
421
+
422
+ # Calculate delay based on bandwidth simulation
423
+ end = time.perf_counter()
424
+ delay_seconds = (
425
+ total_bytes / self._bandwidth_byte_ps if self._bandwidth_byte_ps > 0 else 0
426
+ )
427
+ delay_seconds -= end - start
428
+ delay_seconds = max(delay_seconds, 0) # Ensure non-negative delay
429
+
430
+ # Schedule completion coroutine on the event loop
431
+ await asyncio.sleep(delay_seconds)
432
+ with self._lock:
433
+ self._completed_store_tasks[task_id] = success
434
+
435
+ if stored_keys:
436
+ self._notify_keys_stored(stored_keys)
437
+ self._signal_store_event()
438
+
439
+ def _signal_lookup_event(self) -> None:
440
+ """Signal the lookup event fd to notify completion."""
441
+ os.eventfd_write(self._lookup_efd, 1)
442
+
443
+ def _execute_lookup_in_the_loop(
444
+ self, keys: list[ObjectKey], task_id: L2TaskId
445
+ ) -> None:
446
+ bitmap = Bitmap(len(keys))
447
+ for i, key in enumerate(keys):
448
+ if key not in self._memory_objects:
449
+ continue
450
+ bitmap.set(i)
451
+ self._locked_keys[key] += 1
452
+ with self._lock:
453
+ self._completed_lookup_tasks[task_id] = bitmap
454
+ self._signal_lookup_event()
455
+
456
+ def _signal_load_event(self) -> None:
457
+ """Signal the load event fd to notify completion."""
458
+ os.eventfd_write(self._load_efd, 1)
459
+
460
+ async def _execute_load_in_loop(
461
+ self,
462
+ keys: list[ObjectKey],
463
+ objects: list[MemoryObj],
464
+ task_id: L2TaskId,
465
+ ) -> None:
466
+ """
467
+ Execute the load operation in the event loop thread.
468
+ This is a helper function to avoid blocking the main thread.
469
+ """
470
+ bitmap = Bitmap(len(keys))
471
+ total_bytes = 0
472
+ accessed_keys: list[ObjectKey] = []
473
+ start = time.perf_counter()
474
+
475
+ for i, key in enumerate(keys):
476
+ if key not in self._memory_objects:
477
+ continue
478
+ # load data into the provided memory object
479
+ obj = self._memory_objects[key]
480
+ src_tensor = obj.tensor
481
+ dst_tensor = objects[i].tensor
482
+ assert src_tensor is not None
483
+ assert dst_tensor is not None
484
+ dst_tensor.copy_(src_tensor)
485
+ bitmap.set(i)
486
+ total_bytes += obj.get_size()
487
+ accessed_keys.append(key)
488
+
489
+ end = time.perf_counter()
490
+ delay_seconds = (
491
+ total_bytes / self._bandwidth_byte_ps if self._bandwidth_byte_ps > 0 else 0
492
+ )
493
+ delay_seconds -= end - start
494
+ delay_seconds = max(delay_seconds, 0) # Ensure non-negative delay
495
+
496
+ await asyncio.sleep(delay_seconds)
497
+ if accessed_keys:
498
+ self._notify_keys_accessed(accessed_keys)
499
+ with self._lock:
500
+ self._completed_load_tasks[task_id] = bitmap
501
+ self._signal_load_event()
502
+
503
+
504
+ # Self-register config type and adapter factory
505
+ register_l2_adapter_type("mock", MockL2AdapterConfig)
506
+
507
+
508
+ def _create_mock_adapter(
509
+ config: L2AdapterConfigBase,
510
+ l1_memory_desc: "Optional[L1MemoryDesc]" = None,
511
+ ) -> L2AdapterInterface:
512
+ """Create a MockL2Adapter from config."""
513
+ return MockL2Adapter(config) # type: ignore[arg-type]
514
+
515
+
516
+ register_l2_adapter_factory("mock", _create_mock_adapter)