Transfer Engine Rust API#
This page documents the Rust bindings living under mooncake-transfer-engine/rust.
At the time of writing, the crate (transfer_engine_rust) is primarily used as a Rust-side binding + example binary. The public Rust types are implemented in src/transfer_engine.rs and wrap the Transfer Engine C API (transfer_engine_c.h).
For Transfer Engine design docs and non-Rust APIs, see:
Transfer Engine design docs:
design/transfer-engine/indexTransfer Engine C++ API:
design/transfer-engine/cpp-api
Build & runtime prerequisites#
The Rust crate uses bindgen + CMake to link against the Transfer Engine C/C++ build outputs.
Build:
Requires Rust toolchain and libclang (bindgen).
The crate has
build.rsthat expects to find / build the native library via CMake.
Runtime:
Dynamic linker must find Transfer Engine shared libraries.
You need a metadata server backend (commonly etcd) and a reachable peer segment registry.
Mental model#
The Transfer Engine operates on segments and transfer batches:
You create a
TransferEnginebound to:metadata_uri(for example, etcd endpoint)local_server_name(this node’s address/name)rpc_port(RPC listener port)
You register local memory regions as RDMA-capable buffers.
You open a remote segment to obtain a
segment_id(an integer handle).You allocate a batch id for a fixed number of transfer requests.
You submit a batch of
TransferRequest.You poll status per task id inside the batch, then free the batch id.
API reference#
Enums#
OpcodeEnum#
OpcodeEnum::ReadOpcodeEnum::Write
Used by TransferRequest.opcode.
TransferStatusEnum#
Status values returned by the C layer. Common values you will check for:
CompletedFailedTimeout
Structs#
TransferRequest#
One transfer operation inside a batch.
Fields:
opcode: OpcodeEnumsource: *mut c_void: local source/destination pointer (depends on opcode).target_id: i32: segment id returned byopen_segment().target_offset: u64: byte offset inside the target segment.length: u64: transfer length in bytes.
BufferEntry#
Used for batch memory registration:
addr: *mut c_voidlength: u64
TransferEngine#
new(metadata_uri, local_server_name, rpc_port) -> anyhow::Result<TransferEngine>#
Create a new engine handle.
Notes:
metadata_uriandlocal_server_nameare passed throughCString; interior\0bytes will error.The wrapper currently disables
auto_discoverin the underlying C call.
discover_topology() -> anyhow::Result<()>#
Trigger topology discovery.
install_transport(proto) -> anyhow::Result<()>#
Install a transport by name (e.g. "tcp", "rdma", "efa" depending on build/runtime support).
register_local_memory(addr, length, location) -> anyhow::Result<()>#
Register a local memory region for zero-copy transfers.
addr: pointer to the memory region.length: size in bytes.location: location string such as"cpu:0".
unregister_local_memory(addr) -> anyhow::Result<()>#
Unregister a previously registered memory region.
register_local_memory_batch(buffer_list, location) -> anyhow::Result<()>#
Batch register multiple local buffers.
No-op when
buffer_listis empty.
unregister_local_memory_batch(buffer_list) -> anyhow::Result<()>#
Batch unregister multiple local buffers.
open_segment(name: String) -> anyhow::Result<i32>#
Open a remote segment by name and get a segment id.
close_segment(segment_id: i32) -> anyhow::Result<()>#
Close a previously opened segment.
warmup_efa_segment(name: &str) -> anyhow::Result<()>#
Eagerly establish EFA endpoints so the first submit_transfer() does not pay the serial connection setup cost.
No-op on non-EFA transports.
Call after
open_segment()and after the metadata server has published the peer’s NIC list.
sync_segment_cache() -> anyhow::Result<()>#
Synchronize segment cache from metadata.
allocate_batch_id(batch_size) -> anyhow::Result<u64>#
Allocate a batch id for batch_size transfer requests.
You must call free_batch_id(batch_id) after all tasks are done.
submit_transfer(batch_id, requests) -> anyhow::Result<()>#
Submit a batch transfer request list.
No-op when
requestsis empty.The wrapper converts each
TransferRequestinto the C representation (transfer_request_t).
get_transfer_status(batch_id, task_id) -> anyhow::Result<(i32, u64)>#
Get status for one task in a batch.
task_idis an index inside the batch, typically0..batch_size.Returns
(status_code, transferred_bytes).
The status_code maps to values in TransferStatusEnum (represented as i32).
free_batch_id(batch_id) -> anyhow::Result<()>#
Free a previously allocated batch id.
Minimal usage example (pseudo-code)#
The crate’s src/main.rs contains a full benchmark-style example. The following sketch shows the typical control flow:
use std::ffi::c_void;
use transfer_engine_rust::transfer_engine::{OpcodeEnum, TransferEngine, TransferRequest};
fn main() -> anyhow::Result<()> {
let engine = TransferEngine::new("127.0.0.1:2379", "127.0.0.1", 12345)?;
engine.discover_topology()?;
engine.install_transport("tcp")?;
// Register local memory (example only; you must allocate and pin memory appropriately).
let mut buffer = vec![0u8; 4096];
engine.register_local_memory(buffer.as_mut_ptr() as *mut c_void, buffer.len(), "cpu:0")?;
let seg_id = engine.open_segment("target-seg".to_string())?;
let batch_id = engine.allocate_batch_id(1)?;
let mut reqs = [TransferRequest {
opcode: OpcodeEnum::Write,
source: buffer.as_mut_ptr() as *mut c_void,
target_id: seg_id,
target_offset: 0,
length: buffer.len() as u64,
}];
engine.submit_transfer(batch_id, &mut reqs)?;
let (status, bytes) = engine.get_transfer_status(batch_id, 0)?;
println!("status={status}, bytes={bytes}");
engine.free_batch_id(batch_id)?;
engine.close_segment(seg_id)?;
engine.unregister_local_memory(buffer.as_mut_ptr() as *mut c_void)?;
Ok(())
}
Safety & thread-safety#
The wrapper marks
TransferEngineasSend + Sync, but it owns an FFI handle (transfer_engine_t).All pointer-based arguments must satisfy Rust’s aliasing and lifetime rules.
You must ensure registered memory remains valid until it is unregistered.