asynkron / protoactor-go

Proto Actor - Ultra fast distributed actors for Go, C# and Java/Kotlin
http://proto.actor
Apache License 2.0
4.97k stars 515 forks source link

How can I make ‘Spawn’ return the error that occurred when 'Started' is received? #1105

Open DustOak opened 2 months ago

DustOak commented 2 months ago

When I spawn an actor, if I need to initialize the actor upon receiving 'Started', and if initialization fails, the actor should not start successfully. How should I write this code? I noticed that after I receive 'Started', the spawn has already returned the Pid. However, I do not want this because I need the actor to stop when initialization fails and have the spawn return an error

rogeralsing commented 2 months ago

I would rather wrap all that in a helper function that handles all of this. e.g. if your code fails to initialize, set some state of the actor to failed. then add a message request+response where you can query that state.

func NewSomeActor() {
        props := actor.PropsFromProducer(......)
        pid := system.Root.Spawn(props)
        res := system.Root.RequestFuture(pid, &SomeRequest {}).Result
        if res == .... {
                 //actor started successfully
        }
        else {
                  //actor failed to initialize
        }
}

Or if possible, you could do it even before proto.actor is involved at all. You can set state in the actor upon producing it.

func() actor.Actor { return &helloActor{
     .....set state here
} }

you could create a function that resolves all deps and returns a Props if successful, or err if failed. That way, you could catch this error before the actor is spawned.

DustOak commented 2 months ago

我宁愿将所有这些包装在一个处理所有这些的帮助程序函数中。例如,如果您的代码无法初始化,请将 actor 的某些状态设置为 failed。然后添加消息 request+response,您可以在其中查询该状态。

func NewSomeActor() {
          props := actor.PropsFromProducer(......)
        pid := system.Root.Spawn(props)
        res := system.Root.RequestFuture(pid, &SomeRequest {}).Result
        if res == .... {
                 //actor started successfully
        }
        else {
                  //actor failed to initialize
        }
}

或者,如果可能的话,你甚至可以在 proto.actor 参与之前就这样做。您可以在生成 actor 时在 actor 中设置状态。

func() actor.Actor { return &helloActor{
     .....set state here
} }

您可以创建一个函数来解析所有 deps,如果成功,则返回 Props,如果失败,则返回 err。这样,您就可以在生成 actor 之前捕获此错误。

I have considered and tried the first method, but I am unable to determine how long the timeout for synchronous requests should be set. If the time is too long, it may affect the overall performance.

DustOak commented 2 months ago

新一期参

For the second approach, I might need to do some things using actor.Context during initialization, so it may not work as well.

DustOak commented 2 months ago

Currently, I am not sure if there will be any issues with the way I am doing it. The code is as follows:

func makeHook(factory BehaviorFactory, args ...any) actor.SpawnFunc {
    return func(actorSystem *actor.ActorSystem, id string, props *actor.Props, parentContext actor.SpawnerContext) (pid *actor.PID, err error) {
        hook := &hook{Behavior: factory(args...), Actor: makeActor(), factory: factory, args: args}
        wait := make(chan error)
        defer close(wait)
        fun := func(ctx actor.Context) {
            hook.update(ctx)
            hook.Errorf("--------------------------------> %s", ctx.Parent())
            hook.Do(func() {
                if err := hook.Init(hook); err != nil {
                    hook.Debugf("behavior init failed: %s", err)
                    hook.Exit()
                    wait <- err
                    return
                }
                hook.Debugf("behavior init ok.")
                wait <- nil
            })
            hook.Receive(ctx)
        }
        props = actor.PropsFromFunc(fun, actor.WithSupervisor(actor.DefaultSupervisorStrategy()))
        if pid, err = actor.DefaultSpawner(actorSystem, id, props, parentContext); err != nil {
            return nil, err
        }
        err = <-wait
        return
    }
}
func Spawn(alias Alias, factory BehaviorFactory, args ...any) (pid Pid, err error) {
    props := (&actor.Props{}).Configure(actor.WithSpawnFunc(makeHook(factory, args...)))
    if pid, err = root.ActorSystem.Root.SpawnNamed(props, string(alias)); err != nil {
        return
    }
    root.Children = append(root.Children, pid)
    return
}