1use std::fmt::{self, Debug};
10use std::result::Result as StdResult;
11use std::sync::Arc;
12
13use ahash::HashMap;
14use bytes::Bytes;
15use futures_util::{FutureExt, TryFutureExt};
16use tokio::time::{Duration, Instant};
17use tracing::{Instrument, debug, info, warn};
18
19use acktor::{
20 Actor, ActorContext, Address, ErrorReport, Handler, Message, Sender, SenderInfo,
21 channel::oneshot,
22 message::FutureMessageResult,
23 utils::{ShortName, debug_trace},
24};
25use acktor_ipc_proto::{message, utils as proto_utils};
26
27use crate::actor_ref::ActorRef;
28use crate::codec::{Decode, DecodeContext, DecodeError, Encode, EncodeContext};
29use crate::error::SessionError;
30use crate::ipc_method::IpcConnection;
31use crate::node::actor_mgr::{self, ActorMgr};
32use crate::remote::{
33 BinaryMessage, RemoteAddressable, RemoteMailbox, RemoteMailboxRegistry, RemoteSpawnable,
34};
35
36pub mod command;
37
38mod context;
39use context::SessionContext;
40
41mod proxy;
42use proxy::Proxy;
43
44type Result<T> = StdResult<T, SessionError>;
45
46pub(crate) const RESPONSE_TIMEOUT: Duration = Duration::from_secs(60);
50
51pub(crate) const CLEANUP_INTERVAL: Duration = Duration::from_secs(15);
53
54struct MessageResponse {
55 tag: u64,
56 result: StdResult<Bytes, String>,
57}
58
59impl Debug for MessageResponse {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 f.debug_tuple("MessageResponse").field(&self.tag).finish()
62 }
63}
64
65impl Message for MessageResponse {
66 type Result = ();
67}
68
69pub struct Session {
71 connection: Box<dyn IpcConnection>,
72 registry: RemoteMailboxRegistry,
73 actor_mgr: Address<ActorMgr>,
74 tag: u64, proxy: Option<Arc<Proxy>>,
76 message_res_tx_map: HashMap<u64, (oneshot::Sender<Bytes>, Instant)>,
77}
78
79impl Session {
80 pub(crate) fn new(
83 connection: Box<dyn IpcConnection>,
84 registry: RemoteMailboxRegistry,
85 actor_mgr: Address<ActorMgr>,
86 ) -> Self {
87 Self {
88 connection,
89 registry,
90 actor_mgr,
91 tag: 0,
92 proxy: None,
93 message_res_tx_map: HashMap::default(),
94 }
95 }
96
97 fn cleanup_message_res_tx_map(&mut self) {
98 let now = Instant::now();
99
100 let expired = self.message_res_tx_map.extract_if(|_, (tx, timestamp)| {
101 tx.is_closed() || now.duration_since(*timestamp) >= RESPONSE_TIMEOUT
102 });
103
104 for (tag, (tx, _)) in expired {
105 if tx.is_closed() {
106 debug!(
107 "The sender of message with tag {} has closed the response rx, remove the \
108 corresponding response tx",
109 tag
110 );
111 } else {
112 let _ = tx.send_err(SessionError::ResponseTimeout);
113 }
114 }
115 }
116
117 fn next_tag(&mut self) -> u64 {
118 let tag = self.tag;
119 self.tag = self.tag.wrapping_add(1);
120 tag
121 }
122
123 async fn send_ipc_message(&mut self, ipc_msg: message::IpcMessage) -> Result<()> {
124 let encoded_ipc_msg = ipc_msg.encode_to_bytes(None)?;
125 self.connection
126 .send(encoded_ipc_msg)
127 .await
128 .map_err(SessionError::SendOutboundMessageFailed)?;
129
130 Ok(())
131 }
132
133 fn encode_context(&self) -> Arc<dyn EncodeContext + Send + Sync> {
134 self.proxy
135 .clone()
136 .expect("proxy is always available after session is started")
137 as Arc<dyn EncodeContext + Send + Sync>
138 }
139
140 fn decode_context(&self) -> Arc<dyn DecodeContext + Send + Sync> {
141 self.proxy
142 .clone()
143 .expect("proxy is always available after session is started")
144 as Arc<dyn DecodeContext + Send + Sync>
145 }
146
147 fn find_mailbox(&self, actor_id: u64) -> Result<RemoteMailbox> {
148 self.registry
149 .get(actor_id)
150 .ok_or_else(|| SessionError::ActorNotFound(actor_id.to_string()))
151 }
152
153 async fn find_mailbox_by_label(&self, label: String) -> Result<RemoteMailbox> {
154 self.actor_mgr
155 .send(actor_mgr::GetActor { label })
156 .await?
157 .await?
158 }
159
160 async fn handle_node_message(
161 &mut self,
162 message: message::NodeMessage,
163 ctx: &mut <Self as Actor>::Context,
164 ) -> Result<()> {
165 match message.message {
166 Some(message::NodeMessageType::CreateActor(message::CreateActor {
167 type_id,
168 label,
169 config,
170 tag,
171 })) => {
172 let actor_mgr = self.actor_mgr.clone();
173 let address = ctx.address();
174
175 tokio::spawn(
177 async move {
178 actor_mgr
179 .send(actor_mgr::CreateActor {
180 type_id,
181 label,
182 config,
183 })
184 .await?
185 .await?
186 }
187 .then(move |result| async move {
188 let result = match result {
189 Ok(actor_id) => proto_utils::ResultAddress::ok(actor_id.as_local()),
190 Err(e) => proto_utils::ResultAddress::err(e.report()),
191 };
192
193 let bytes: Bytes = prost::Message::encode_to_vec(&result).into();
194
195 address
200 .do_send(MessageResponse {
201 tag,
202 result: Ok(bytes),
203 })
204 .await
205 })
206 .inspect_err(|e| {
207 warn!(
210 "Could not send `NodeMessageResponse::CreateActor` to remote node: {}",
211 e.report()
212 );
213 })
214 .in_current_span(),
215 );
216
217 Ok(())
218 }
219
220 Some(message::NodeMessageType::GetActor(message::GetActor { actor, tag })) => {
221 let result = match actor {
222 Some(proto_utils::ActorRef { r#ref }) => match r#ref {
223 Some(proto_utils::ActorRefType::Index(actor_id)) => {
224 self.find_mailbox(actor_id)
225 }
226 Some(proto_utils::ActorRefType::Label(label)) => {
227 self.find_mailbox_by_label(label).await
228 }
229 None => Err(DecodeError::from("missing field `ref` in `ActorRef`").into()),
230 },
231 _ => Err(
232 DecodeError::from("missing field `actor` in `NodeMessage::GetActor`")
233 .into(),
234 ),
235 };
236
237 let result = match result {
238 Ok(mailbox) => proto_utils::ResultAddress::ok(mailbox.index().as_local()),
239 Err(e) => proto_utils::ResultAddress::err(e.report()),
240 };
241
242 let bytes: Bytes = prost::Message::encode_to_vec(&result).into();
243 let ipc_msg = message::IpcMessage::message_response(message::MessageResponse::new(
244 tag,
245 Ok(bytes),
246 ));
247
248 self.send_ipc_message(ipc_msg).await.inspect_err(|e| {
249 warn!(
252 "Could not send `NodeMessageResponse::GetActor` to remote node: {}",
253 e.report()
254 );
255 })
256 }
257
258 _ => Err(DecodeError::from("missing field `message` in `NodeMessage`").into()),
259 }
260 }
261
262 async fn handle_actor_message(
263 &mut self,
264 message: message::ActorMessage,
265 ctx: &mut <Self as Actor>::Context,
266 ) -> Result<()> {
267 let message::ActorMessage {
268 actor_id,
269 message_id,
270 message,
271 tag,
272 } = message;
273
274 match tag {
275 Some(tag) => {
276 let address = ctx.address();
279 let recipient = self.find_mailbox(actor_id);
280 let (tx, rx) = oneshot::channel();
281 let message = BinaryMessage::send(actor_id, message_id, message, tx)
282 .with_encode_context(self.encode_context())
283 .with_decode_context(self.decode_context());
284
285 tokio::spawn(
287 async move {
288 recipient?
289 .do_send(message)
290 .await
291 .map_err(|e| SessionError::ForwardInboundMessageFailed(e.into()))?;
292
293 let result = rx
294 .await
295 .map_err(|e| SessionError::HandleInboundMessageFailed(e.into()))?;
296
297 Ok::<Bytes, SessionError>(result)
298 }
299 .then(move |result| async move {
300 address
305 .do_send(MessageResponse {
306 tag,
307 result: result.map_err(|e| e.report()),
308 })
309 .await
310 })
311 .inspect_err(|e| {
312 warn!(
315 "Could not send `ActorMessageResponse` to remote node: {}",
316 e.report()
317 );
318 })
319 .in_current_span(),
320 );
321
322 Ok(())
323 }
324
325 None => {
326 self.find_mailbox(actor_id)?
331 .do_send(
332 BinaryMessage::do_send(actor_id, message_id, message)
333 .with_decode_context(self.decode_context()),
334 )
335 .await
336 .map_err(|e| SessionError::ForwardInboundMessageFailed(e.into()))
337 }
338 }
339 }
340
341 fn handle_message_response(
342 &mut self,
343 response: message::MessageResponse,
344 _ctx: &mut <Self as Actor>::Context,
345 ) -> Result<()> {
346 let tag = response.tag;
347
348 let (sender, _) = self
349 .message_res_tx_map
350 .remove(&tag)
351 .ok_or(SessionError::InvalidMessageResTxTag(tag))?;
355
356 let result: Result<_> = match response.response {
359 Some(message::ResponseType::Ok(bytes)) => Ok(bytes),
360 Some(message::ResponseType::Err(err)) => Err(SessionError::RemoteNodeError(err)),
361 None => Err(DecodeError::from("missing field `response` in `MessageResponse`").into()),
362 };
363
364 match result {
367 Ok(bytes) => sender
368 .send(bytes)
369 .map_err(|_| SessionError::ForwardMessageResFailed),
370 Err(e) => sender
371 .send_err(e)
372 .map_err(|_| SessionError::ForwardMessageResFailed),
373 }
374 }
375
376 async fn handle_ipc_message(
377 &mut self,
378 message: Bytes,
379 ctx: &mut <Self as Actor>::Context,
380 ) -> Result<()> {
381 let ipc_message = message::IpcMessage::decode(message, None)?;
382
383 match ipc_message.message {
384 Some(message::IpcMessageType::NodeMessage(message)) => {
385 self.handle_node_message(message, ctx).await
386 }
387 Some(message::IpcMessageType::ActorMessage(message)) => {
388 self.handle_actor_message(message, ctx).await
389 }
390 Some(message::IpcMessageType::MessageResponse(response)) => {
391 self.handle_message_response(response, ctx)
392 }
393 _ => Err(DecodeError::from("missing field `message` in `IpcMessage`").into()),
394 }
395 }
396}
397
398impl Actor for Session {
399 type Context = SessionContext;
400 type Error = SessionError;
401
402 async fn post_start(&mut self, ctx: &mut Self::Context) -> Result<()> {
403 info!("Session {} is started", self.connection.peer_endpoint());
404
405 self.proxy = Some(Proxy::new(ctx.address(), self.registry.clone()));
406
407 Ok(())
408 }
409
410 async fn post_stop(&mut self, _ctx: &mut Self::Context) -> Result<()> {
411 self.connection
412 .close()
413 .await
414 .map_err(SessionError::IoError)?;
415
416 info!("Session {} is stopped", self.connection.peer_endpoint());
417
418 Ok(())
419 }
420}
421
422impl<A> Handler<command::RemoteCreateActor<A>> for Session
423where
424 A: Actor + RemoteSpawnable,
425{
426 type Result = FutureMessageResult<command::RemoteCreateActor<A>>;
427
428 async fn handle(
429 &mut self,
430 msg: command::RemoteCreateActor<A>,
431 _ctx: &mut <Self as Actor>::Context,
432 ) -> Self::Result {
433 debug_trace!("Handle command RemoteCreateActor<{}>", ShortName::of::<A>());
434
435 let command::RemoteCreateActor { label, config, .. } = msg;
436
437 let (tx, rx) = oneshot::channel();
438
439 let tag = self.next_tag();
440 let ipc_msg = message::IpcMessage::node_message(message::NodeMessage::create_actor(
441 A::TYPE_ID.as_u64(),
442 label,
443 config.unwrap_or_default(),
444 tag,
445 ));
446
447 if let Err(e) = self.send_ipc_message(ipc_msg).await {
448 warn!(
449 "Could not send `NodeMessage::CreateActor` to remote node: {}",
450 e.report()
451 );
452 if let Err(e) = tx.send_err(e) {
455 warn!(
458 "Could not report the error in `Handler<RemoteCreateActor<{}>>` to the \
459 original sender: {}",
460 ShortName::of::<A>(),
461 e.report()
462 );
463 }
464 } else {
465 self.message_res_tx_map.insert(tag, (tx, Instant::now()));
466 }
467
468 let decode_context = self.decode_context();
469
470 FutureMessageResult::new(async move {
471 let bytes = rx.await?;
472 let result = <proto_utils::ResultAddress as prost::Message>::decode(bytes)
473 .map_err(DecodeError::other)?;
474 match result.result {
475 Some(proto_utils::ResultAddressType::Ok(actor_id)) => {
476 Address::new_with_decode_context(actor_id, decode_context.as_ref())
477 .map_err(Into::into)
478 }
479 Some(proto_utils::ResultAddressType::Err(e)) => {
480 Err(SessionError::RemoteNodeError(e))
481 }
482 _ => Err(DecodeError::other("missing field `result` in `ResultAddress`").into()),
483 }
484 })
485 }
486}
487
488impl<A> Handler<command::RemoteGetActor<A>> for Session
489where
490 A: Actor + RemoteAddressable,
491{
492 type Result = FutureMessageResult<command::RemoteGetActor<A>>;
493
494 async fn handle(
495 &mut self,
496 msg: command::RemoteGetActor<A>,
497 _ctx: &mut <Self as Actor>::Context,
498 ) -> Self::Result {
499 debug_trace!("Handle command RemoteGetActor<{}>", ShortName::of::<A>());
500
501 let command::RemoteGetActor { actor, .. } = msg;
502
503 let (tx, rx) = oneshot::channel();
504
505 let tag = self.next_tag();
506 let ipc_msg = match &actor {
507 ActorRef::Index(actor_id) => message::IpcMessage::node_message(
508 message::NodeMessage::get_actor_by_index(actor_id.as_local(), tag),
509 ),
510 ActorRef::Label(label) => message::IpcMessage::node_message(
511 message::NodeMessage::get_actor_by_label(label.clone(), tag),
512 ),
513 };
514
515 if let Err(e) = self.send_ipc_message(ipc_msg).await {
516 warn!(
517 "Could not send `NodeMessage::GetActor` to remote node: {}",
518 e.report()
519 );
520 if let Err(e) = tx.send_err(e) {
523 warn!(
526 "Could not report the error in `Handler<GetRemoteActor>` to the original \
527 sender: {}",
528 e.report()
529 );
530 }
531 } else {
532 self.message_res_tx_map.insert(tag, (tx, Instant::now()));
533 }
534
535 let decode_context = self.decode_context();
536
537 FutureMessageResult::new(async move {
538 let bytes = rx.await?;
539 let result = <proto_utils::ResultAddress as prost::Message>::decode(bytes)
540 .map_err(DecodeError::other)?;
541 match result.result {
542 Some(proto_utils::ResultAddressType::Ok(actor_id)) => {
543 Address::new_with_decode_context(actor_id, decode_context.as_ref())
544 .map_err(Into::into)
545 }
546 Some(proto_utils::ResultAddressType::Err(e)) => {
547 Err(SessionError::RemoteNodeError(e))
548 }
549 _ => Err(DecodeError::other("missing field `result` in `ResultAddress`").into()),
550 }
551 })
552 }
553}
554
555impl Handler<BinaryMessage> for Session {
556 type Result = ();
557
558 async fn handle(
559 &mut self,
560 msg: BinaryMessage,
561 _ctx: &mut <Self as Actor>::Context,
562 ) -> Self::Result {
563 debug_trace!("Handle command {:?}", msg);
564
565 let BinaryMessage {
566 actor_id,
567 message_id,
568 bytes: message,
569 result_tx,
570 ..
571 } = msg;
572
573 match result_tx {
574 Some(tx) => {
575 let tag = self.next_tag();
578 let ipc_msg = message::IpcMessage::actor_message(message::ActorMessage::send(
579 actor_id, message_id, message, tag,
580 ));
581
582 if let Err(e) = self.send_ipc_message(ipc_msg).await {
583 warn!(
584 "Could not send `ActorMessage` to remote node: {}",
585 e.report()
586 );
587 if let Err(e) = tx.send_err(e) {
590 warn!(
593 "Could not report the error in `Handler<BinaryMessage>` to the \
594 original sender: {}",
595 e.report()
596 );
597 }
598
599 return;
600 }
601
602 self.message_res_tx_map.insert(tag, (tx, Instant::now()));
603 }
604
605 None => {
606 let ipc_msg = message::IpcMessage::actor_message(message::ActorMessage::do_send(
609 actor_id, message_id, message,
610 ));
611
612 if let Err(e) = self.send_ipc_message(ipc_msg).await {
613 warn!(
616 "Could not do_send `ActorMessage` to remote node: {}",
617 e.report()
618 );
619 }
620 }
621 }
622 }
623}
624
625impl Handler<MessageResponse> for Session {
626 type Result = ();
627
628 async fn handle(
629 &mut self,
630 msg: MessageResponse,
631 _ctx: &mut <Self as Actor>::Context,
632 ) -> Self::Result {
633 debug_trace!("Handle command {:?}", msg);
634
635 let MessageResponse { tag, result } = msg;
636
637 let ipc_msg =
638 message::IpcMessage::message_response(message::MessageResponse::new(tag, result));
639
640 if let Err(e) = self.send_ipc_message(ipc_msg).await {
641 warn!(
642 "Could not send `MessageResponse` to remote node: {}",
643 e.report()
644 )
645 }
646 }
647}
648
649#[cfg(test)]
650mod tests {
651 use acktor::RecvError;
652 use anyhow::Result;
653 use pretty_assertions::assert_eq;
654 use tracing_test::traced_test;
655
656 use super::*;
657 use crate::actor_ref::ActorRef;
658 use crate::test_utils::{Echo, start_failing_session};
659
660 #[test]
661 fn test_debug_fmt() {
662 let ok = MessageResponse {
663 tag: 42,
664 result: Ok(Bytes::from_static(b"payload")),
665 };
666 assert_eq!(format!("{:?}", ok), "MessageResponse(42)");
667
668 let err = MessageResponse {
669 tag: 7,
670 result: Err("boom".to_string()),
671 };
672 assert_eq!(format!("{:?}", err), "MessageResponse(7)");
673 }
674
675 #[tokio::test]
679 #[traced_test]
680 async fn test_binary_message_send_reports_failure() -> Result<()> {
681 let session = start_failing_session();
682
683 let (tx, rx) = oneshot::channel::<Bytes>();
684
685 session
686 .do_send(BinaryMessage::send(1, 2, Bytes::new(), tx))
687 .await?;
688
689 let result = rx.await;
690 assert!(
691 matches!(result, Err(RecvError::Other(e)) if e.to_string() == "could not send the outbound remote message to the remote node")
692 );
693
694 assert!(logs_contain("Could not send `ActorMessage` to remote node"));
695
696 Ok(())
697 }
698
699 #[tokio::test]
703 #[traced_test]
704 async fn test_binary_message_do_send_survives_failure() -> Result<()> {
705 let session = start_failing_session();
706
707 session
709 .do_send(BinaryMessage::do_send(1, 2, Bytes::new()))
710 .await?;
711
712 let (tx, rx) = oneshot::channel::<Bytes>();
714 session
715 .do_send(BinaryMessage::send(3, 4, Bytes::new(), tx))
716 .await?;
717
718 assert!(matches!(rx.await, Err(RecvError::Other(_))));
719 assert!(logs_contain(
720 "Could not do_send `ActorMessage` to remote node"
721 ));
722
723 Ok(())
724 }
725
726 #[tokio::test]
729 #[traced_test]
730 async fn test_message_response_survives_failure() -> Result<()> {
731 let session = start_failing_session();
732
733 session
734 .do_send(MessageResponse {
735 tag: 1,
736 result: Ok(Bytes::from_static(b"payload")),
737 })
738 .await?;
739
740 let (tx, rx) = oneshot::channel::<Bytes>();
742 session
743 .do_send(BinaryMessage::send(2, 3, Bytes::new(), tx))
744 .await?;
745
746 assert!(matches!(rx.await, Err(RecvError::Other(_))));
747 assert!(logs_contain(
748 "Could not send `MessageResponse` to remote node"
749 ));
750
751 Ok(())
752 }
753
754 #[tokio::test]
757 #[traced_test]
758 async fn test_remote_create_actor_reports_failure() -> Result<()> {
759 let session = start_failing_session();
760
761 let result = session
762 .send(command::RemoteCreateActor::<Echo>::new("remote-echo", None))
763 .await?
764 .await?;
765
766 assert!(result.is_err());
767 assert!(logs_contain(
768 "Could not send `NodeMessage::CreateActor` to remote node"
769 ));
770
771 Ok(())
772 }
773
774 #[tokio::test]
777 #[traced_test]
778 async fn test_remote_get_actor_reports_failure() -> Result<()> {
779 let session = start_failing_session();
780
781 let result = session
782 .send(command::RemoteGetActor::<Echo>::new(ActorRef::Label(
783 "remote-echo".to_string(),
784 )))
785 .await?
786 .await?;
787
788 assert!(result.is_err());
789 assert!(logs_contain(
790 "Could not send `NodeMessage::GetActor` to remote node"
791 ));
792
793 Ok(())
794 }
795}