pub mod consumer;
#[allow(dead_code)]
pub(crate) mod descriptor;
pub mod handle;
pub mod handlers;
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) mod pinned;
pub mod protocol;
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub mod rdma;
pub mod store;
pub mod transparent;
pub mod write;
pub use handle::DataHandle;
pub use protocol::DataMetadata;
pub use store::{RegisterOptions, StageMode};
pub use transparent::{RendezvousResolver, RendezvousStager};
pub use write::RendezvousWrite;
use std::sync::{Arc, OnceLock};
use std::time::Instant;
use crate::observability::{HandlerOutcome, RendezvousOp, VeloMetrics};
use anyhow::Result;
use bytes::Bytes;
use velo_ext::WorkerId;
pub struct RendezvousManager {
worker_id: WorkerId,
store: Arc<store::DataStore>,
messenger_lock: OnceLock<Arc<crate::messenger::Messenger>>,
metrics: Option<Arc<VeloMetrics>>,
#[cfg(all(target_os = "linux", feature = "ucx"))]
reaper_shutdown: tokio_util::sync::CancellationToken,
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
test_hook: parking_lot::Mutex<Option<RdmaTestHook>>,
}
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum RdmaTestHook {
UnknownBackend,
TruncateDescriptor,
TrailingByte,
LyingKeyLength,
FailGet,
SlowGet(std::time::Duration),
}
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
impl RdmaTestHook {
fn is_descriptor_fault(&self) -> bool {
matches!(
self,
Self::UnknownBackend
| Self::TruncateDescriptor
| Self::TrailingByte
| Self::LyingKeyLength
)
}
fn corrupt(&self, mut descriptor: Vec<u8>) -> Vec<u8> {
match self {
Self::UnknownBackend => {
if let Some(byte) = descriptor.first_mut() {
*byte = 0xEE;
}
}
Self::TruncateDescriptor => {
descriptor.pop();
}
Self::TrailingByte => descriptor.push(0),
Self::LyingKeyLength => {
let at = descriptor::HEADER_LEN - 2;
if descriptor.len() >= descriptor::HEADER_LEN {
descriptor[at..at + 2].copy_from_slice(&u16::MAX.to_le_bytes());
}
}
Self::FailGet | Self::SlowGet(_) => {}
}
descriptor
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) struct RdmaContext {
pub(crate) registry: Arc<rdma::RdmaRegistry>,
pub(crate) config: rdma::RdmaRendezvousConfig,
pub(crate) backend: descriptor::DescriptorBackend,
}
impl RendezvousManager {
pub fn new(worker_id: WorkerId) -> Self {
Self::build(worker_id, None)
}
pub fn with_metrics(worker_id: WorkerId, metrics: Arc<VeloMetrics>) -> Self {
Self::build(worker_id, Some(metrics))
}
fn build(worker_id: WorkerId, metrics: Option<Arc<VeloMetrics>>) -> Self {
Self {
worker_id,
store: Arc::new(store::DataStore::with_metrics(metrics.clone())),
messenger_lock: OnceLock::new(),
metrics,
#[cfg(all(target_os = "linux", feature = "ucx"))]
reaper_shutdown: tokio_util::sync::CancellationToken::new(),
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
test_hook: parking_lot::Mutex::new(None),
}
}
pub fn register_handlers(
self: &Arc<Self>,
messenger: Arc<crate::messenger::Messenger>,
) -> Result<()> {
use handlers::{
create_rv_acquire_handler, create_rv_detach_handler, create_rv_lease_renew_handler,
create_rv_metadata_handler, create_rv_pull_handler, create_rv_ref_handler,
create_rv_release_handler,
};
messenger
.register_streaming_handler(create_rv_metadata_handler(Arc::clone(&self.store)))?;
messenger.register_streaming_handler(create_rv_acquire_handler(Arc::clone(&self.store)))?;
messenger.register_streaming_handler(create_rv_pull_handler(Arc::clone(&self.store)))?;
messenger.register_streaming_handler(create_rv_ref_handler(Arc::clone(&self.store)))?;
messenger.register_streaming_handler(create_rv_detach_handler(Arc::clone(&self.store)))?;
messenger.register_streaming_handler(create_rv_release_handler(Arc::clone(&self.store)))?;
messenger
.register_streaming_handler(create_rv_lease_renew_handler(Arc::clone(&self.store)))?;
self.messenger_lock
.set(messenger)
.map_err(|_| anyhow::anyhow!("register_handlers called twice"))?;
Ok(())
}
fn messenger(&self) -> &Arc<crate::messenger::Messenger> {
self.messenger_lock
.get()
.expect("RendezvousManager::register_handlers must be called before use")
}
pub fn register_data(&self, data: Bytes) -> DataHandle {
self.stage(store::SlotBody::InMemory(data), None)
}
pub fn register_data_with(&self, data: Bytes, opts: RegisterOptions) -> DataHandle {
self.stage(store::SlotBody::InMemory(data), Some(opts))
}
fn stage(&self, body: store::SlotBody, opts: Option<RegisterOptions>) -> DataHandle {
let started = Instant::now();
let data_len = body.total_len() as usize;
let local_id = self.store.register_body(body, opts);
if let Some(m) = &self.metrics {
m.record_rendezvous_operation(
RendezvousOp::Register,
HandlerOutcome::Success,
started.elapsed(),
);
m.record_rendezvous_bytes(RendezvousOp::Register, data_len);
m.set_rendezvous_active_slots(self.store.slots.len());
}
DataHandle::pack(self.worker_id, local_id)
}
pub async fn register_data_pinned(&self, data: &[u8]) -> DataHandle {
#[cfg(all(target_os = "linux", feature = "ucx"))]
if !data.is_empty()
&& let Some(ctx) = self.store.rdma()
{
use crate::observability::RdmaPathReason;
if !ctx.config.enabled {
self.store.record_path(RdmaPathReason::KillSwitch);
} else {
match ctx.registry.alloc_pinned(data.len()).await {
Ok(mut buf) => {
buf.copy_from_slice(data);
return self.stage(
store::SlotBody::Pinned(pinned::PinnedSlot::from_pool(
buf,
ctx.backend,
)),
None,
);
}
Err(e) => {
let reason = match e {
rdma::RdmaError::BudgetExceeded { .. } => RdmaPathReason::Budget,
_ => RdmaPathReason::PoolExhausted,
};
self.store.record_path(reason);
tracing::debug!(
bytes = data.len(),
error = %e,
"rendezvous: pinned staging refused; staging in plain memory"
);
}
}
}
}
self.register_data(Bytes::copy_from_slice(data))
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) fn register_data_pinned_sync(&self, data: Bytes) -> DataHandle {
use crate::observability::RdmaPathReason;
if !data.is_empty()
&& let Some(ctx) = self.store.rdma()
{
if !ctx.config.enabled {
self.store.record_path(RdmaPathReason::KillSwitch);
} else if let Some(mut buf) = ctx.registry.try_alloc_pinned(data.len()) {
buf.copy_from_slice(&data);
return self.stage(
store::SlotBody::Pinned(pinned::PinnedSlot::from_pool(buf, ctx.backend)),
None,
);
} else {
self.store.record_path(RdmaPathReason::PoolExhausted);
}
}
self.register_data(data)
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub fn register_data_in_region(
&self,
guard: &rdma::RegionGuard,
range: std::ops::Range<u64>,
) -> Result<DataHandle, rdma::RdmaError> {
let ctx = self.store.rdma().ok_or(rdma::RdmaError::NotConfigured)?;
let len = range
.end
.checked_sub(range.start)
.filter(|len| *len != 0)
.ok_or(rdma::RdmaError::OutOfRange)?;
if range.end > guard.len() {
return Err(rdma::RdmaError::OutOfRange);
}
let addr = guard
.addr()
.checked_add(range.start)
.ok_or(rdma::RdmaError::OutOfRange)?;
let in_flight = guard.in_flight().acquire();
if guard.in_flight().is_draining() || guard.is_deregistered() || guard.is_shutting_down() {
drop(in_flight);
return Err(rdma::RdmaError::ShuttingDown);
}
let remote = guard.remote();
Ok(self.stage(
store::SlotBody::Pinned(pinned::PinnedSlot::from_region(
in_flight,
guard.watch(),
ctx.backend,
addr,
len,
remote.generation,
remote.packed_key,
)),
None,
))
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) fn set_rdma_context(
&self,
registry: Arc<rdma::RdmaRegistry>,
config: rdma::RdmaRendezvousConfig,
runtime: &tokio::runtime::Handle,
) -> Result<()> {
let key = registry.backend_key().to_string();
let backend = descriptor::DescriptorBackend::from_key(&key).ok_or_else(|| {
anyhow::anyhow!("rdma backend {key:?} has no descriptor discriminator")
})?;
let mut config = config;
config.lease_timeout = normalize_lease_timeout(config.lease_timeout);
let lease_period = (config.lease_timeout / 2).max(std::time::Duration::from_millis(10));
let period = match registry.arena_reclaim_after() {
Some(after) => lease_period.min((after / 2).max(std::time::Duration::from_millis(10))),
None => lease_period,
};
let ctx = RdmaContext {
registry: Arc::clone(®istry),
config,
backend,
};
if self.store.set_rdma(ctx).is_err() {
anyhow::bail!("set_rdma_context called twice");
}
let store = Arc::downgrade(&self.store);
let registry = Arc::downgrade(®istry);
let token = self.reaper_shutdown.clone();
runtime.spawn(async move {
reap_expired_leases(store, registry, token, period).await;
});
Ok(())
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub(crate) fn shutdown(&self) {
self.reaper_shutdown.cancel();
let (demoted, dropped) = self.store.demote_pinned_slots();
if demoted != 0 || dropped != 0 {
tracing::debug!(
demoted,
dropped,
"rendezvous: released pinned staging ahead of the registration sweep"
);
}
}
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
pub fn arm_rdma_hook(&self, hook: RdmaTestHook) {
*self.test_hook.lock() = Some(hook);
}
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
fn take_descriptor_hook(&self) -> Option<RdmaTestHook> {
let mut slot = self.test_hook.lock();
match slot.as_ref() {
Some(hook) if hook.is_descriptor_fault() => slot.take(),
_ => None,
}
}
#[cfg(all(target_os = "linux", feature = "ucx", feature = "test-helpers"))]
fn take_get_hook(&self) -> Option<RdmaTestHook> {
let mut slot = self.test_hook.lock();
match slot.as_ref() {
Some(hook) if !hook.is_descriptor_fault() => slot.take(),
_ => None,
}
}
pub async fn metadata(&self, handle: DataHandle) -> Result<DataMetadata> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
self.store
.metadata(local_id)
.ok_or_else(|| anyhow::anyhow!("rendezvous handle not found: {handle}"))
} else {
consumer::Consumer::metadata(self.messenger(), handle).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Metadata, outcome, started.elapsed());
}
result
}
pub async fn get(&self, handle: DataHandle) -> Result<(Bytes, u64)> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
let lease_id = self
.store
.acquire_read_lock(local_id)
.ok_or_else(|| anyhow::anyhow!("rendezvous handle not found: {handle}"))?;
let lease = self.lease_guard(handle, lease_id);
let data = self
.store
.get_data(local_id)
.ok_or_else(|| anyhow::anyhow!("slot vanished after lock acquire"))?;
Ok((data, lease.disarm()))
} else {
consumer::Consumer::get(self, handle).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Get, outcome, started.elapsed());
if let Ok((ref data, _)) = result {
m.record_rendezvous_bytes(RendezvousOp::Get, data.len());
}
}
result
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub async fn get_pinned(&self, handle: DataHandle) -> Result<(rdma::PinnedBuf, u64)> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
let lease_id = self
.store
.acquire_read_lock(local_id)
.ok_or_else(|| anyhow::anyhow!("rendezvous handle not found: {handle}"))?;
let lease = self.lease_guard(handle, lease_id);
let data = self
.store
.get_data(local_id)
.ok_or_else(|| anyhow::anyhow!("slot vanished after lock acquire"))?;
let ctx = self.store.rdma().ok_or(rdma::RdmaError::NotConfigured)?;
let mut buf = ctx.registry.alloc_pinned(data.len()).await?;
buf.copy_from_slice(&data);
Ok((buf, lease.disarm()))
} else {
consumer::Consumer::get_pinned(self, handle).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Get, outcome, started.elapsed());
if let Ok((ref buf, _)) = result {
m.record_rendezvous_bytes(RendezvousOp::Get, buf.len());
}
}
result
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
pub async fn alloc_pinned_writer(
&self,
len: usize,
) -> Result<write::PinnedWriter, rdma::RdmaError> {
let ctx = self.store.rdma().ok_or(rdma::RdmaError::NotConfigured)?;
Ok(write::PinnedWriter::new(
ctx.registry.alloc_pinned(len).await?,
))
}
pub async fn get_into(
&self,
handle: DataHandle,
dest: &mut impl RendezvousWrite,
) -> Result<u64> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
let lease_id = self
.store
.acquire_read_lock(local_id)
.ok_or_else(|| anyhow::anyhow!("rendezvous handle not found: {handle}"))?;
let lease = self.lease_guard(handle, lease_id);
let data = self
.store
.get_data(local_id)
.ok_or_else(|| anyhow::anyhow!("slot vanished after lock acquire"))?;
dest.write_chunk(0, &data)?;
Ok(lease.disarm())
} else {
consumer::Consumer::get_into(self, handle, dest).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Get, outcome, started.elapsed());
}
result
}
pub async fn ref_handle(&self, handle: DataHandle) -> Result<()> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
if !self.store.ref_increment(local_id) {
anyhow::bail!("rendezvous handle not found: {handle}");
}
Ok(())
} else {
consumer::Consumer::ref_handle(self.messenger(), handle).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Ref, outcome, started.elapsed());
}
result
}
pub async fn detach(&self, handle: DataHandle, lease_id: u64) -> Result<()> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
match self.store.consume_lease(lease_id, local_id) {
store::LeaseOutcome::Consumed => {
self.store.release_read_lock(local_id);
self.store.remove_transfers_by_lease(lease_id);
Ok(())
}
outcome => {
anyhow::bail!(
"invalid or already-consumed lease {lease_id} for {handle}: {outcome:?}"
)
}
}
} else {
consumer::Consumer::detach(self.messenger(), handle, lease_id).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Detach, outcome, started.elapsed());
}
result
}
pub async fn release(&self, handle: DataHandle, lease_id: u64) -> Result<()> {
let started = Instant::now();
let (target_worker, local_id) = handle.unpack();
let result = if target_worker == self.worker_id {
match self.store.consume_lease(lease_id, local_id) {
store::LeaseOutcome::Consumed => {
self.store.release_read_lock(local_id);
self.store.remove_transfers_by_lease(lease_id);
let should_free = self.store.ref_decrement(local_id);
if should_free {
self.store.try_free(local_id);
}
Ok(())
}
outcome => {
anyhow::bail!(
"invalid or already-consumed lease {lease_id} for {handle}: {outcome:?}"
)
}
}
} else {
consumer::Consumer::release(self.messenger(), handle, lease_id).await
};
if let Some(m) = &self.metrics {
let outcome = if result.is_ok() {
HandlerOutcome::Success
} else {
HandlerOutcome::Error
};
m.record_rendezvous_operation(RendezvousOp::Release, outcome, started.elapsed());
m.set_rendezvous_active_slots(self.store.slots.len());
}
result
}
pub fn worker_id(&self) -> WorkerId {
self.worker_id
}
pub fn data_store(&self) -> &Arc<store::DataStore> {
&self.store
}
pub(crate) fn lease_guard(&self, handle: DataHandle, lease_id: u64) -> LeaseGuard {
let local = handle.worker_id() == self.worker_id;
LeaseGuard {
store: Arc::clone(&self.store),
messenger: if local {
None
} else {
self.messenger_lock.get().cloned()
},
runtime: self
.messenger_lock
.get()
.map(|m| m.runtime().clone())
.unwrap_or_else(tokio::runtime::Handle::current),
handle,
lease_id,
armed: true,
}
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
const MIN_LEASE_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(1);
#[cfg(all(target_os = "linux", feature = "ucx"))]
fn normalize_lease_timeout(configured: std::time::Duration) -> std::time::Duration {
if configured >= MIN_LEASE_TIMEOUT {
return configured;
}
tracing::warn!(
configured = ?configured,
clamped_to = ?MIN_LEASE_TIMEOUT,
"rendezvous: the configured RDMA lease timeout is below the millisecond the wire \
format can carry, where it would encode as \"no deadline\" while the owner still \
armed one. Clamping. A timeout this short will reap live transfers."
);
MIN_LEASE_TIMEOUT
}
#[must_use = "the lease is released when this is dropped; call disarm() to keep it"]
pub(crate) struct LeaseGuard {
store: Arc<store::DataStore>,
messenger: Option<Arc<crate::messenger::Messenger>>,
runtime: tokio::runtime::Handle,
handle: DataHandle,
lease_id: u64,
armed: bool,
}
impl LeaseGuard {
pub(crate) fn disarm(mut self) -> u64 {
self.armed = false;
self.lease_id
}
}
impl Drop for LeaseGuard {
fn drop(&mut self) {
if !self.armed {
return;
}
let (_, local_id) = self.handle.unpack();
let lease_id = self.lease_id;
let handle = self.handle;
let Some(messenger) = self.messenger.clone() else {
if self.store.consume_lease(lease_id, local_id) == store::LeaseOutcome::Consumed {
self.store.release_read_lock(local_id);
self.store.remove_transfers_by_lease(lease_id);
}
return;
};
tracing::debug!(
%handle,
lease = lease_id,
"rendezvous: releasing a lease whose get did not complete"
);
let spawned = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
self.runtime.spawn(async move {
if let Err(e) = consumer::Consumer::detach(&messenger, handle, lease_id).await {
tracing::warn!(
%handle,
lease = lease_id,
error = %e,
"rendezvous: could not detach a lease after a failed get"
);
}
})
}));
if spawned.is_err() {
tracing::warn!(
%handle,
lease = lease_id,
"rendezvous: the runtime is gone, so this lease was not detached; the owner's \
reaper reclaims it if it was an RDMA lease"
);
}
}
}
#[cfg(all(target_os = "linux", feature = "ucx"))]
async fn reap_expired_leases(
store: std::sync::Weak<store::DataStore>,
registry: std::sync::Weak<rdma::RdmaRegistry>,
token: tokio_util::sync::CancellationToken,
period: std::time::Duration,
) {
loop {
tokio::select! {
_ = token.cancelled() => return,
_ = tokio::time::sleep(period) => {}
}
let Some(store) = store.upgrade() else { return };
let registry = registry.upgrade();
if let Some(registry) = ®istry {
registry.reclaim_idle_arenas().await;
}
let expired = store.expired_leases(Instant::now());
let mut reaped = 0usize;
for (lease_id, local_id) in expired {
if store.force_release_lease(lease_id, local_id) {
reaped += 1;
tracing::warn!(
lease = lease_id,
slot = local_id,
"rendezvous: force-releasing an RDMA lease past its deadline; the consumer \
did not release it and stopped renewing it"
);
}
}
if let Some(m) = store.metrics() {
if reaped != 0 {
m.record_rendezvous_leases_reaped(reaped);
m.set_rendezvous_active_slots(store.slots.len());
}
if let Some(registry) = ®istry
&& let Some(regions) = registry.live_regions()
{
m.set_rdma_live_regions(regions);
}
}
}
}
#[cfg(all(target_os = "linux", feature = "ucx", test))]
mod lease_timeout_tests {
use super::*;
use std::time::Duration;
#[test]
fn a_lease_timeout_always_survives_the_trip_to_milliseconds() {
for degenerate in [
Duration::ZERO,
Duration::from_nanos(1),
Duration::from_micros(1),
Duration::from_micros(999),
] {
let normalized = normalize_lease_timeout(degenerate);
assert_eq!(normalized, MIN_LEASE_TIMEOUT, "{degenerate:?}");
assert_ne!(
normalized.as_millis(),
0,
"{degenerate:?} still encodes as \"no deadline\" on the wire"
);
}
for sane in [
Duration::from_millis(1),
Duration::from_millis(250),
Duration::from_secs(30),
] {
assert_eq!(
normalize_lease_timeout(sane),
sane,
"a usable timeout must be left alone"
);
}
}
}