Actix의 supervisor는 자신이 생성한 actor가 동작을 멈출 경우, restart 해주는 역할을 한다. “무조건 restart하는 것”이 현재 지원되는 유일한 failure handling 전략으로 보이며, 추후에는 escalation, restart conditionally 등의 전략을 지원할 수 있기를. 아래의 예제는 MyActor를 의도적으로 3번 stop 시키고, 마지막에는 몇번 restart되었는지를 확인하여 표준 출력하는 프로그램이다.

sequenceDiagram
    participant SystemRunner
    participant MyActor
    SystemRunner->>MyActor: PoisonPill
    MyActor->>MyActor: Restarted
    SystemRunner->>MyActor: PoisonPill
    MyActor->>MyActor: Restarted
    SystemRunner->>MyActor: PoisonPill
    MyActor->>MyActor: Restarted
    SystemRunner->>MyActor: ReportNumRestarts
    MyActor->>SystemRunner: 3

actix::Supervised::restarting() 에서 self.num_restarted 의 값이 reset되지 않고 이전 값이 남아 있음을 관심있게 볼 것. 즉 actor를 재시작한다고 해서 가지고 있던 상태를 초기화하는 것이 아니고, 재사용하되 execution context만 초기화된다는 점.

use actix::prelude::*;
use actix::{Actor, Context, Handler, System};

#[derive(Message)]
#[rtype(result = "()")]
struct PoisonPill;

#[derive(Message)]
#[rtype(result = "u32")]
struct ReportNumRestarts;

struct MyActor {
    num_restarted: u32,
}

impl Actor for MyActor {
    type Context = Context<Self>;
}

impl actix::Supervised for MyActor {
    fn restarting(&mut self, ctx: &mut Context<MyActor>) {
        self.num_restarted += 1; // 여기에서 값이 초기화되지 않고 누적됨!
        println!("restarting {} times...", self.num_restarted);
    }
}

impl Handler<PoisonPill> for MyActor {
    type Result = ();

    fn handle(&mut self, _: PoisonPill, ctx: &mut Context<MyActor>) {
        ctx.stop(); // 독약을 먹여서 (!), 의도적으로 중단시킴
    }
}

impl Handler<ReportNumRestarts> for MyActor {
    type Result = u32;

    fn handle(&mut self, msg: ReportNumRestarts, ctx: &mut Self::Context) -> Self::Result {
        self.num_restarted
    }
}

fn main() {
    let mut sys = System::new();
    sys.block_on(async {
        // 현재 Supervisor의 전략은 restart가 유일한 듯.
        let addr = actix::Supervisor::start(|_| MyActor { num_restarted: 0 });
        addr.do_send(PoisonPill);
        addr.do_send(PoisonPill);
        addr.do_send(PoisonPill);
        let num_restarts = addr.send(ReportNumRestarts).await.unwrap();
        println!("MyActor has restarted {} times", num_restarts);
    });

    sys.run();
}

실행하면, 다음과 같다.

$ cargo run
restarting 1 times...
restarting 2 times...
restarting 3 times...
MyActor has restarted 3 times