use std::{future::Future, pin::Pin, sync::Arc};
use crate::receptionist::Visibility;
use crate::{Op, OpType, Receptionist, RemoteInvocation, ResponseRegistry};
use super::framing::ControlMessage;
use super::membership::ClusterEvent;
use super::net::Net;
use super::remote;
use tokio::sync::{broadcast, mpsc};
pub async fn request_sync_from_peer(
net: &Arc<dyn Net>,
receptionist: &Receptionist,
node_id: &str,
) {
let our_vv = receptionist.version_vector();
if let Err(e) = net
.send_control(node_id, ControlMessage::RegistrySyncRequest(our_vv))
.await
{
tracing::warn!("Failed to request sync from {node_id}: {e}");
}
}
pub async fn send_sync_to_peer(
net: &Arc<dyn Net>,
receptionist: &Receptionist,
node_id: &str,
peer_vv: &crate::VersionVector,
) {
let delta = receptionist.ops_since(peer_vv);
if delta.is_empty() {
return;
}
tracing::debug!("Sending {} ops to {node_id}", delta.len());
if let Err(e) = net
.send_control(node_id, ControlMessage::RegistrySync(delta))
.await
{
tracing::warn!("Failed to send sync to {node_id}: {e}");
}
}
pub async fn send_public_sync_to_peer(
net: &Arc<dyn Net>,
receptionist: &Receptionist,
peer_node_id: &str,
peer_vv: &crate::VersionVector,
) {
let delta = receptionist.public_ops_since(peer_vv);
if delta.is_empty() {
return;
}
tracing::debug!("Sending {} public ops to edge {peer_node_id}", delta.len());
if let Err(e) = net
.send_control(peer_node_id, ControlMessage::RegistrySync(delta))
.await
{
tracing::debug!("Failed to send public sync to {peer_node_id}: {e}");
}
}
pub async fn periodic_sync(
net: &Arc<dyn Net>,
receptionist: &Receptionist,
node_registry: &crate::cluster::NodeRegistry,
) {
let nodes = net.connected_nodes().await;
for node_id in &nodes {
if node_registry
.get(node_id)
.map(|e| e.is_edge_client)
.unwrap_or(false)
{
continue;
}
request_sync_from_peer(net, receptionist, node_id).await;
}
}
pub type RemoteRegistrationFn = Box<
dyn Fn(
&Receptionist,
&str,
mpsc::UnboundedSender<RemoteInvocation>,
ResponseRegistry,
&str,
Visibility,
) + Send
+ Sync,
>;
pub struct TypeRegistry {
factories: std::collections::HashMap<String, RemoteRegistrationFn>,
}
impl TypeRegistry {
pub fn new() -> Self {
Self {
factories: std::collections::HashMap::new(),
}
}
pub fn from_auto() -> Self {
let mut reg = Self::new();
for entry in crate::ACTOR_TYPE_ENTRIES {
reg.register((entry.actor_type_name)(), Box::new(entry.register));
}
reg
}
pub fn register(&mut self, actor_type_name: impl Into<String>, factory: RemoteRegistrationFn) {
self.factories.insert(actor_type_name.into(), factory);
}
pub fn get(&self, actor_type_name: &str) -> Option<&RemoteRegistrationFn> {
self.factories.get(actor_type_name)
}
pub fn known_types(&self) -> Vec<String> {
self.factories.keys().cloned().collect()
}
}
impl Default for TypeRegistry {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, thiserror::Error)]
pub enum SpawnError {
#[error("unknown actor type: {0}")]
UnknownType(String),
#[error("failed to deserialize state: {0}")]
DeserializeFailed(String),
#[error("failed to start actor: {0}")]
StartFailed(String),
}
pub type SpawnFactory = Box<
dyn Fn(
Receptionist,
String,
Vec<u8>,
) -> Pin<Box<dyn Future<Output = Result<(), SpawnError>> + Send>>
+ Send
+ Sync,
>;
pub struct SpawnRegistry {
factories: std::collections::HashMap<String, SpawnFactory>,
}
impl SpawnRegistry {
pub fn new() -> Self {
Self {
factories: std::collections::HashMap::new(),
}
}
pub fn register(&mut self, actor_type_name: impl Into<String>, factory: SpawnFactory) {
self.factories.insert(actor_type_name.into(), factory);
}
pub fn get(&self, actor_type_name: &str) -> Option<&SpawnFactory> {
self.factories.get(actor_type_name)
}
pub async fn spawn(
&self,
receptionist: Receptionist,
label: &str,
actor_type_name: &str,
state_bytes: &[u8],
) -> Result<(), SpawnError> {
let factory = self
.get(actor_type_name)
.ok_or_else(|| SpawnError::UnknownType(actor_type_name.to_string()))?;
factory(receptionist, label.to_string(), state_bytes.to_vec()).await
}
pub fn known_types(&self) -> Vec<String> {
self.factories.keys().cloned().collect()
}
}
impl Default for SpawnRegistry {
fn default() -> Self {
Self::new()
}
}
pub fn apply_remote_ops(
ops: Vec<Op>,
receptionist: &Receptionist,
type_registry: &TypeRegistry,
_remote_node_id: &str,
event_tx: &broadcast::Sender<ClusterEvent>,
net: &Arc<dyn Net>,
) {
receptionist.apply_ops(ops.clone());
for op in &ops {
if let OpType::Register {
label,
actor_type_name,
visibility,
..
} = &op.op_type
{
if op.node_id == receptionist.node_id() {
continue;
}
if receptionist.has_entry(label) {
continue;
}
if let Some(factory) = type_registry.get(actor_type_name) {
let (wire_tx, wire_rx) = mpsc::unbounded_channel();
let response_registry = ResponseRegistry::new();
factory(
receptionist,
label,
wire_tx,
response_registry.clone(),
&op.node_id,
*visibility,
);
let target_node_id = op.node_id.clone();
receptionist
.runtime()
.spawn(Box::pin(remote::run_actor_stream_writer(
Arc::clone(net),
receptionist.runtime().clone(),
target_node_id,
label.clone(),
wire_rx,
response_registry,
)));
let _ = event_tx.send(ClusterEvent::ActorRegistered {
label: label.clone(),
node_id: op.node_id.clone(),
actor_type: actor_type_name.clone(),
});
tracing::info!(
"Registered remote actor {label} (type {actor_type_name}) from node {}",
op.node_id
);
} else {
tracing::debug!(
"Unknown actor type {actor_type_name} for {label} — stored op for relay"
);
}
}
}
}