Skip to main content

acktor_ipc/
session.rs

1//! Per-connection session actor.
2//!
3//! A [`Session`] wraps a single [`IpcConnection`] and mediates all traffic over it: routing
4//! inbound frames to local actors, forwarding outbound messages from [`RemoteAddress`]es, and
5//! tracking pending request tags for response correlation. Sessions are owned by a
6//! [`Node`][crate::node::Node] and are created through it rather than directly.
7//!
8
9use std::fmt::{self, Debug};
10use std::result::Result as StdResult;
11
12use ahash::HashMap;
13use bytes::Bytes;
14use futures_util::{FutureExt, TryFutureExt};
15use tokio::time::{Duration, Instant};
16use tracing::{Instrument, info, warn};
17
18use acktor::{
19    Actor, ActorContext, ActorId, Address, ErrorReport, Handler, Message, Recipient, Sender,
20    SenderId, channel::oneshot, message::FutureMessageResult, utils::debug_trace,
21};
22use acktor_ipc_proto::{actor_message, ipc_message, node_message, utils as proto_utils};
23
24use crate::actor_handle::ActorHandle;
25use crate::codec::{Decode, DecodeContext, Encode};
26use crate::errors::{DecodeError, SessionError};
27use crate::ipc_method::IpcConnection;
28use crate::node::{
29    LabelMap,
30    factory::{self, Factory},
31};
32use crate::remote_actor::RemoteActorRegistry;
33use crate::remote_address::RemoteAddress;
34use crate::remote_message::RemoteMessage;
35
36pub mod command;
37
38mod session_handle;
39pub use session_handle::SessionHandle;
40
41mod context;
42use context::SessionContext;
43
44type Result<T> = StdResult<T, SessionError>;
45
46#[derive(Message)]
47#[result_type(())]
48struct ActorMessageResponse {
49    tag: u64,
50    result: StdResult<Bytes, String>,
51}
52
53impl Debug for ActorMessageResponse {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        f.debug_tuple("ActorMessageResponse")
56            .field(&self.tag)
57            .finish()
58    }
59}
60
61#[derive(Message)]
62#[result_type(())]
63struct CreateActorResponse {
64    tag: u64,
65    result: StdResult<ActorId, String>,
66}
67
68impl Debug for CreateActorResponse {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        f.debug_tuple("CreateActorResponse")
71            .field(&self.tag)
72            .finish()
73    }
74}
75
76/// An actor which manages the IPC connection to a remote endpoint.
77pub struct Session {
78    connection: Box<dyn IpcConnection>,
79    factory: Address<Factory>,
80    registry: RemoteActorRegistry,
81    label_map: LabelMap,
82    tag: u64, // unique tag generator
83    decode_context: Option<DecodeContext>,
84    node_msg_res_tx_map: HashMap<u64, (oneshot::Sender<Result<RemoteAddress>>, Instant)>,
85    actor_msg_res_tx_map: HashMap<u64, (oneshot::Sender<Bytes>, Instant)>,
86}
87
88impl Session {
89    /// Constructs a new [`Session`]. Called internally by [`Node`][crate::node::Node] when it
90    /// accepts or initiates an IPC connection; not intended for direct use.
91    pub(crate) fn new(
92        connection: Box<dyn IpcConnection>,
93        factory: Address<Factory>,
94        registry: RemoteActorRegistry,
95        label_map: LabelMap,
96    ) -> Self {
97        Self {
98            connection,
99            factory,
100            registry,
101            label_map,
102            tag: 0,
103            decode_context: None,
104            actor_msg_res_tx_map: HashMap::default(),
105            node_msg_res_tx_map: HashMap::default(),
106        }
107    }
108
109    fn cleanup_expired_response_tx(&mut self) {
110        let now = Instant::now();
111
112        self.node_msg_res_tx_map
113            .retain(|_, (_, timestamp)| now.duration_since(*timestamp) < Duration::from_secs(30));
114        self.actor_msg_res_tx_map
115            .retain(|_, (_, timestamp)| now.duration_since(*timestamp) < Duration::from_secs(30));
116    }
117
118    fn next_tag(&mut self) -> u64 {
119        let tag = self.tag;
120        self.tag = self.tag.wrapping_add(1);
121        tag
122    }
123
124    async fn send_ipc_message(&mut self, ipc_msg: ipc_message::IpcMessage) -> Result<()> {
125        let encoded_ipc_msg = ipc_msg.encode_to_bytes(None)?;
126        self.connection
127            .send(encoded_ipc_msg)
128            .await
129            .map_err(SessionError::SendOutboundMessageFailed)?;
130
131        Ok(())
132    }
133
134    fn decode_context(&self) -> Result<&DecodeContext> {
135        self.decode_context
136            .as_ref()
137            .ok_or_else(|| DecodeError::MissingDecodeContext.into())
138    }
139
140    fn find_actor(&self, actor: &ActorHandle) -> Result<Recipient<RemoteMessage>> {
141        match actor {
142            ActorHandle::Index(actor_id) => {
143                if actor_id.is_remote() {
144                    return Err(DecodeError::DecodeRemoteAddress.into());
145                }
146
147                self.registry
148                    .get(*actor_id)
149                    .ok_or_else(|| SessionError::ActorNotFound(actor_id.to_string()))
150            }
151
152            ActorHandle::Label(label) => self
153                .label_map
154                .get(label)
155                .ok_or_else(|| SessionError::ActorNotFound(label.clone()))
156                .and_then(|actor_id| {
157                    // the registry may have reaped the entry out from under the label_map
158                    // (the factory sweep lags by up to 30s); report the original label so the
159                    // caller gets a stable diagnostic
160                    self.registry
161                        .get(*actor_id)
162                        .ok_or_else(|| SessionError::ActorNotFound(label.clone()))
163                }),
164        }
165    }
166
167    async fn handle_node_message(
168        &mut self,
169        message: node_message::NodeMessage,
170        ctx: &mut <Self as Actor>::Context,
171    ) -> Result<()> {
172        match message.message {
173            Some(node_message::MessageType::CreateActor(node_message::CreateActor {
174                label,
175                r#type,
176                config,
177                tag,
178            })) => {
179                let factory = self.factory.clone();
180                let address = ctx.address();
181
182                // spawn a task to handle the potentially time consuming actor creation process
183                tokio::spawn(
184                    async move {
185                        factory
186                            .send(factory::CreateActor {
187                                label,
188                                r#type,
189                                config,
190                            })
191                            .await?
192                            .await?
193                    }
194                    .then(move |result| async move {
195                        // send the result back to this actor with CreateActorResponse message
196                        // the IpcConnection can not be cloned into the spawned task without a
197                        // Arc<Mutex<..>>, so we convert this into a sequential message handling
198                        // process
199                        address
200                            .do_send(CreateActorResponse {
201                                tag,
202                                result: result.map_err(|e| e.report()),
203                            })
204                            .await
205                    })
206                    .inspect_err(|e| {
207                        // we can not do much if sending the response back to this actor fails,
208                        // just log it
209                        warn!(
210                            "Could not send `NodeMessageResponse::CreateActor` to remote peer: {}",
211                            e.report()
212                        );
213                    })
214                    .in_current_span(),
215                );
216
217                Ok(())
218            }
219
220            Some(node_message::MessageType::GetActor(node_message::GetActor { actor, tag })) => {
221                // convert proto::utils::ActorHandle to crate::ActorHandle, ugly
222                let actor = match actor {
223                    Some(proto_utils::ActorHandle { handle }) => match handle {
224                        Some(proto_utils::ActorHandleType::Index(actor_id)) => {
225                            ActorHandle::Index(actor_id)
226                        }
227                        Some(proto_utils::ActorHandleType::Label(label)) => {
228                            ActorHandle::Label(label)
229                        }
230                        None => {
231                            return Err(DecodeError::from(
232                                "missing field `handle` in `ActorHandle`",
233                            )
234                            .into());
235                        }
236                    },
237
238                    _ => {
239                        return Err(DecodeError::from(
240                            "missing field `actor` in `NodeMessage::GetActor`",
241                        )
242                        .into());
243                    }
244                };
245
246                let result = self.find_actor(&actor).map(|recipient| recipient.index());
247
248                let ipc_msg = ipc_message::IpcMessage::node_message_response(
249                    node_message::NodeMessageResponse::get_actor(
250                        tag,
251                        result.map_err(|e| e.report()),
252                    ),
253                );
254
255                self.send_ipc_message(ipc_msg).await.inspect_err(|e| {
256                    // we can not do much if sending the response back to the remote peer fails,
257                    // just log it
258                    warn!(
259                        "Could not send `NodeMessageResponse::GetActor` to remote peer: {}",
260                        e.report()
261                    );
262                })
263            }
264
265            _ => Err(DecodeError::from("missing field `message` in `NodeMessage`").into()),
266        }
267    }
268
269    fn _handle_node_message_response(
270        &mut self,
271        tag: u64,
272        result: Option<node_message::ResultType>,
273        name: &str,
274    ) -> Result<()> {
275        let (sender, _) = self
276            .node_msg_res_tx_map
277            .remove(&tag)
278            // if the tag is not found in the map, we do not know who to send the result to, and
279            // we do not kown who to report the error to either, so just return an error and the
280            // session's context will log it
281            .ok_or(SessionError::InvalidNodeMsgResTxTag(tag))?;
282
283        // remote error and processing error should be reported to the original sender who is
284        // waiting for a `Result<RemoteAddress, SessionError>`
285        let result = match result {
286            Some(node_message::ResultType::ActorId(actor_id)) => match self.decode_context() {
287                Ok(ctx) => ctx.create_remote_address(actor_id).map_err(Into::into),
288                Err(e) => Err(e),
289            },
290            Some(node_message::ResultType::Err(e)) => Err(SessionError::RemotePeerError(e)),
291
292            _ => Err(DecodeError::from(format!(
293                "missing field `result` in `NodeMessageResponse::{}`",
294                name
295            ))
296            .into()),
297        };
298
299        sender
300            .send(result)
301            // we can not do much if sending the result back to the original sender fails, just
302            // return an error and the session's context will log it
303            .map_err(|_| SessionError::ForwardNodeMsgResFailed)
304    }
305
306    fn handle_node_message_response(
307        &mut self,
308        response: node_message::NodeMessageResponse,
309        _ctx: &mut <Self as Actor>::Context,
310    ) -> Result<()> {
311        let tag = response.tag;
312
313        match response.response {
314            Some(node_message::ResponseType::CreateActor(node_message::ResultRemoteAddress {
315                result,
316            })) => self._handle_node_message_response(tag, result, "CreateActor"),
317
318            Some(node_message::ResponseType::GetActor(node_message::ResultRemoteAddress {
319                result,
320            })) => self._handle_node_message_response(tag, result, "GetActor"),
321
322            _ => Err(DecodeError::from("missing field `response` in `NodeMessageResponse`").into()),
323        }
324    }
325
326    async fn handle_actor_message(
327        &mut self,
328        message: actor_message::ActorMessage,
329        ctx: &mut <Self as Actor>::Context,
330    ) -> Result<()> {
331        let actor_message::ActorMessage {
332            actor_id,
333            message_id,
334            message,
335            tag,
336        } = message;
337
338        match tag {
339            Some(tag) => {
340                // send
341
342                let address = ctx.address();
343                let recipient = self.find_actor(&ActorHandle::Index(actor_id));
344                let decode_context = self.decode_context().cloned();
345                let (tx, rx) = oneshot::channel();
346
347                // spawn a task to handle the potentially time consuming message handling process
348                tokio::spawn(
349                    async move {
350                        recipient?
351                            .do_send(
352                                RemoteMessage::send(actor_id, message_id, message, tx)
353                                    .with_context(decode_context?),
354                            )
355                            .await
356                            .map_err(|e| SessionError::ForwardInboundMessageFailed(e.into()))?;
357
358                        let result = rx
359                            .await
360                            .map_err(|e| SessionError::HandleInboundMessageFailed(e.into()))?;
361
362                        Ok::<Bytes, SessionError>(result)
363                    }
364                    .then(move |result| async move {
365                        // send the result back to this actor with ActorMessageResponse message
366                        // the IpcConnection can not be cloned into the spawned task without a
367                        // Arc<Mutex<..>>, so we convert this into a sequential message handling
368                        // process
369                        address
370                            .do_send(ActorMessageResponse {
371                                tag,
372                                result: result.map_err(|e| e.report()),
373                            })
374                            .await
375                    })
376                    .inspect_err(|e| {
377                        // we can not do much if sending the response back to this actor fails,
378                        // just log it
379                        warn!(
380                            "Could not send `ActorMessageResponse` to remote peer: {}",
381                            e.report()
382                        );
383                    })
384                    .in_current_span(),
385                );
386
387                Ok(())
388            }
389
390            None => {
391                // do_send
392
393                // sender has explicitly indicated that it does not care about the result of this
394                // message, so we just return the error and the session's context will log it
395                self.find_actor(&ActorHandle::Index(actor_id))?
396                    .do_send(
397                        RemoteMessage::do_send(actor_id, message_id, message)
398                            .with_context(self.decode_context()?.clone()),
399                    )
400                    .await
401                    .map_err(|e| SessionError::ForwardInboundMessageFailed(e.into()))
402            }
403        }
404    }
405
406    async fn handle_actor_message_response(
407        &mut self,
408        response: actor_message::ActorMessageResponse,
409        _ctx: &mut <Self as Actor>::Context,
410    ) -> Result<()> {
411        let tag = response.tag;
412
413        let (sender, _) = self
414            .actor_msg_res_tx_map
415            .remove(&tag)
416            // if the tag is not found in the map, we do not know who to send the result
417            // to, and we do not kown who to report the error to either, so just return
418            // an error and the session's context will log it
419            .ok_or(SessionError::InvalidActorMsgResTxTag(tag))?;
420
421        // remote error and processing error should be reported to the original sender who is
422        // waiting for a `Result<M::Result, RecvError>`
423        let result: Result<_> = match response.response {
424            Some(actor_message::ResponseType::Ok(bytes)) => Ok(bytes),
425            Some(actor_message::ResponseType::Err(err)) => Err(SessionError::RemotePeerError(err)),
426            None => {
427                Err(DecodeError::from("missing field `response` in `ActorMessageResponse`").into())
428            }
429        };
430
431        // we can not do much if sending the result back to the original sender fails, just return
432        // an error and the session's context will log it
433        match result {
434            Ok(bytes) => sender
435                .send(bytes)
436                .map_err(|_| SessionError::ForwardActorMessageResFailed),
437            Err(e) => sender
438                .send_err(e)
439                .map_err(|_| SessionError::ForwardActorMessageResFailed),
440        }
441    }
442
443    async fn handle_ipc_message(
444        &mut self,
445        message: Bytes,
446        ctx: &mut <Self as Actor>::Context,
447    ) -> Result<()> {
448        let ipc_message = ipc_message::IpcMessage::decode(message, None)?;
449
450        match ipc_message.message {
451            Some(ipc_message::IpcMessageType::NodeMessage(message)) => {
452                self.handle_node_message(message, ctx).await
453            }
454            Some(ipc_message::IpcMessageType::NodeMessageResponse(response)) => {
455                self.handle_node_message_response(response, ctx)
456            }
457            Some(ipc_message::IpcMessageType::ActorMessage(message)) => {
458                self.handle_actor_message(message, ctx).await
459            }
460            Some(ipc_message::IpcMessageType::ActorMessageResponse(response)) => {
461                self.handle_actor_message_response(response, ctx).await
462            }
463            _ => Err(DecodeError::from("missing field `message` in `IpcMessage`").into()),
464        }
465    }
466}
467
468impl Actor for Session {
469    type Context = SessionContext;
470    type Error = SessionError;
471
472    async fn post_start(&mut self, ctx: &mut Self::Context) -> Result<()> {
473        info!("Session {} is started", self.connection.peer_endpoint());
474
475        self.decode_context = Some(DecodeContext::new(ctx.address(), self.registry.clone()));
476
477        Ok(())
478    }
479
480    async fn post_stop(&mut self, _ctx: &mut Self::Context) -> Result<()> {
481        self.connection
482            .close()
483            .await
484            .map_err(SessionError::IoError)?;
485
486        info!("Session {} is stopped", self.connection.peer_endpoint());
487
488        Ok(())
489    }
490}
491
492// See `handle_node_message` for what the remote peer actor will do when it receives the
493// `NodeMessage` sent by this handler.
494// See `handle_node_message_response` for how this actor forwards the result to the original
495// sender when it receives the `NodeMessageResponse` from the remote peer actor.
496impl Handler<command::CreateRemoteActor> for Session {
497    type Result = FutureMessageResult<command::CreateRemoteActor>;
498
499    async fn handle(
500        &mut self,
501        msg: command::CreateRemoteActor,
502        _ctx: &mut <Self as Actor>::Context,
503    ) -> Self::Result {
504        debug_trace!("Handle command {:?}", msg);
505
506        let command::CreateRemoteActor {
507            label,
508            r#type,
509            config,
510        } = msg;
511
512        let (tx, rx) = oneshot::channel();
513
514        let tag = self.next_tag();
515        let ipc_msg = ipc_message::IpcMessage::node_message(
516            node_message::NodeMessage::create_actor(label, r#type, config, tag),
517        );
518
519        if let Err(e) = self.send_ipc_message(ipc_msg).await {
520            warn!(
521                "Could not send `NodeMessage::CreateActor` to remote peer: {}",
522                e.report()
523            );
524            // sends the error back to the original sender who is waiting for a
525            // `Result<RemoteAddress, SessionError>`
526            if let Err(e) = tx.send(Err(e)) {
527                // we can not do much if sending the error back to the original sender fails, just
528                // log it
529                warn!(
530                    "Could not report the error in `Handler<CreateRemoteActor>` to original \
531                     sender: {}",
532                    e.report()
533                );
534            }
535        } else {
536            self.node_msg_res_tx_map.insert(tag, (tx, Instant::now()));
537        }
538
539        FutureMessageResult::new(rx.map(|r| r.unwrap_or_else(|e| Err(e.into()))))
540    }
541}
542
543// See `handle_node_message` for what the remote peer actor will do when it receives the
544// `NodeMessage` sent by this handler.
545// See `handle_node_message_response` for how this actor forwards the result to the original
546// sender when it receives the `NodeMessageResponse` from the remote peer actor.
547impl Handler<command::GetRemoteActor> for Session {
548    type Result = FutureMessageResult<command::GetRemoteActor>;
549
550    async fn handle(
551        &mut self,
552        msg: command::GetRemoteActor,
553        _ctx: &mut <Self as Actor>::Context,
554    ) -> Self::Result {
555        debug_trace!("Handle command {:?}", msg);
556
557        let command::GetRemoteActor { actor } = msg;
558
559        let (tx, rx) = oneshot::channel();
560
561        let tag = self.next_tag();
562        let ipc_msg = match &actor {
563            ActorHandle::Index(actor_id) => ipc_message::IpcMessage::node_message(
564                node_message::NodeMessage::get_actor_with_index(*actor_id, tag),
565            ),
566            ActorHandle::Label(label) => ipc_message::IpcMessage::node_message(
567                node_message::NodeMessage::get_actor_with_label(label.clone(), tag),
568            ),
569        };
570
571        if let Err(e) = self.send_ipc_message(ipc_msg).await {
572            warn!(
573                "Could not send `NodeMessage::GetActor` to remote peer: {}",
574                e.report()
575            );
576            // sends the error back to the original sender who is waiting for a
577            // `Result<RemoteAddress, SessionError>`
578            if let Err(e) = tx.send(Err(e)) {
579                // we can not do much if sending the error back to the original sender fails, just
580                // log it
581                warn!(
582                    "Could not report the error in `Handler<GetRemoteActor>` to original sender: \
583                     {}",
584                    e.report()
585                );
586            }
587        } else {
588            self.node_msg_res_tx_map.insert(tag, (tx, Instant::now()));
589        }
590
591        FutureMessageResult::new(rx.map(|r| r.unwrap_or_else(|e| Err(e.into()))))
592    }
593}
594
595// See `handle_actor_message` for what the remote peer actor will do when it receives the
596// `ActorMessage` sent by this handler.
597// See `handle_actor_message_response` for how this actor forwards the result to the original
598// sender when it receives the `ActorMessageResponse` from the remote peer actor.
599impl Handler<RemoteMessage> for Session {
600    type Result = ();
601
602    async fn handle(
603        &mut self,
604        msg: RemoteMessage,
605        _ctx: &mut <Self as Actor>::Context,
606    ) -> Self::Result {
607        debug_trace!("Handle command {:?}", msg);
608
609        let RemoteMessage {
610            actor_id,
611            message_id,
612            message,
613            result_tx,
614            ..
615        } = msg;
616
617        match result_tx {
618            Some(tx) => {
619                // send
620
621                let tag = self.next_tag();
622                let ipc_msg = ipc_message::IpcMessage::actor_message(
623                    actor_message::ActorMessage::send(actor_id, message_id, message, tag),
624                );
625
626                if let Err(e) = self.send_ipc_message(ipc_msg).await {
627                    warn!(
628                        "Could not send `ActorMessage` to remote peer: {}",
629                        e.report()
630                    );
631                    // sends the error back to the original sender who is waiting for a
632                    // `Result<M::Result, RecvError>`, typically a `RemoteAddress`, note that the
633                    // error original sender receives is a `RecvError` because the signature of
634                    // the `Sender` trait so we need to use `send_err` here
635                    if let Err(e) = tx.send_err(e) {
636                        // we can not do much if sending the error back to the original sender
637                        // fails, just log it
638                        warn!(
639                            "Could not report the error in `Handler<RemoteMessage>` to original \
640                             sender: {}",
641                            e.report()
642                        );
643                    }
644
645                    return;
646                }
647
648                self.actor_msg_res_tx_map.insert(tag, (tx, Instant::now()));
649            }
650
651            None => {
652                // do_send
653
654                let ipc_msg = ipc_message::IpcMessage::actor_message(
655                    actor_message::ActorMessage::do_send(actor_id, message_id, message),
656                );
657
658                if let Err(e) = self.send_ipc_message(ipc_msg).await {
659                    // sender has explicitly indicated that it does not care about the result of
660                    // this message, so we just log the error
661                    warn!(
662                        "Could not do_send `ActorMessage` to remote peer: {}",
663                        e.report()
664                    );
665                }
666            }
667        }
668    }
669}
670
671impl Handler<CreateActorResponse> for Session {
672    type Result = ();
673
674    async fn handle(&mut self, msg: CreateActorResponse, _ctx: &mut Self::Context) -> Self::Result {
675        debug_trace!("Handle command {:?}", msg);
676
677        let CreateActorResponse { tag, result } = msg;
678
679        let ipc_msg = ipc_message::IpcMessage::node_message_response(
680            node_message::NodeMessageResponse::create_actor(tag, result),
681        );
682
683        if let Err(e) = self.send_ipc_message(ipc_msg).await {
684            // we can not do much if sending the response back to the remote peer fails, just log
685            // it
686            warn!(
687                "Could not send `NodeMessageResponse::CreateActor` to remote peer: {}",
688                e.report()
689            )
690        }
691    }
692}
693
694impl Handler<ActorMessageResponse> for Session {
695    type Result = ();
696
697    async fn handle(
698        &mut self,
699        msg: ActorMessageResponse,
700        _ctx: &mut <Self as Actor>::Context,
701    ) -> Self::Result {
702        debug_trace!("Handle command {:?}", msg);
703
704        let ActorMessageResponse { tag, result } = msg;
705
706        let ipc_msg = ipc_message::IpcMessage::actor_message_response(
707            actor_message::ActorMessageResponse::new(tag, result),
708        );
709
710        if let Err(e) = self.send_ipc_message(ipc_msg).await {
711            warn!(
712                "Could not send `ActorMessageResponse` to remote peer: {}",
713                e.report()
714            )
715        }
716    }
717}
718
719#[cfg(test)]
720mod tests {
721    use super::*;
722
723    #[test]
724    fn test_debug_str() {
725        let ok = ActorMessageResponse {
726            tag: 42,
727            result: Ok(Bytes::from_static(b"payload")),
728        };
729        assert_eq!(format!("{ok:?}"), "ActorMessageResponse(42)");
730
731        let err = ActorMessageResponse {
732            tag: 7,
733            result: Err("boom".to_string()),
734        };
735        assert_eq!(format!("{err:?}"), "ActorMessageResponse(7)");
736
737        let ok = CreateActorResponse {
738            tag: 42,
739            result: Ok(1234),
740        };
741        assert_eq!(format!("{ok:?}"), "CreateActorResponse(42)");
742
743        let err = CreateActorResponse {
744            tag: 7,
745            result: Err("boom".to_string()),
746        };
747        assert_eq!(format!("{err:?}"), "CreateActorResponse(7)");
748    }
749}