Repository navigation
Conversation
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
c9947fc to
bd0ebe8
Compare
| // Await a TE-poll Future and always resume off the poll worker. | ||
| // Prefer the Lazy's CurrentExecutor; if none is bound (e.g. syncAwait without | ||
| // via), fall back to coro_io's global executor so copy/commit/RPC never | ||
| // continue on te_poll threads. | ||
| template <typename V> | ||
| inline async_simple::coro::Lazy<tl::expected<V, ErrorCode>> | ||
| AwaitTeExpectedFuture(async_simple::Future<tl::expected<V, ErrorCode>> fut) { | ||
| async_simple::Executor* ex = co_await async_simple::CurrentExecutor{}; | ||
| if (ex == nullptr) { | ||
| ex = coro_io::get_global_executor(); | ||
| LOG(WARNING) << "AwaitTeExpectedFuture: no CurrentExecutor; " | ||
| "resuming via coro_io::get_global_executor()"; | ||
| } | ||
| try { | ||
| co_return co_await std::move(fut).via(ex); | ||
| } catch (const std::exception& e) { | ||
| LOG(ERROR) << "TE wait future exception: " << e.what(); | ||
| co_return tl::unexpected(ErrorCode::INTERNAL_ERROR); | ||
| } catch (...) { | ||
| LOG(ERROR) << "TE wait future unknown exception"; | ||
| co_return tl::unexpected(ErrorCode::INTERNAL_ERROR); | ||
| } | ||
| } |
There was a problem hiding this comment.
-
If this is intended to be a general‑purpose coroutine component, the naming and logging should not contain TE‑specific information, and it should be placed in
task_handle.h. -
If
data_managerremoveste_poll_executor_andWaitAllTransferBatchesAsyncuses an independent executor for coroutine logic, wouldAwaitTeExpectedFuturestill be necessary?
There was a problem hiding this comment.
Yes — still needed (now AwaitExpectedFuture). Wait runs on te_wait_pool_, so the Future completes there. Without hop-back, copy/commit/UnPin would resume on that pool. We don’t run wait on the caller’s executor: many callers are the RPC pool, and TE polling would crowd PreWrite/PinKey/Heartbeat off those threads. Hop-back is the cost of keeping wait off RPC.
…o coroutine te_poll
…ng pin and holding the transfer Future in std::optional
…e P2P via from RPC Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
bd0ebe8 to
6ec9319
Compare
4db1c02
into
kvcache-ai:P2P-Mooncake-Store
Description
Offload TransferEngine wait (
WaitAllTransferBatches/ poll + sleep) from coroutine/RPC workers onto a dedicatedte_pollthread pool, so multi-key P2P coroutines can yield during TE completion instead of occupying business threads.This is a store-side bridge, not true TE async completion callbacks. Submit stays on the caller; only wait is offloaded when
te_async_poll_worker_num > 0(default32;0keeps the previous sync-wait behavior).What this adds
DataManager TE wait bridge
te_poll_executor_(AsyncMemcpyExecutor) sized byte_async_poll_worker_numWaitAllTransferBatchesAsync: when the pool is enabled, runs onlyWaitAllTransferBatcheson poll workers and returns aFuture; whente_async_poll_worker_num == 0, wait stays synchronous on the caller (legacy behavior)TransferDataAsync: validate + submit on the caller, then returns that waitFuture(submit is not offloaded)Forward (accessor TE)
PreWrite/Pin→ TE →WriteCommit/UnPin)co_await TransferDataAsync/TeTransferFnFuture instead of blocking the coroutine workerAwaitTeExpectedFutureresumes onCurrentExecutor, orcoro_io::get_global_executor()if none is bound—so Commit/Revoke/UnPin/metrics do not run onte_pollthreadssetExceptionso the outer write Promise always completesLocal TE Put/Get
BuildDataCopierViaTe: offloads wait viaFutureHandle, keepingTeSubmitResult(handle / temp buffer) alive until wait finishesPutViaTe: PreWrite + submit on caller; only wait goes to the pool; DRAM staging copy +WriteCommit/ revoke run after wait on the caller / resume executor (PutViaTeTaskHandle), not inside the poll taskReverse (owner TE)
ClientRpcService::{Read,Write}RemoteDatabecomeLazyhandlersReadRemoteDataAsync/WriteRemoteDataAsync: submit on the RPC path,co_awaitwait offload, then copy/commit (write) or return (read)DataManager::{Read,Write}RemoteDataremain assyncAwaitwrappers for existing unit testsConfig / wiring
te_async_poll_worker_numonP2PClientConfig/LocalTransferConfig, exposed via client config builder (incl. dict),real_client_maingflag, and PythonMooncakeStorector32; set0to disable the poll poolInflightTracker change (needed for reverse transfer)
Reverse Lazy handlers hold
peer_tracker_Guard acrossco_await. The old Guard kept astd::shared_lock, which must unlock on the same OS thread that locked—unsafe if the coroutine resumes on ate_poll(or other) thread.Change: drain by atomic
inflight_+ CV instead of r/w lock.Close()rejects newEnter()Wait()blocks untilinflight_ == 0(does not by itself reject new enters—alwaysClose()thenWait())peer_tracker_andlocal_inflight_tracker_share this implementation.Module
mooncake-transfer-engine)mooncake-store)mooncake-ep)mooncake-pg)mooncake-integration)mooncake-p2p-store)mooncake-wheel)mooncake-common)mooncake-rl)Type of Change
How Has This Been Tested?
Unit
inflight_tracker_test— admit/close/drain; cross-thread Guard destroy;Close+Waitdata_manager_test— poll executor create/disable;TransferDataAsync/WaitAllTransferBatchesAsync; FutureHandle; shutdown cancel surfaces as exceptionclient_config_builder_test—te_async_poll_worker_numdefault / args / dictclient_rpc_service_test— LazyRead/WriteRemoteDataviasyncAwaitIntegration
p2p_client_integration_testTeAsyncPollForwardRemoteBatchPutAndGet(te_async={0,4})TeAsyncPollReverseRemoteBatchPutAndGet(te_async={0,4})Test commands:
Test results:
Checklist
./scripts/code_format.shpre-commit run --all-filesand all hooks passAI Assistance Disclosure
Cursor agent assisted implementation and PR text; author reviewed and owns the change.