lmcache-cli 0.4.5.dev0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lmcache/__init__.py +84 -0
- lmcache/_version.py +24 -0
- lmcache/cli/__init__.py +1 -0
- lmcache/cli/commands/__init__.py +34 -0
- lmcache/cli/commands/base.py +157 -0
- lmcache/cli/commands/bench/__init__.py +557 -0
- lmcache/cli/commands/bench/engine_bench/__init__.py +1 -0
- lmcache/cli/commands/bench/engine_bench/config.py +245 -0
- lmcache/cli/commands/bench/engine_bench/interactive/__init__.py +274 -0
- lmcache/cli/commands/bench/engine_bench/interactive/config.json +10 -0
- lmcache/cli/commands/bench/engine_bench/interactive/schema.py +352 -0
- lmcache/cli/commands/bench/engine_bench/interactive/state.py +327 -0
- lmcache/cli/commands/bench/engine_bench/interactive/terminal.py +291 -0
- lmcache/cli/commands/bench/engine_bench/progress.py +145 -0
- lmcache/cli/commands/bench/engine_bench/request_sender.py +232 -0
- lmcache/cli/commands/bench/engine_bench/stats.py +275 -0
- lmcache/cli/commands/bench/engine_bench/workloads/__init__.py +153 -0
- lmcache/cli/commands/bench/engine_bench/workloads/base.py +122 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_permutator.py +435 -0
- lmcache/cli/commands/bench/engine_bench/workloads/long_doc_qa.py +281 -0
- lmcache/cli/commands/bench/engine_bench/workloads/multi_round_chat.py +337 -0
- lmcache/cli/commands/bench/engine_bench/workloads/random_prefill.py +178 -0
- lmcache/cli/commands/describe.py +310 -0
- lmcache/cli/commands/kvcache.py +133 -0
- lmcache/cli/commands/mock.py +75 -0
- lmcache/cli/commands/ping.py +113 -0
- lmcache/cli/commands/query/__init__.py +155 -0
- lmcache/cli/commands/query/prompt.py +134 -0
- lmcache/cli/commands/query/request.py +357 -0
- lmcache/cli/commands/server.py +99 -0
- lmcache/cli/commands/tool/__init__.py +63 -0
- lmcache/cli/commands/tool/cache_simulator.py +113 -0
- lmcache/cli/commands/trace/__init__.py +505 -0
- lmcache/cli/commands/trace/dispatch.py +249 -0
- lmcache/cli/commands/trace/driver.py +372 -0
- lmcache/cli/commands/trace/stats.py +289 -0
- lmcache/cli/documents/lmcache.txt +11 -0
- lmcache/cli/main.py +42 -0
- lmcache/cli/metrics/__init__.py +29 -0
- lmcache/cli/metrics/formatter.py +171 -0
- lmcache/cli/metrics/handler.py +94 -0
- lmcache/cli/metrics/metrics.py +161 -0
- lmcache/cli/metrics/section.py +77 -0
- lmcache/connections.py +173 -0
- lmcache/integration/__init__.py +2 -0
- lmcache/integration/base_service_factory.py +165 -0
- lmcache/integration/request_telemetry/__init__.py +1 -0
- lmcache/integration/request_telemetry/base.py +51 -0
- lmcache/integration/request_telemetry/factory.py +113 -0
- lmcache/integration/request_telemetry/fastapi.py +109 -0
- lmcache/integration/request_telemetry/noop.py +35 -0
- lmcache/integration/sglang/__init__.py +2 -0
- lmcache/integration/sglang/sglang_adapter.py +326 -0
- lmcache/integration/sglang/utils.py +39 -0
- lmcache/integration/vllm/__init__.py +1 -0
- lmcache/integration/vllm/lmcache_connector_v1.py +213 -0
- lmcache/integration/vllm/lmcache_connector_v1_085.py +150 -0
- lmcache/integration/vllm/lmcache_mp_connector_0180.py +1072 -0
- lmcache/integration/vllm/tests/test_mm_hash_utils.py +112 -0
- lmcache/integration/vllm/utils.py +433 -0
- lmcache/integration/vllm/vllm_multi_process_adapter.py +1090 -0
- lmcache/integration/vllm/vllm_service_factory.py +339 -0
- lmcache/integration/vllm/vllm_v1_adapter.py +1713 -0
- lmcache/logging.py +107 -0
- lmcache/native_storage_ops.pyi +230 -0
- lmcache/non_cuda_equivalents.py +1424 -0
- lmcache/observability.py +1958 -0
- lmcache/storage_backend/serde/__init__.py +1 -0
- lmcache/storage_backend/serde/cachegen_basics.py +210 -0
- lmcache/storage_backend/serde/cachegen_decoder.py +207 -0
- lmcache/storage_backend/serde/cachegen_encoder.py +394 -0
- lmcache/storage_backend/serde/serde.py +75 -0
- lmcache/tools/__init__.py +1 -0
- lmcache/tools/cache_simulator/README.md +392 -0
- lmcache/tools/cache_simulator/__init__.py +1 -0
- lmcache/tools/cache_simulator/docs/simulate_example.png +0 -0
- lmcache/tools/cache_simulator/docs/sweep_example.png +0 -0
- lmcache/tools/cache_simulator/gen_bench_dataset.py +360 -0
- lmcache/tools/cache_simulator/lru_cache.py +124 -0
- lmcache/tools/cache_simulator/plot_hit_rate.py +231 -0
- lmcache/tools/cache_simulator/simulator.py +795 -0
- lmcache/tools/controller_benchmark/README.md +161 -0
- lmcache/tools/controller_benchmark/__init__.py +1 -0
- lmcache/tools/controller_benchmark/__main__.py +331 -0
- lmcache/tools/controller_benchmark/benchmark.py +660 -0
- lmcache/tools/controller_benchmark/config.py +44 -0
- lmcache/tools/controller_benchmark/constants.py +10 -0
- lmcache/tools/controller_benchmark/handlers/__init__.py +46 -0
- lmcache/tools/controller_benchmark/handlers/admit.py +52 -0
- lmcache/tools/controller_benchmark/handlers/base.py +47 -0
- lmcache/tools/controller_benchmark/handlers/deregister.py +49 -0
- lmcache/tools/controller_benchmark/handlers/evict.py +52 -0
- lmcache/tools/controller_benchmark/handlers/heartbeat.py +56 -0
- lmcache/tools/controller_benchmark/handlers/p2p_lookup.py +47 -0
- lmcache/tools/controller_benchmark/handlers/register.py +56 -0
- lmcache/tools/mp_status_viewer/__init__.py +1 -0
- lmcache/tools/mp_status_viewer/__main__.py +95 -0
- lmcache/usage_context.py +417 -0
- lmcache/utils.py +665 -0
- lmcache/v1/__init__.py +2 -0
- lmcache/v1/api_server/__init__.py +2 -0
- lmcache/v1/api_server/__main__.py +537 -0
- lmcache/v1/basic_check.py +112 -0
- lmcache/v1/cache_controller/__init__.py +9 -0
- lmcache/v1/cache_controller/commands/__init__.py +15 -0
- lmcache/v1/cache_controller/commands/base.py +35 -0
- lmcache/v1/cache_controller/commands/full_sync.py +49 -0
- lmcache/v1/cache_controller/config.py +176 -0
- lmcache/v1/cache_controller/controller_manager.py +535 -0
- lmcache/v1/cache_controller/controllers/__init__.py +11 -0
- lmcache/v1/cache_controller/controllers/full_sync_tracker.py +473 -0
- lmcache/v1/cache_controller/controllers/kv_controller.py +439 -0
- lmcache/v1/cache_controller/controllers/registration_controller.py +282 -0
- lmcache/v1/cache_controller/executor.py +463 -0
- lmcache/v1/cache_controller/frontend/static/css/style.css +201 -0
- lmcache/v1/cache_controller/frontend/static/img/logo.png +0 -0
- lmcache/v1/cache_controller/frontend/static/index.html +234 -0
- lmcache/v1/cache_controller/frontend/static/js/controller_app.js +660 -0
- lmcache/v1/cache_controller/full_sync_sender.py +475 -0
- lmcache/v1/cache_controller/locks.py +149 -0
- lmcache/v1/cache_controller/message.py +828 -0
- lmcache/v1/cache_controller/observability.py +208 -0
- lmcache/v1/cache_controller/utils.py +679 -0
- lmcache/v1/cache_controller/worker.py +665 -0
- lmcache/v1/cache_engine.py +2058 -0
- lmcache/v1/cache_interface.py +19 -0
- lmcache/v1/check/__init__.py +74 -0
- lmcache/v1/check/check_mode_gen.py +86 -0
- lmcache/v1/check/check_mode_test_l2_adapter.py +284 -0
- lmcache/v1/check/check_mode_test_remote.py +155 -0
- lmcache/v1/check/check_mode_test_storage_manager.py +142 -0
- lmcache/v1/check/utils.py +571 -0
- lmcache/v1/compute/__init__.py +2 -0
- lmcache/v1/compute/attention/__init__.py +0 -0
- lmcache/v1/compute/attention/abstract.py +39 -0
- lmcache/v1/compute/attention/flash_attn.py +129 -0
- lmcache/v1/compute/attention/flash_infer_sparse.py +284 -0
- lmcache/v1/compute/attention/metadata.py +85 -0
- lmcache/v1/compute/attention/utils.py +14 -0
- lmcache/v1/compute/blend/__init__.py +7 -0
- lmcache/v1/compute/blend/blender.py +168 -0
- lmcache/v1/compute/blend/metadata.py +34 -0
- lmcache/v1/compute/blend/utils.py +63 -0
- lmcache/v1/compute/models/__init__.py +0 -0
- lmcache/v1/compute/models/base.py +141 -0
- lmcache/v1/compute/models/llama.py +9 -0
- lmcache/v1/compute/models/qwen3.py +24 -0
- lmcache/v1/compute/models/utils.py +68 -0
- lmcache/v1/compute/positional_encoding.py +199 -0
- lmcache/v1/config.py +848 -0
- lmcache/v1/config_base.py +848 -0
- lmcache/v1/distributed/api.py +248 -0
- lmcache/v1/distributed/config.py +321 -0
- lmcache/v1/distributed/error.py +64 -0
- lmcache/v1/distributed/eviction.py +192 -0
- lmcache/v1/distributed/eviction_policy/__init__.py +21 -0
- lmcache/v1/distributed/eviction_policy/factory.py +27 -0
- lmcache/v1/distributed/eviction_policy/lru.py +244 -0
- lmcache/v1/distributed/eviction_policy/noop.py +50 -0
- lmcache/v1/distributed/internal_api.py +170 -0
- lmcache/v1/distributed/l1_manager.py +835 -0
- lmcache/v1/distributed/l2_adapters/__init__.py +67 -0
- lmcache/v1/distributed/l2_adapters/base.py +360 -0
- lmcache/v1/distributed/l2_adapters/config.py +385 -0
- lmcache/v1/distributed/l2_adapters/factory.py +205 -0
- lmcache/v1/distributed/l2_adapters/fs_l2_adapter.py +747 -0
- lmcache/v1/distributed/l2_adapters/fs_native_l2_adapter.py +167 -0
- lmcache/v1/distributed/l2_adapters/mock_l2_adapter.py +516 -0
- lmcache/v1/distributed/l2_adapters/mooncake_store_l2_adapter.py +135 -0
- lmcache/v1/distributed/l2_adapters/native_connector_l2_adapter.py +468 -0
- lmcache/v1/distributed/l2_adapters/native_plugin_l2_adapter.py +199 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_dynamic_l2_adapter.py +831 -0
- lmcache/v1/distributed/l2_adapters/nixl_store_l2_adapter.py +983 -0
- lmcache/v1/distributed/l2_adapters/plugin_l2_adapter.py +210 -0
- lmcache/v1/distributed/l2_adapters/resp_l2_adapter.py +176 -0
- lmcache/v1/distributed/memory_manager.py +179 -0
- lmcache/v1/distributed/storage_controller.py +39 -0
- lmcache/v1/distributed/storage_controllers/__init__.py +43 -0
- lmcache/v1/distributed/storage_controllers/eviction_controller.py +242 -0
- lmcache/v1/distributed/storage_controllers/prefetch_controller.py +830 -0
- lmcache/v1/distributed/storage_controllers/prefetch_policy.py +193 -0
- lmcache/v1/distributed/storage_controllers/store_controller.py +452 -0
- lmcache/v1/distributed/storage_controllers/store_policy.py +213 -0
- lmcache/v1/distributed/storage_manager.py +532 -0
- lmcache/v1/event_manager.py +145 -0
- lmcache/v1/exceptions/__init__.py +16 -0
- lmcache/v1/gpu_connector/__init__.py +126 -0
- lmcache/v1/gpu_connector/gpu_connectors.py +1906 -0
- lmcache/v1/gpu_connector/gpu_ops.py +85 -0
- lmcache/v1/gpu_connector/hpu_connector.py +326 -0
- lmcache/v1/gpu_connector/mock_gpu_connector.py +67 -0
- lmcache/v1/gpu_connector/utils.py +890 -0
- lmcache/v1/gpu_connector/xpu_connectors.py +916 -0
- lmcache/v1/health_monitor/__init__.py +1 -0
- lmcache/v1/health_monitor/base.py +587 -0
- lmcache/v1/health_monitor/checks/__init__.py +1 -0
- lmcache/v1/health_monitor/checks/remote_backend_check.py +304 -0
- lmcache/v1/health_monitor/constants.py +36 -0
- lmcache/v1/internal_api_server/__init__.py +0 -0
- lmcache/v1/internal_api_server/api_registry.py +59 -0
- lmcache/v1/internal_api_server/api_server.py +120 -0
- lmcache/v1/internal_api_server/common/__init__.py +1 -0
- lmcache/v1/internal_api_server/common/env_api.py +22 -0
- lmcache/v1/internal_api_server/common/loglevel_api.py +57 -0
- lmcache/v1/internal_api_server/common/metrics_api.py +29 -0
- lmcache/v1/internal_api_server/common/periodic_thread_api.py +138 -0
- lmcache/v1/internal_api_server/common/run_script_api.py +73 -0
- lmcache/v1/internal_api_server/common/thread_api.py +63 -0
- lmcache/v1/internal_api_server/controller/__init__.py +1 -0
- lmcache/v1/internal_api_server/controller/key_stats_api.py +81 -0
- lmcache/v1/internal_api_server/controller/worker_info_api.py +136 -0
- lmcache/v1/internal_api_server/utils.py +43 -0
- lmcache/v1/internal_api_server/vllm/__init__.py +1 -0
- lmcache/v1/internal_api_server/vllm/backend_api.py +221 -0
- lmcache/v1/internal_api_server/vllm/bypass_api.py +204 -0
- lmcache/v1/internal_api_server/vllm/cache_api.py +895 -0
- lmcache/v1/internal_api_server/vllm/chunk_statistics_api.py +141 -0
- lmcache/v1/internal_api_server/vllm/conf_api.py +147 -0
- lmcache/v1/internal_api_server/vllm/freeze_api.py +172 -0
- lmcache/v1/internal_api_server/vllm/hot_cache_api.py +184 -0
- lmcache/v1/internal_api_server/vllm/inference_api.py +65 -0
- lmcache/v1/internal_api_server/vllm/load_fs_chunks_api.py +320 -0
- lmcache/v1/internal_api_server/vllm/lookup_api.py +145 -0
- lmcache/v1/internal_api_server/vllm/version_api.py +25 -0
- lmcache/v1/kv_layer_groups.py +267 -0
- lmcache/v1/lazy_memory_allocator.py +284 -0
- lmcache/v1/lookup_client/__init__.py +25 -0
- lmcache/v1/lookup_client/abstract_client.py +77 -0
- lmcache/v1/lookup_client/async_lookup_message.py +50 -0
- lmcache/v1/lookup_client/chunk_statistics_lookup_client.py +200 -0
- lmcache/v1/lookup_client/factory.py +251 -0
- lmcache/v1/lookup_client/hit_limit_lookup_client.py +86 -0
- lmcache/v1/lookup_client/lmcache_async_lookup_client.py +407 -0
- lmcache/v1/lookup_client/lmcache_lookup_client.py +285 -0
- lmcache/v1/lookup_client/lmcache_lookup_client_bypass.py +99 -0
- lmcache/v1/lookup_client/mooncake_lookup_client.py +87 -0
- lmcache/v1/lookup_client/record_strategies/__init__.py +77 -0
- lmcache/v1/lookup_client/record_strategies/base.py +327 -0
- lmcache/v1/lookup_client/record_strategies/file_hash.py +130 -0
- lmcache/v1/lookup_client/record_strategies/memory_bloom_filter.py +81 -0
- lmcache/v1/manager.py +539 -0
- lmcache/v1/memory_management.py +2619 -0
- lmcache/v1/metadata.py +114 -0
- lmcache/v1/mp_observability/AGENTS.override.md +21 -0
- lmcache/v1/mp_observability/README.md +204 -0
- lmcache/v1/mp_observability/config.py +340 -0
- lmcache/v1/mp_observability/event.py +100 -0
- lmcache/v1/mp_observability/event_bus.py +313 -0
- lmcache/v1/mp_observability/otel_init.py +145 -0
- lmcache/v1/mp_observability/subscribers/__init__.py +28 -0
- lmcache/v1/mp_observability/subscribers/logging/__init__.py +19 -0
- lmcache/v1/mp_observability/subscribers/logging/l1.py +56 -0
- lmcache/v1/mp_observability/subscribers/logging/l2.py +73 -0
- lmcache/v1/mp_observability/subscribers/logging/lookup_hash.py +209 -0
- lmcache/v1/mp_observability/subscribers/logging/mp_server.py +90 -0
- lmcache/v1/mp_observability/subscribers/logging/sm.py +59 -0
- lmcache/v1/mp_observability/subscribers/metrics/__init__.py +20 -0
- lmcache/v1/mp_observability/subscribers/metrics/l0_lifecycle.py +290 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1.py +55 -0
- lmcache/v1/mp_observability/subscribers/metrics/l1_lifecycle.py +166 -0
- lmcache/v1/mp_observability/subscribers/metrics/l2.py +121 -0
- lmcache/v1/mp_observability/subscribers/metrics/sm.py +69 -0
- lmcache/v1/mp_observability/subscribers/tracing/__init__.py +12 -0
- lmcache/v1/mp_observability/subscribers/tracing/mp_server.py +333 -0
- lmcache/v1/mp_observability/subscribers/tracing/span_registry.py +148 -0
- lmcache/v1/mp_observability/trace/__init__.py +50 -0
- lmcache/v1/mp_observability/trace/codecs.py +255 -0
- lmcache/v1/mp_observability/trace/decorator.py +147 -0
- lmcache/v1/mp_observability/trace/format.py +132 -0
- lmcache/v1/mp_observability/trace/lifecycle.py +83 -0
- lmcache/v1/mp_observability/trace/reader.py +167 -0
- lmcache/v1/mp_observability/trace/recorder.py +300 -0
- lmcache/v1/multiprocess/__init__.py +0 -0
- lmcache/v1/multiprocess/affinity_pool.py +102 -0
- lmcache/v1/multiprocess/blend_server_v2.py +891 -0
- lmcache/v1/multiprocess/config.py +253 -0
- lmcache/v1/multiprocess/custom_types.py +281 -0
- lmcache/v1/multiprocess/futures.py +194 -0
- lmcache/v1/multiprocess/gpu_context.py +511 -0
- lmcache/v1/multiprocess/http_server.py +235 -0
- lmcache/v1/multiprocess/mp_runtime_plugin_launcher.py +130 -0
- lmcache/v1/multiprocess/mq.py +732 -0
- lmcache/v1/multiprocess/protocol.py +86 -0
- lmcache/v1/multiprocess/protocols/README.md +213 -0
- lmcache/v1/multiprocess/protocols/__init__.py +127 -0
- lmcache/v1/multiprocess/protocols/base.py +89 -0
- lmcache/v1/multiprocess/protocols/blend.py +109 -0
- lmcache/v1/multiprocess/protocols/blend_v2.py +57 -0
- lmcache/v1/multiprocess/protocols/controller.py +53 -0
- lmcache/v1/multiprocess/protocols/debug.py +34 -0
- lmcache/v1/multiprocess/protocols/engine.py +146 -0
- lmcache/v1/multiprocess/protocols/observability.py +39 -0
- lmcache/v1/multiprocess/server.py +1134 -0
- lmcache/v1/multiprocess/session.py +190 -0
- lmcache/v1/multiprocess/token_hasher.py +441 -0
- lmcache/v1/offload_server/__init__.py +17 -0
- lmcache/v1/offload_server/abstract_server.py +37 -0
- lmcache/v1/offload_server/message.py +30 -0
- lmcache/v1/offload_server/zmq_server.py +122 -0
- lmcache/v1/periodic_thread.py +579 -0
- lmcache/v1/pin_monitor.py +246 -0
- lmcache/v1/plugin/__init__.py +0 -0
- lmcache/v1/plugin/runtime_plugin_launcher.py +211 -0
- lmcache/v1/protocol.py +317 -0
- lmcache/v1/rpc/__init__.py +17 -0
- lmcache/v1/rpc/transport.py +105 -0
- lmcache/v1/rpc/zmq_transport.py +213 -0
- lmcache/v1/rpc_utils.py +165 -0
- lmcache/v1/server/__init__.py +2 -0
- lmcache/v1/server/__main__.py +170 -0
- lmcache/v1/server/storage_backend/__init__.py +21 -0
- lmcache/v1/server/storage_backend/abstract_backend.py +80 -0
- lmcache/v1/server/storage_backend/local_backend.py +75 -0
- lmcache/v1/server/utils.py +21 -0
- lmcache/v1/standalone/__init__.py +1 -0
- lmcache/v1/standalone/__main__.py +583 -0
- lmcache/v1/standalone/manager.py +80 -0
- lmcache/v1/standalone/standalone_service_factory.py +86 -0
- lmcache/v1/storage_backend/__init__.py +313 -0
- lmcache/v1/storage_backend/abstract_backend.py +445 -0
- lmcache/v1/storage_backend/audit_backend.py +233 -0
- lmcache/v1/storage_backend/batched_message_sender.py +222 -0
- lmcache/v1/storage_backend/cache_policy/__init__.py +45 -0
- lmcache/v1/storage_backend/cache_policy/base_policy.py +87 -0
- lmcache/v1/storage_backend/cache_policy/fifo.py +58 -0
- lmcache/v1/storage_backend/cache_policy/lfu.py +105 -0
- lmcache/v1/storage_backend/cache_policy/lru.py +81 -0
- lmcache/v1/storage_backend/cache_policy/mru.py +61 -0
- lmcache/v1/storage_backend/connector/__init__.py +443 -0
- lmcache/v1/storage_backend/connector/audit_adapter.py +77 -0
- lmcache/v1/storage_backend/connector/audit_connector.py +320 -0
- lmcache/v1/storage_backend/connector/base_connector.py +379 -0
- lmcache/v1/storage_backend/connector/blackhole_adapter.py +21 -0
- lmcache/v1/storage_backend/connector/blackhole_connector.py +37 -0
- lmcache/v1/storage_backend/connector/eic_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/eic_connector.py +757 -0
- lmcache/v1/storage_backend/connector/external_adapter.py +79 -0
- lmcache/v1/storage_backend/connector/fs_adapter.py +51 -0
- lmcache/v1/storage_backend/connector/fs_connector.py +403 -0
- lmcache/v1/storage_backend/connector/infinistore_adapter.py +56 -0
- lmcache/v1/storage_backend/connector/infinistore_connector.py +177 -0
- lmcache/v1/storage_backend/connector/instrumented_connector.py +219 -0
- lmcache/v1/storage_backend/connector/lm_adapter.py +31 -0
- lmcache/v1/storage_backend/connector/lm_connector.py +176 -0
- lmcache/v1/storage_backend/connector/mock_adapter.py +57 -0
- lmcache/v1/storage_backend/connector/mock_connector.py +349 -0
- lmcache/v1/storage_backend/connector/mooncakestore_adapter.py +43 -0
- lmcache/v1/storage_backend/connector/mooncakestore_connector.py +614 -0
- lmcache/v1/storage_backend/connector/redis_adapter.py +181 -0
- lmcache/v1/storage_backend/connector/redis_connector.py +828 -0
- lmcache/v1/storage_backend/connector/s3_adapter.py +59 -0
- lmcache/v1/storage_backend/connector/s3_connector.py +699 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_adapter.py +233 -0
- lmcache/v1/storage_backend/connector/sagemaker_hyperpod_connector.py +987 -0
- lmcache/v1/storage_backend/connector/valkey_adapter.py +114 -0
- lmcache/v1/storage_backend/connector/valkey_connector.py +627 -0
- lmcache/v1/storage_backend/gds_backend.py +1199 -0
- lmcache/v1/storage_backend/job_executor/__init__.py +0 -0
- lmcache/v1/storage_backend/job_executor/base_executor.py +34 -0
- lmcache/v1/storage_backend/job_executor/pq_executor.py +235 -0
- lmcache/v1/storage_backend/local_cpu_backend.py +810 -0
- lmcache/v1/storage_backend/local_disk_backend.py +656 -0
- lmcache/v1/storage_backend/maru_backend.py +734 -0
- lmcache/v1/storage_backend/naive_serde/__init__.py +50 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_basics.py +133 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_decoder.py +135 -0
- lmcache/v1/storage_backend/naive_serde/cachegen_encoder.py +83 -0
- lmcache/v1/storage_backend/naive_serde/kivi_serde.py +22 -0
- lmcache/v1/storage_backend/naive_serde/naive_serde.py +18 -0
- lmcache/v1/storage_backend/naive_serde/serde.py +37 -0
- lmcache/v1/storage_backend/native_clients/connector_client_base.py +165 -0
- lmcache/v1/storage_backend/native_clients/resp_client.py +35 -0
- lmcache/v1/storage_backend/nixl_storage_backend.py +1400 -0
- lmcache/v1/storage_backend/p2p_backend.py +788 -0
- lmcache/v1/storage_backend/path_sharder.py +117 -0
- lmcache/v1/storage_backend/pd_backend.py +646 -0
- lmcache/v1/storage_backend/plugins/dax_backend.py +1443 -0
- lmcache/v1/storage_backend/plugins/rust_raw_block_backend.py +1361 -0
- lmcache/v1/storage_backend/remote_backend.py +624 -0
- lmcache/v1/storage_backend/resp_client.py +227 -0
- lmcache/v1/storage_backend/storage_backend_listener.py +19 -0
- lmcache/v1/storage_backend/storage_manager.py +1352 -0
- lmcache/v1/system_detection.py +110 -0
- lmcache/v1/token_database.py +551 -0
- lmcache/v1/transfer_channel/__init__.py +83 -0
- lmcache/v1/transfer_channel/abstract.py +285 -0
- lmcache/v1/transfer_channel/mock_memory_channel.py +156 -0
- lmcache/v1/transfer_channel/nixl_channel.py +639 -0
- lmcache/v1/transfer_channel/py_socket_channel.py +260 -0
- lmcache/v1/transfer_channel/transfer_utils.py +63 -0
- lmcache/v1/utils/__init__.py +1 -0
- lmcache/v1/utils/bloom_filter.py +109 -0
- lmcache/v1/utils/cache_utils.py +125 -0
- lmcache_cli-0.4.5.dev0.dist-info/METADATA +185 -0
- lmcache_cli-0.4.5.dev0.dist-info/RECORD +399 -0
- lmcache_cli-0.4.5.dev0.dist-info/WHEEL +5 -0
- lmcache_cli-0.4.5.dev0.dist-info/entry_points.txt +2 -0
- lmcache_cli-0.4.5.dev0.dist-info/licenses/LICENSE +201 -0
- lmcache_cli-0.4.5.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# First Party
|
|
3
|
+
from lmcache.logging import init_logger
|
|
4
|
+
from lmcache.v1.storage_backend.connector import (
|
|
5
|
+
ConnectorAdapter,
|
|
6
|
+
ConnectorContext,
|
|
7
|
+
parse_remote_url,
|
|
8
|
+
)
|
|
9
|
+
from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
|
|
10
|
+
|
|
11
|
+
logger = init_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ExternalConnectorAdapter(ConnectorAdapter):
|
|
15
|
+
"""Adapter for External connectors."""
|
|
16
|
+
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
super().__init__("external://")
|
|
19
|
+
|
|
20
|
+
def create_connector(self, context: ConnectorContext) -> RemoteConnector:
|
|
21
|
+
"""
|
|
22
|
+
Create an External connector. This connector stores data
|
|
23
|
+
in the key-value store.
|
|
24
|
+
URL format:
|
|
25
|
+
- external://host:port/module_path/?connector_name=ConnectorName
|
|
26
|
+
Examples:
|
|
27
|
+
- external://host:0/external_log_connector.lmc_external_log_connector/?connector_name=ExternalLogConnector
|
|
28
|
+
"""
|
|
29
|
+
logger.info(f"Creating External connector for URL: {context.url}")
|
|
30
|
+
logger.warning(
|
|
31
|
+
"External connector is due for deprecation in release v0.5.0. "
|
|
32
|
+
"Please use the Remote Storage Plugin Framework instead."
|
|
33
|
+
)
|
|
34
|
+
hosts = context.url.split(",")
|
|
35
|
+
if len(hosts) > 1:
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Only one host is supported for external connector, but got {hosts}"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
parse_url = parse_remote_url(context.url)
|
|
41
|
+
|
|
42
|
+
# Get the module path and connector name
|
|
43
|
+
module_path = parse_url.path.strip("/")
|
|
44
|
+
connector_name = parse_url.query_params.get("connector_name", [""])[0]
|
|
45
|
+
if not connector_name:
|
|
46
|
+
raise ValueError(
|
|
47
|
+
"External connector requires 'connector_name' in query parameters"
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Lazily import the module and get the connector class
|
|
51
|
+
# Standard
|
|
52
|
+
import importlib
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
module = importlib.import_module(module_path)
|
|
56
|
+
connector_class = getattr(module, connector_name)
|
|
57
|
+
|
|
58
|
+
# Verify that it's a subclass of RemoteConnector
|
|
59
|
+
if not issubclass(connector_class, RemoteConnector):
|
|
60
|
+
raise TypeError(
|
|
61
|
+
f"{connector_name} must be a subclass of RemoteConnector"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Create the connector instance
|
|
65
|
+
connector = connector_class(
|
|
66
|
+
loop=context.loop,
|
|
67
|
+
local_cpu_backend=context.local_cpu_backend,
|
|
68
|
+
config=context.config,
|
|
69
|
+
)
|
|
70
|
+
logger.info(f"Loaded external connector: {module_path}.{connector_name}")
|
|
71
|
+
return connector
|
|
72
|
+
except ImportError as e:
|
|
73
|
+
raise ImportError(
|
|
74
|
+
f"Could not import module '{module_path}', error: {e}"
|
|
75
|
+
) from e
|
|
76
|
+
except AttributeError as e:
|
|
77
|
+
raise AttributeError(
|
|
78
|
+
f"Module '{module_path}' has no class '{connector_name}', error: {e}"
|
|
79
|
+
) from e
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# First Party
|
|
3
|
+
from lmcache.logging import init_logger
|
|
4
|
+
from lmcache.v1.storage_backend.connector import (
|
|
5
|
+
ConnectorAdapter,
|
|
6
|
+
ConnectorContext,
|
|
7
|
+
extract_plugin_type,
|
|
8
|
+
parse_remote_url,
|
|
9
|
+
)
|
|
10
|
+
from lmcache.v1.storage_backend.connector.base_connector import (
|
|
11
|
+
RemoteConnector,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
logger = init_logger(__name__)
|
|
15
|
+
|
|
16
|
+
PLUGIN_TYPE = "fs"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class FsConnectorAdapter(ConnectorAdapter):
|
|
20
|
+
"""Adapter for Filesystem connectors."""
|
|
21
|
+
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
super().__init__("fs://")
|
|
24
|
+
|
|
25
|
+
def can_parse(self, url: str) -> bool:
|
|
26
|
+
if url.startswith(self.schema):
|
|
27
|
+
return True
|
|
28
|
+
if url.startswith("plugin://"):
|
|
29
|
+
pname = url[len("plugin://") :]
|
|
30
|
+
return extract_plugin_type(pname) == PLUGIN_TYPE
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
def create_connector(self, context: ConnectorContext) -> RemoteConnector:
|
|
34
|
+
# Local
|
|
35
|
+
from .fs_connector import FSConnector
|
|
36
|
+
|
|
37
|
+
logger.info("Creating FS connector")
|
|
38
|
+
|
|
39
|
+
# Legacy URL mode: extract base_path from URL
|
|
40
|
+
base_paths_str = None
|
|
41
|
+
if context.plugin_name is None:
|
|
42
|
+
parsed = parse_remote_url(context.url)
|
|
43
|
+
base_paths_str = parsed.path
|
|
44
|
+
|
|
45
|
+
return FSConnector(
|
|
46
|
+
context.loop,
|
|
47
|
+
context.local_cpu_backend,
|
|
48
|
+
context.config,
|
|
49
|
+
plugin_name=context.plugin_name,
|
|
50
|
+
base_paths_str=base_paths_str,
|
|
51
|
+
)
|
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Standard
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Optional, Tuple, no_type_check
|
|
5
|
+
import asyncio
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
# Third Party
|
|
9
|
+
import aiofiles
|
|
10
|
+
import aiofiles.os
|
|
11
|
+
|
|
12
|
+
# First Party
|
|
13
|
+
from lmcache.logging import init_logger
|
|
14
|
+
from lmcache.utils import CacheEngineKey
|
|
15
|
+
from lmcache.v1.config import LMCacheEngineConfig
|
|
16
|
+
from lmcache.v1.memory_management import MemoryObj
|
|
17
|
+
from lmcache.v1.protocol import RemoteMetadata
|
|
18
|
+
from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
|
|
19
|
+
from lmcache.v1.storage_backend.local_cpu_backend import LocalCPUBackend
|
|
20
|
+
|
|
21
|
+
logger = init_logger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class FSConnector(RemoteConnector):
|
|
25
|
+
"""File system based connector that stores data in local files.
|
|
26
|
+
|
|
27
|
+
Data is stored in the following format:
|
|
28
|
+
- Each key is stored as a separate file
|
|
29
|
+
- File content: metadata (remote_metadata_bytes) + serialized data
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
loop: asyncio.AbstractEventLoop,
|
|
35
|
+
local_cpu_backend: LocalCPUBackend,
|
|
36
|
+
config: Optional[LMCacheEngineConfig],
|
|
37
|
+
plugin_name: Optional[str] = None,
|
|
38
|
+
base_paths_str: Optional[str] = None,
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Args:
|
|
42
|
+
loop: Asyncio event loop
|
|
43
|
+
local_cpu_backend: Memory allocator interface
|
|
44
|
+
config: Lmcache engine config
|
|
45
|
+
plugin_name: Plugin instance name
|
|
46
|
+
(e.g. "fs", "fs.primary")
|
|
47
|
+
base_paths_str: Comma-separated base paths
|
|
48
|
+
(legacy, passed from adapter when using
|
|
49
|
+
fs:// URL)
|
|
50
|
+
"""
|
|
51
|
+
# initialize base class
|
|
52
|
+
super().__init__(
|
|
53
|
+
local_cpu_backend.config,
|
|
54
|
+
local_cpu_backend.metadata,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
base_path = base_paths_str
|
|
58
|
+
if base_path is None:
|
|
59
|
+
# Resolve from extra_config
|
|
60
|
+
extra_config = config.extra_config if config else None
|
|
61
|
+
if extra_config is not None:
|
|
62
|
+
key_prefix = plugin_name or "fs"
|
|
63
|
+
base_path = extra_config.get(
|
|
64
|
+
"remote_storage_plugin.%s.base_path" % key_prefix
|
|
65
|
+
)
|
|
66
|
+
if base_path is None:
|
|
67
|
+
if extra_config is not None:
|
|
68
|
+
base_path = extra_config.get("fs_base_path")
|
|
69
|
+
if base_path is None:
|
|
70
|
+
raise ValueError(
|
|
71
|
+
"FS connector requires base_path via URL or extra_config"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Parse comma separated paths
|
|
75
|
+
self.base_paths = (
|
|
76
|
+
[Path(p.strip()) for p in base_path.split(",")]
|
|
77
|
+
if "," in base_path
|
|
78
|
+
else [Path(base_path)]
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
self.loop = loop
|
|
82
|
+
self.local_cpu_backend = local_cpu_backend
|
|
83
|
+
|
|
84
|
+
relative_tmp_dir = (
|
|
85
|
+
None
|
|
86
|
+
if config is None
|
|
87
|
+
else config.get_extra_config_value("fs_connector_relative_tmp_dir", None)
|
|
88
|
+
)
|
|
89
|
+
self.relative_tmp_dir = None
|
|
90
|
+
if relative_tmp_dir is not None:
|
|
91
|
+
self.relative_tmp_dir = Path(relative_tmp_dir)
|
|
92
|
+
assert not self.relative_tmp_dir.is_absolute()
|
|
93
|
+
|
|
94
|
+
self.read_ahead_size = (
|
|
95
|
+
None
|
|
96
|
+
if config is None
|
|
97
|
+
else config.get_extra_config_value("fs_connector_read_ahead_size", None)
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
self.use_odirect = (
|
|
101
|
+
False
|
|
102
|
+
if config is None
|
|
103
|
+
else config.get_extra_config_value("fs_connector_use_odirect", False)
|
|
104
|
+
)
|
|
105
|
+
self.os_disk_bs = 0
|
|
106
|
+
if self.use_odirect:
|
|
107
|
+
# save_chunk_meta is useful if save_unfull_chunk is True, since partial
|
|
108
|
+
# chunk will be saved. When loading partial chunk, we need to know
|
|
109
|
+
# data size and shape. However, chunk meta is short (28 bytes) which
|
|
110
|
+
# is not aligned to disk block size (512 or 4096).
|
|
111
|
+
# Therefore, we disable O_DIRECT if save_chunk_meta is True.
|
|
112
|
+
# TODO: support O_DIRECT for save_chunk_meta by
|
|
113
|
+
# padding meta data to 4096.
|
|
114
|
+
if self.save_chunk_meta:
|
|
115
|
+
logger.warning("Cannot use O_DIRECT if save_chunk_meta enabled.")
|
|
116
|
+
self.use_odirect = False
|
|
117
|
+
else:
|
|
118
|
+
stat = os.statvfs(self.base_paths[0])
|
|
119
|
+
self.os_disk_bs = stat.f_bsize
|
|
120
|
+
|
|
121
|
+
logger.info(
|
|
122
|
+
f"Initialized FSConnector with base paths {self.base_paths}, "
|
|
123
|
+
f"relative tmp dir: {self.relative_tmp_dir}, "
|
|
124
|
+
f"read ahead size: {self.read_ahead_size}, "
|
|
125
|
+
f"use O_DIRECT: {self.use_odirect}"
|
|
126
|
+
)
|
|
127
|
+
# Create directories for all paths
|
|
128
|
+
for path in self.base_paths:
|
|
129
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
130
|
+
if self.relative_tmp_dir is not None:
|
|
131
|
+
(path / self.relative_tmp_dir).mkdir(parents=False, exist_ok=True)
|
|
132
|
+
|
|
133
|
+
def _get_base_path(self, key: CacheEngineKey) -> Path:
|
|
134
|
+
"""Get file base path for the given key"""
|
|
135
|
+
if len(self.base_paths) == 1:
|
|
136
|
+
base_path = self.base_paths[0]
|
|
137
|
+
else:
|
|
138
|
+
# Calculate hash value and modulo to select path
|
|
139
|
+
hash_val = abs(key.chunk_hash)
|
|
140
|
+
idx = hash_val % len(self.base_paths)
|
|
141
|
+
base_path = self.base_paths[idx]
|
|
142
|
+
|
|
143
|
+
return base_path
|
|
144
|
+
|
|
145
|
+
def _get_file_name(self, key: CacheEngineKey) -> str:
|
|
146
|
+
return key.to_string().replace("/", "-SEP-") + ".data"
|
|
147
|
+
|
|
148
|
+
def _get_file_path(self, key: CacheEngineKey) -> Path:
|
|
149
|
+
"""Get file path for the given key"""
|
|
150
|
+
base_path = self._get_base_path(key)
|
|
151
|
+
file_name = self._get_file_name(key)
|
|
152
|
+
return base_path / file_name
|
|
153
|
+
|
|
154
|
+
def _get_file_and_tmp_path(self, key: CacheEngineKey) -> Tuple[Path, Path]:
|
|
155
|
+
"""Get file and tmp path for the given key"""
|
|
156
|
+
base_path = self._get_base_path(key)
|
|
157
|
+
file_name = self._get_file_name(key)
|
|
158
|
+
file_path = base_path / file_name
|
|
159
|
+
if self.relative_tmp_dir is not None:
|
|
160
|
+
tmp_path = base_path / self.relative_tmp_dir / file_name
|
|
161
|
+
else:
|
|
162
|
+
tmp_path = file_path.with_suffix(".tmp")
|
|
163
|
+
return file_path, tmp_path
|
|
164
|
+
|
|
165
|
+
async def exists(self, key: CacheEngineKey) -> bool:
|
|
166
|
+
"""Check if key exists in file system"""
|
|
167
|
+
file_path = self._get_file_path(key)
|
|
168
|
+
return await aiofiles.os.path.exists(file_path)
|
|
169
|
+
|
|
170
|
+
def exists_sync(self, key: CacheEngineKey) -> bool:
|
|
171
|
+
"""Check if key exists in file system synchronized"""
|
|
172
|
+
file_path = self._get_file_path(key)
|
|
173
|
+
return os.path.exists(file_path)
|
|
174
|
+
|
|
175
|
+
def _get_with_odirect(self, file_path: Path) -> Optional[MemoryObj]:
|
|
176
|
+
"""Synchronous direct IO read, executed in a thread."""
|
|
177
|
+
fd = -1
|
|
178
|
+
memory_obj: Optional[MemoryObj] = None
|
|
179
|
+
try:
|
|
180
|
+
memory_obj = self.local_cpu_backend.allocate(
|
|
181
|
+
self.meta_shapes, self.meta_dtypes, self.meta_fmt
|
|
182
|
+
)
|
|
183
|
+
if memory_obj is None:
|
|
184
|
+
logger.debug("Memory allocation failed.")
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
buffer = memory_obj.byte_array
|
|
188
|
+
size = len(buffer)
|
|
189
|
+
|
|
190
|
+
fblock_aligned = (
|
|
191
|
+
self.os_disk_bs is not None
|
|
192
|
+
and self.os_disk_bs > 0
|
|
193
|
+
and size % self.os_disk_bs == 0
|
|
194
|
+
)
|
|
195
|
+
if not fblock_aligned:
|
|
196
|
+
logger.warning(
|
|
197
|
+
f"Cannot use O_DIRECT for {file_path}, size is not aligned."
|
|
198
|
+
)
|
|
199
|
+
with open(file_path, "rb") as f:
|
|
200
|
+
num_read = f.readinto(buffer)
|
|
201
|
+
else:
|
|
202
|
+
fd = os.open(file_path, os.O_RDONLY | getattr(os, "O_DIRECT", 0))
|
|
203
|
+
with os.fdopen(fd, "rb", buffering=0) as fdo:
|
|
204
|
+
# The fd is now managed by the file object, so we "forget" it
|
|
205
|
+
# to prevent closing it in the finally block.
|
|
206
|
+
fd = -1
|
|
207
|
+
num_read = fdo.readinto(buffer)
|
|
208
|
+
|
|
209
|
+
memory_obj = self.reshape_partial_chunk(memory_obj, num_read)
|
|
210
|
+
return memory_obj
|
|
211
|
+
|
|
212
|
+
except Exception as e:
|
|
213
|
+
logger.error(f"Failed to read from file {file_path}: {str(e)}")
|
|
214
|
+
if memory_obj is not None:
|
|
215
|
+
memory_obj.ref_count_down()
|
|
216
|
+
return None
|
|
217
|
+
finally:
|
|
218
|
+
if fd >= 0:
|
|
219
|
+
try:
|
|
220
|
+
os.close(fd)
|
|
221
|
+
except OSError:
|
|
222
|
+
pass
|
|
223
|
+
|
|
224
|
+
async def get(self, key: CacheEngineKey) -> Optional[MemoryObj]:
|
|
225
|
+
"""Get data from file system"""
|
|
226
|
+
file_path = self._get_file_path(key)
|
|
227
|
+
|
|
228
|
+
if self.use_odirect and not self.save_chunk_meta:
|
|
229
|
+
return await self.loop.run_in_executor(
|
|
230
|
+
None, self._get_with_odirect, file_path
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
memory_obj = None
|
|
234
|
+
try:
|
|
235
|
+
async with aiofiles.open(file_path, "rb") as f:
|
|
236
|
+
if self.save_chunk_meta:
|
|
237
|
+
# Read metadata buffer first to get shape, dtype, fmt
|
|
238
|
+
# to be able to allocate memory object for the data and read into it
|
|
239
|
+
md_buffer = bytearray(self.remote_metadata_bytes)
|
|
240
|
+
num_read = await f.readinto(md_buffer)
|
|
241
|
+
if num_read != len(md_buffer):
|
|
242
|
+
raise RuntimeError(
|
|
243
|
+
f"Partial read meta {len(md_buffer)} got {num_read}"
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
# Deserialize metadata and allocate memory
|
|
247
|
+
metadata = RemoteMetadata.deserialize(md_buffer)
|
|
248
|
+
memory_obj = self.local_cpu_backend.allocate(
|
|
249
|
+
metadata.shapes, metadata.dtypes, metadata.fmt
|
|
250
|
+
)
|
|
251
|
+
else:
|
|
252
|
+
memory_obj = self.local_cpu_backend.allocate(
|
|
253
|
+
self.meta_shapes, self.meta_dtypes, self.meta_fmt
|
|
254
|
+
)
|
|
255
|
+
if memory_obj is None:
|
|
256
|
+
logger.debug("Memory allocation failed during async disk load.")
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
# Read the actual data into allocated memory
|
|
260
|
+
buffer = memory_obj.byte_array
|
|
261
|
+
if self.save_chunk_meta:
|
|
262
|
+
# if save chunk meta, read meta will trigger
|
|
263
|
+
# read ahead if fs supported
|
|
264
|
+
num_read = await f.readinto(buffer)
|
|
265
|
+
if num_read != len(buffer):
|
|
266
|
+
raise RuntimeError(
|
|
267
|
+
f"Partial read data {len(buffer)} got {num_read}"
|
|
268
|
+
)
|
|
269
|
+
else:
|
|
270
|
+
if self.read_ahead_size is None:
|
|
271
|
+
num_read = await f.readinto(buffer)
|
|
272
|
+
else:
|
|
273
|
+
if not isinstance(buffer, memoryview):
|
|
274
|
+
buffer = memoryview(buffer)
|
|
275
|
+
|
|
276
|
+
# trigger read head if fs supported
|
|
277
|
+
num_read_ahead = await f.readinto(
|
|
278
|
+
buffer[: self.read_ahead_size]
|
|
279
|
+
)
|
|
280
|
+
assert num_read_ahead <= self.read_ahead_size
|
|
281
|
+
|
|
282
|
+
# if num_read_ahead == self.read_ahead_size,
|
|
283
|
+
# means there may still be some remaining content
|
|
284
|
+
if num_read_ahead == self.read_ahead_size:
|
|
285
|
+
num_read_tail = await f.readinto(
|
|
286
|
+
buffer[self.read_ahead_size :]
|
|
287
|
+
)
|
|
288
|
+
assert num_read_tail is not None
|
|
289
|
+
num_read = num_read_ahead + num_read_tail
|
|
290
|
+
else:
|
|
291
|
+
num_read = num_read_ahead
|
|
292
|
+
# reshape and check
|
|
293
|
+
assert num_read is not None
|
|
294
|
+
memory_obj = self.reshape_partial_chunk(memory_obj, num_read)
|
|
295
|
+
|
|
296
|
+
return memory_obj
|
|
297
|
+
|
|
298
|
+
except Exception as e:
|
|
299
|
+
if not isinstance(e, FileNotFoundError):
|
|
300
|
+
logger.error(f"Failed to read from file {file_path}: {str(e)}")
|
|
301
|
+
if memory_obj is not None:
|
|
302
|
+
memory_obj.ref_count_down()
|
|
303
|
+
return None
|
|
304
|
+
|
|
305
|
+
def _put_with_odirect(self, file_path: Path, buffer: bytes) -> None:
|
|
306
|
+
fd = -1
|
|
307
|
+
try:
|
|
308
|
+
fd = os.open(
|
|
309
|
+
str(file_path),
|
|
310
|
+
os.O_CREAT | os.O_WRONLY | getattr(os, "O_DIRECT", 0),
|
|
311
|
+
0o644,
|
|
312
|
+
)
|
|
313
|
+
os.write(fd, buffer)
|
|
314
|
+
except Exception as e:
|
|
315
|
+
logger.error(f"Failed to write to file {file_path}: {e}")
|
|
316
|
+
raise
|
|
317
|
+
finally:
|
|
318
|
+
if fd >= 0:
|
|
319
|
+
try:
|
|
320
|
+
os.close(fd)
|
|
321
|
+
except OSError:
|
|
322
|
+
pass
|
|
323
|
+
|
|
324
|
+
async def put(self, key: CacheEngineKey, memory_obj: MemoryObj):
|
|
325
|
+
"""Store data to file system"""
|
|
326
|
+
final_path, temp_path = self._get_file_and_tmp_path(key)
|
|
327
|
+
|
|
328
|
+
try:
|
|
329
|
+
# Prepare metadata
|
|
330
|
+
buffer = memory_obj.byte_array
|
|
331
|
+
metadata = (
|
|
332
|
+
RemoteMetadata(
|
|
333
|
+
len(buffer),
|
|
334
|
+
memory_obj.get_shapes(),
|
|
335
|
+
memory_obj.get_dtypes(),
|
|
336
|
+
memory_obj.get_memory_format(),
|
|
337
|
+
)
|
|
338
|
+
if self.save_chunk_meta
|
|
339
|
+
else None
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
size = len(buffer)
|
|
343
|
+
do_use_odirect = self.use_odirect
|
|
344
|
+
if do_use_odirect:
|
|
345
|
+
fblock_aligned = self.os_disk_bs > 0 and size % self.os_disk_bs == 0
|
|
346
|
+
if not fblock_aligned:
|
|
347
|
+
logger.warning(
|
|
348
|
+
f"Cannot use O_DIRECT for writing size {size}, "
|
|
349
|
+
f"which is not aligned to block size {self.os_disk_bs}."
|
|
350
|
+
)
|
|
351
|
+
do_use_odirect = False
|
|
352
|
+
|
|
353
|
+
if do_use_odirect:
|
|
354
|
+
# Use Direct I/O
|
|
355
|
+
await self.loop.run_in_executor(
|
|
356
|
+
None, self._put_with_odirect, temp_path, buffer
|
|
357
|
+
)
|
|
358
|
+
else:
|
|
359
|
+
# Use standard async I/O
|
|
360
|
+
# Write to file (metadata + data)
|
|
361
|
+
async with aiofiles.open(temp_path, "wb") as f:
|
|
362
|
+
if metadata is not None:
|
|
363
|
+
await f.write(metadata.serialize())
|
|
364
|
+
await f.write(buffer)
|
|
365
|
+
|
|
366
|
+
# Atomically rename temp file to final destination
|
|
367
|
+
await aiofiles.os.replace(temp_path, final_path)
|
|
368
|
+
|
|
369
|
+
except Exception as e:
|
|
370
|
+
logger.error(f"Failed to write file {final_path}: {str(e)}")
|
|
371
|
+
if await aiofiles.os.path.exists(temp_path):
|
|
372
|
+
await aiofiles.os.unlink(temp_path) # Remove corrupted file
|
|
373
|
+
raise
|
|
374
|
+
|
|
375
|
+
def remove_sync(self, key: CacheEngineKey) -> bool:
|
|
376
|
+
"""
|
|
377
|
+
Remove the file associated with the given key.
|
|
378
|
+
|
|
379
|
+
Args:
|
|
380
|
+
key: The key to remove.
|
|
381
|
+
|
|
382
|
+
Returns:
|
|
383
|
+
bool: True if the file was successfully removed, False otherwise.
|
|
384
|
+
"""
|
|
385
|
+
file_path = self._get_file_path(key)
|
|
386
|
+
try:
|
|
387
|
+
os.remove(file_path)
|
|
388
|
+
return True
|
|
389
|
+
except OSError as e:
|
|
390
|
+
logger.error(f"Failed to remove file {file_path}: {e}")
|
|
391
|
+
return False
|
|
392
|
+
|
|
393
|
+
@no_type_check
|
|
394
|
+
async def list(self) -> List[str]:
|
|
395
|
+
"""List all keys in file system"""
|
|
396
|
+
keys = []
|
|
397
|
+
for base_path in self.base_paths:
|
|
398
|
+
keys.extend([f.stem for f in base_path.glob("*.data")])
|
|
399
|
+
return keys
|
|
400
|
+
|
|
401
|
+
async def close(self):
|
|
402
|
+
"""Clean up resources"""
|
|
403
|
+
logger.info("Closed the file system connector")
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# First Party
|
|
3
|
+
from lmcache.logging import init_logger
|
|
4
|
+
from lmcache.v1.storage_backend.connector import (
|
|
5
|
+
ConnectorAdapter,
|
|
6
|
+
ConnectorContext,
|
|
7
|
+
parse_remote_url,
|
|
8
|
+
)
|
|
9
|
+
from lmcache.v1.storage_backend.connector.base_connector import RemoteConnector
|
|
10
|
+
|
|
11
|
+
logger = init_logger(__name__)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InfinistoreConnectorAdapter(ConnectorAdapter):
|
|
15
|
+
"""Adapter for Infinistore connectors."""
|
|
16
|
+
|
|
17
|
+
def __init__(self) -> None:
|
|
18
|
+
super().__init__("infinistore://")
|
|
19
|
+
|
|
20
|
+
def create_connector(self, context: ConnectorContext) -> RemoteConnector:
|
|
21
|
+
# Third Party
|
|
22
|
+
import infinistore
|
|
23
|
+
|
|
24
|
+
# Local
|
|
25
|
+
from .infinistore_connector import InfinistoreConnector
|
|
26
|
+
|
|
27
|
+
logger.info(f"Creating Infinistore connector for URL: {context.url}")
|
|
28
|
+
hosts = context.url.split(",")
|
|
29
|
+
if len(hosts) > 1:
|
|
30
|
+
raise ValueError(
|
|
31
|
+
f"Only one host is supported for infinistore, but got {hosts}"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
parse_url = parse_remote_url(context.url)
|
|
35
|
+
device_name = parse_url.query_params.get("device", ["mlx5_0"])[0]
|
|
36
|
+
|
|
37
|
+
link_type_str = "LINK_ETHERNET"
|
|
38
|
+
if context.config and context.config.extra_config:
|
|
39
|
+
link_type_str = context.config.extra_config.get(
|
|
40
|
+
"infinistore_link_type", link_type_str
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
link_type_str = link_type_str.upper()
|
|
44
|
+
try:
|
|
45
|
+
link_type = getattr(infinistore, link_type_str)
|
|
46
|
+
except AttributeError as e:
|
|
47
|
+
raise ValueError(f"Invalid link_type: {link_type_str}") from e
|
|
48
|
+
|
|
49
|
+
return InfinistoreConnector(
|
|
50
|
+
host=parse_url.host,
|
|
51
|
+
port=parse_url.port,
|
|
52
|
+
dev_name=device_name,
|
|
53
|
+
link_type=link_type,
|
|
54
|
+
loop=context.loop,
|
|
55
|
+
memory_allocator=context.local_cpu_backend,
|
|
56
|
+
)
|