Created
November 17, 2025 11:26
-
-
Save doitian/e4a9f74d79f533617bb7c491dd809571 to your computer and use it in GitHub Desktop.
Ractor FSM
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #[derive(Debug, Clone)] | |
| enum ConnectionState { | |
| Disconnected, | |
| Connecting { attempts: u32 }, | |
| Connected { session_id: String }, | |
| Reconnecting { last_session: String }, | |
| } | |
| enum FSMMessage { | |
| Connect, | |
| ConnectionSucceeded(String), | |
| ConnectionFailed, | |
| Disconnect, | |
| GetState(RpcReplyPort<ConnectionState>), | |
| } | |
| struct ConnectionFSM; | |
| #[cfg_attr(feature = "async-trait", ractor::async_trait)] | |
| impl Actor for ConnectionFSM { | |
| type Msg = FSMMessage; | |
| type State = ConnectionState; | |
| type Arguments = (); | |
| async fn pre_start( | |
| &self, | |
| _myself: ActorRef<Self::Msg>, | |
| _args: (), | |
| ) -> Result<Self::State, ActorProcessingErr> { | |
| Ok(ConnectionState::Disconnected) | |
| } | |
| async fn handle( | |
| &self, | |
| myself: ActorRef<Self::Msg>, | |
| message: Self::Msg, | |
| state: &mut Self::State, | |
| ) -> Result<(), ActorProcessingErr> { | |
| let new_state = match (&*state, message) { | |
| // Transitions from Disconnected | |
| (ConnectionState::Disconnected, FSMMessage::Connect) => { | |
| // Entry action: start connection attempt | |
| tracing::info!("Starting connection..."); | |
| Some(ConnectionState::Connecting { attempts: 1 }) | |
| } | |
| // Transitions from Connecting | |
| (ConnectionState::Connecting { attempts }, FSMMessage::ConnectionSucceeded(session_id)) => { | |
| tracing::info!("Connected with session: {}", session_id); | |
| Some(ConnectionState::Connected { session_id }) | |
| } | |
| (ConnectionState::Connecting { attempts }, FSMMessage::ConnectionFailed) if *attempts < 3 => { | |
| tracing::warn!("Connection failed, retrying... (attempt {})", attempts + 1); | |
| Some(ConnectionState::Connecting { attempts: attempts + 1 }) | |
| } | |
| (ConnectionState::Connecting { .. }, FSMMessage::ConnectionFailed) => { | |
| tracing::error!("Connection failed after max attempts"); | |
| Some(ConnectionState::Disconnected) | |
| } | |
| // Transitions from Connected | |
| (ConnectionState::Connected { session_id }, FSMMessage::Disconnect) => { | |
| tracing::info!("Disconnecting session: {}", session_id); | |
| Some(ConnectionState::Disconnected) | |
| } | |
| // Query handling (no state change) | |
| (current, FSMMessage::GetState(reply)) => { | |
| let _ = reply.send(current.clone()); | |
| None // No state change | |
| } | |
| // Invalid transitions | |
| _ => { | |
| tracing::warn!("Invalid state transition attempted"); | |
| None | |
| } | |
| }; | |
| if let Some(next_state) = new_state { | |
| *state = next_state; | |
| } | |
| Ok(()) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment