use std::collections::HashMap;
use std::sync::{OnceLock, RwLock};
use anyhow::Result;
use futures::future::BoxFuture;
use crate::object::ObjectBlockOps;
use crate::{BlockId, InstanceId, SequenceHash};
use kvbm_physical::manager::LayoutHandle;
use kvbm_physical::transfer::TransferOptions;
use super::{
LogicalLayoutHandle, RemoteDescriptor, SerializedLayout, TransferCompleteNotification, Worker,
WorkerLayoutResponse,
};
pub struct CoordinatedWorker {
inner: Box<dyn Worker>,
rank: usize,
host_instance: InstanceId,
local_g1: OnceLock<LayoutHandle>,
local_g2: OnceLock<LayoutHandle>,
local_g3: OnceLock<LayoutHandle>,
remote_handles: RwLock<HashMap<(InstanceId, usize, LogicalLayoutHandle), LayoutHandle>>,
}
impl CoordinatedWorker {
pub fn new(inner: Box<dyn Worker>, rank: usize, host_instance: InstanceId) -> Self {
Self {
inner,
rank,
host_instance,
local_g1: OnceLock::new(),
local_g2: OnceLock::new(),
local_g3: OnceLock::new(),
remote_handles: RwLock::new(HashMap::new()),
}
}
pub fn rank(&self) -> usize {
self.rank
}
pub fn host_instance(&self) -> InstanceId {
self.host_instance
}
pub fn inner(&self) -> &dyn Worker {
&*self.inner
}
pub fn set_local_g1(&self, handle: LayoutHandle) -> Result<()> {
self.local_g1
.set(handle)
.map_err(|_| anyhow::anyhow!("G1 handle already set"))
}
pub fn set_local_g2(&self, handle: LayoutHandle) -> Result<()> {
self.local_g2
.set(handle)
.map_err(|_| anyhow::anyhow!("G2 handle already set"))
}
pub fn set_local_g3(&self, handle: LayoutHandle) -> Result<()> {
self.local_g3
.set(handle)
.map_err(|_| anyhow::anyhow!("G3 handle already set"))
}
pub fn apply_layout_response(&self, response: &WorkerLayoutResponse) -> Result<()> {
let unpacked = response.metadata.unpack()?;
for descriptor in &unpacked.layouts {
match descriptor.logical_type {
LogicalLayoutHandle::G1 => {
let _ = self.local_g1.set(descriptor.handle);
}
LogicalLayoutHandle::G2 => {
let _ = self.local_g2.set(descriptor.handle);
}
LogicalLayoutHandle::G3 => {
let _ = self.local_g3.set(descriptor.handle);
}
LogicalLayoutHandle::G4 => {
}
}
}
Ok(())
}
pub fn local_g1(&self) -> Option<LayoutHandle> {
self.local_g1.get().copied()
}
pub fn local_g2(&self) -> Option<LayoutHandle> {
self.local_g2.get().copied()
}
pub fn local_g3(&self) -> Option<LayoutHandle> {
self.local_g3.get().copied()
}
pub async fn import_remote_metadata(
&self,
remote_leader_id: InstanceId,
remote_rank: usize,
metadata: SerializedLayout,
) -> Result<()> {
let unpacked = metadata.unpack()?;
let repacked = SerializedLayout::pack(
unpacked.worker_address.clone(),
unpacked.nixl_metadata.clone(),
unpacked.layouts.clone(),
)?;
let response = self.inner.import_metadata(repacked)?;
let _handles = response.await?;
let mut mapping = self.remote_handles.write().unwrap();
for descriptor in &unpacked.layouts {
mapping.insert(
(remote_leader_id, remote_rank, descriptor.logical_type),
descriptor.handle,
);
}
Ok(())
}
pub fn resolve_remote_handle(
&self,
remote_leader_id: InstanceId,
remote_rank: usize,
logical_type: LogicalLayoutHandle,
) -> Option<LayoutHandle> {
self.remote_handles
.read()
.unwrap()
.get(&(remote_leader_id, remote_rank, logical_type))
.copied()
}
pub fn has_remote_metadata(&self, remote_leader_id: InstanceId, remote_rank: usize) -> bool {
let handles = self.remote_handles.read().unwrap();
handles
.keys()
.any(|(leader, rank, _)| *leader == remote_leader_id && *rank == remote_rank)
}
#[allow(clippy::too_many_arguments)]
pub fn transfer_from_remote(
&self,
remote_leader_id: InstanceId,
remote_rank: usize,
src_logical: LogicalLayoutHandle,
src_block_ids: Vec<BlockId>,
dst_logical: LogicalLayoutHandle,
dst_block_ids: std::sync::Arc<[BlockId]>,
options: TransferOptions,
) -> Result<TransferCompleteNotification> {
let src_handle = self
.resolve_remote_handle(remote_leader_id, remote_rank, src_logical)
.ok_or_else(|| {
anyhow::anyhow!(
"No mapping for remote ({}, rank {}, {:?})",
remote_leader_id,
remote_rank,
src_logical
)
})?;
let src = RemoteDescriptor::Layout {
handle: src_handle,
block_ids: src_block_ids,
};
self.inner
.execute_remote_onboard(src, dst_logical, dst_block_ids, options)
}
}
impl ObjectBlockOps for CoordinatedWorker {
fn has_blocks(
&self,
keys: Vec<SequenceHash>,
) -> BoxFuture<'static, Vec<(SequenceHash, Option<usize>)>> {
self.inner.has_blocks(keys)
}
fn put_blocks(
&self,
keys: Vec<SequenceHash>,
src_layout: LogicalLayoutHandle,
block_ids: Vec<BlockId>,
) -> BoxFuture<'static, Vec<Result<SequenceHash, SequenceHash>>> {
self.inner.put_blocks(keys, src_layout, block_ids)
}
fn get_blocks(
&self,
keys: Vec<SequenceHash>,
dst_layout: LogicalLayoutHandle,
block_ids: Vec<BlockId>,
) -> BoxFuture<'static, Vec<Result<SequenceHash, SequenceHash>>> {
self.inner.get_blocks(keys, dst_layout, block_ids)
}
}
#[cfg(test)]
mod tests {
}