"""Minimal D→P snapshot link over Mooncake RDMA. This module provides a thin wrapper around mooncake.engine.TransferEngine for one-sided RDMA writes of KV bytes from a Decode worker (sender) to a Prefill worker (receiver). It deliberately does NOT use the heavyweight MooncakeKVManager pipeline (which is tied to PREFILL/DECODE roles and chunked transfer protocols): we want a simple, testable byte transport that can be reused by SGLang and by stand-alone smoke tests. Layout: SnapshotPeer — engine + pre-registered receive buffer (receiver) or sender handle (sender) SnapshotEndpoint — what the receiver advertises so the sender can target it: (session_id, base_ptr, length) SnapshotPusher — sender-side: holds a target endpoint, calls batch_transfer_sync_write All transfers are SYNCHRONOUS, single-shot, in-memory. Higher layers add: control plane (how D learns P's endpoint), per-session slot allocation, KV format/layout, hand-off into SGLang scheduler. """ from __future__ import annotations import ctypes import logging import os import threading from dataclasses import dataclass from typing import Optional logger = logging.getLogger(__name__) @dataclass(frozen=True) class SnapshotEndpoint: """What the receiver advertises so the sender can reach it. Attributes ---------- session_id : str ``"host:rpc_port"`` string identifying the receiver's mooncake TransferEngine. Returned by ``TransferEngine.get_rpc_port()`` joined with the host the engine was initialized with. base_ptr : int Address of the registered receive buffer on the receiver side. capacity_bytes : int Length of the registered region. """ session_id: str base_ptr: int capacity_bytes: int def _import_transfer_engine(): try: from mooncake.engine import TransferEngine except ImportError as e: # pragma: no cover raise ImportError( "mooncake.engine.TransferEngine is required for snapshot_link. " "Make sure mooncake-transfer-engine is installed in the venv." ) from e return TransferEngine class SnapshotPeer: """One Mooncake transfer engine endpoint with a registered receive buffer. The engine is dedicated to snapshot traffic — it does NOT share state with SGLang's MooncakeKVManager engine. Each SnapshotPeer needs its own host:port to listen on. """ def __init__( self, host: str, port: int, ib_device: Optional[str] = None, receive_capacity_bytes: int = 0, protocol: Optional[str] = None, ): TransferEngine = _import_transfer_engine() self.host = host self.port = port self.ib_device = ib_device self.engine = TransferEngine() listen = f"{host}:{port}" proto = protocol or os.environ.get("MOONCAKE_PROTOCOL", "rdma") ret = self.engine.initialize( listen, "P2PHANDSHAKE", proto, ib_device or "", ) if ret != 0: raise RuntimeError( f"snapshot_link: engine.initialize({listen!r}, proto={proto}, " f"ib={ib_device}) returned {ret}" ) self._rpc_port = self.engine.get_rpc_port() self._session_id = f"{host}:{self._rpc_port}" self._recv_buffer = None self._recv_ptr = 0 self._recv_capacity = 0 if receive_capacity_bytes > 0: self._allocate_recv_buffer(receive_capacity_bytes) self._lock = threading.Lock() logger.info( "SnapshotPeer up at %s (rpc=%d, ib=%s, recv=%d B)", self._session_id, self._rpc_port, ib_device, receive_capacity_bytes, ) # -- accessors --------------------------------------------------------- @property def session_id(self) -> str: return self._session_id @property def rpc_port(self) -> int: return self._rpc_port @property def endpoint(self) -> SnapshotEndpoint: if self._recv_buffer is None: raise RuntimeError( "SnapshotPeer has no receive buffer; pass receive_capacity_bytes > 0" ) return SnapshotEndpoint( session_id=self._session_id, base_ptr=self._recv_ptr, capacity_bytes=self._recv_capacity, ) # -- buffer management ------------------------------------------------- def _allocate_recv_buffer(self, length: int) -> None: """Allocate + register a pinned host buffer for receiving.""" # Use c_ubyte (unsigned) so bytes() conversions of the underlying # storage always yield valid byte values. buf = (ctypes.c_ubyte * length)() addr = ctypes.addressof(buf) ret = self.engine.register_memory(addr, length) if ret != 0: raise RuntimeError( f"snapshot_link: register_memory({hex(addr)}, {length}) returned {ret}" ) self._recv_buffer = buf self._recv_ptr = addr self._recv_capacity = length def read_bytes(self, offset: int, length: int) -> bytes: """Snapshot the recv buffer at [offset, offset+length) (caller syncs).""" if self._recv_buffer is None: raise RuntimeError("no recv buffer") if offset < 0 or offset + length > self._recv_capacity: raise ValueError( f"read_bytes({offset}, {length}) out of capacity {self._recv_capacity}" ) # string_at copies via memcpy and yields a proper bytes object — works # regardless of signed/unsigned underlying storage. return ctypes.string_at(self._recv_ptr + offset, length) def register_send_buffer(self, ptr: int, length: int) -> None: """Register an externally-allocated send buffer for outbound RDMA writes.""" with self._lock: ret = self.engine.register_memory(ptr, length) if ret != 0: raise RuntimeError( f"snapshot_link: register send buffer({hex(ptr)}, {length}) returned {ret}" ) def deregister(self, ptr: int) -> None: with self._lock: try: self.engine.unregister_memory(ptr) except Exception: pass # -- transfer ---------------------------------------------------------- def push( self, target: SnapshotEndpoint, local_ptr: int, local_offset: int, length: int, remote_offset: int = 0, ) -> int: """Synchronously RDMA-write ``length`` bytes from ``local_ptr+local_offset`` to ``target.base_ptr+remote_offset`` on the peer identified by ``target.session_id``. Returns 0 on success, non-zero (or raises) on failure. """ if length <= 0: return 0 if remote_offset < 0 or remote_offset + length > target.capacity_bytes: raise ValueError( f"push: remote_offset={remote_offset}, length={length} exceeds " f"target capacity {target.capacity_bytes}" ) src = local_ptr + local_offset dst = target.base_ptr + remote_offset try: ret = self.engine.transfer_sync_write( target.session_id, src, dst, length ) except Exception as e: logger.exception("snapshot_link.push transfer_sync_write threw: %s", e) return -1 if ret != 0: logger.warning( "snapshot_link.push transfer_sync_write returned %d (src=%s, " "dst=%s/%s, len=%d)", ret, hex(src), target.session_id, hex(dst), length, ) return ret def batch_push( self, target: SnapshotEndpoint, local_addrs: list[int], remote_addrs: list[int], lengths: list[int], ) -> int: """Batched RDMA write (one-shot).""" if not local_addrs: return 0 try: ret = self.engine.batch_transfer_sync_write( target.session_id, local_addrs, remote_addrs, lengths ) except Exception as e: logger.exception("snapshot_link.batch_push threw: %s", e) return -1 return ret def close(self) -> None: """Best-effort shutdown — release the receive buffer registration.""" if self._recv_ptr: try: self.engine.unregister_memory(self._recv_ptr) except Exception: pass self._recv_ptr = 0 self._recv_capacity = 0 self._recv_buffer = None def make_session_id(host: str, rpc_port: int) -> str: """Build the ``host:port`` form used as mooncake's session id.""" return f"{host}:{rpc_port}"