github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/remote/activator_actor.go (about) 1 package remote 2 3 import ( 4 "errors" 5 "fmt" 6 "log/slog" 7 "time" 8 9 "github.com/asynkron/protoactor-go/actor" 10 ) 11 12 // Register a known actor props by name 13 func (r *Remote) Register(kind string, props *actor.Props) { 14 r.kinds[kind] = props 15 } 16 17 // GetKnownKinds returns a slice of known actor "Kinds" 18 func (r *Remote) GetKnownKinds() []string { 19 keys := make([]string, 0, len(r.kinds)) 20 for k := range r.kinds { 21 keys = append(keys, k) 22 } 23 return keys 24 } 25 26 type activator struct { 27 remote *Remote 28 } 29 30 // ErrActivatorUnavailable : this error will not panic the Activator. 31 // It simply tells Partition this Activator is not available 32 // Partition will then find next available Activator to spawn 33 var ErrActivatorUnavailable = &ActivatorError{ResponseStatusCodeUNAVAILABLE.ToInt32(), true} 34 35 type ActivatorError struct { 36 Code int32 37 DoNotPanic bool 38 } 39 40 func (e *ActivatorError) Error() string { 41 return fmt.Sprint(e.Code) 42 } 43 44 // ActivatorForAddress returns a PID for the activator at the given address 45 func (r *Remote) ActivatorForAddress(address string) *actor.PID { 46 pid := actor.NewPID(address, "activator") 47 return pid 48 } 49 50 // SpawnFuture spawns a remote actor and returns a Future that completes once the actor is started 51 func (r *Remote) SpawnFuture(address, name, kind string, timeout time.Duration) *actor.Future { 52 activator := r.ActivatorForAddress(address) 53 f := r.actorSystem.Root.RequestFuture(activator, &ActorPidRequest{ 54 Name: name, 55 Kind: kind, 56 }, timeout) 57 return f 58 } 59 60 // Spawn spawns a remote actor of a given type at a given address 61 func (r *Remote) Spawn(address, kind string, timeout time.Duration) (*ActorPidResponse, error) { 62 return r.SpawnNamed(address, "", kind, timeout) 63 } 64 65 // SpawnNamed spawns a named remote actor of a given type at a given address 66 func (r *Remote) SpawnNamed(address, name, kind string, timeout time.Duration) (*ActorPidResponse, error) { 67 res, err := r.SpawnFuture(address, name, kind, timeout).Result() 68 if err != nil { 69 return nil, err 70 } 71 switch msg := res.(type) { 72 case *ActorPidResponse: 73 return msg, nil 74 default: 75 return nil, errors.New("remote: Unknown response when remote activating") 76 } 77 } 78 79 func newActivatorActor(remote *Remote) actor.Producer { 80 return func() actor.Actor { 81 return &activator{ 82 remote: remote, 83 } 84 } 85 } 86 87 func (a *activator) Receive(context actor.Context) { 88 switch msg := context.Message().(type) { 89 case *actor.Started: 90 context.Logger().Info("Started Activator") 91 case *Ping: 92 context.Respond(&Pong{}) 93 case *ActorPidRequest: 94 props, exist := a.remote.kinds[msg.Kind] 95 96 // if props not exist, return error and panic 97 if !exist { 98 response := &ActorPidResponse{ 99 StatusCode: ResponseStatusCodeERROR.ToInt32(), 100 } 101 context.Respond(response) 102 panic(fmt.Errorf("no Props found for kind %s", msg.Kind)) 103 } 104 105 name := msg.Name 106 107 // unnamed actor, assign auto ExtensionID 108 if name == "" { 109 name = context.ActorSystem().ProcessRegistry.NextId() 110 } 111 112 pid, err := context.SpawnNamed(props, "Remote$"+name) 113 114 if err == nil { 115 response := &ActorPidResponse{Pid: pid} 116 context.Respond(response) 117 } else if err == actor.ErrNameExists { 118 response := &ActorPidResponse{ 119 Pid: pid, 120 StatusCode: ResponseStatusCodePROCESSNAMEALREADYEXIST.ToInt32(), 121 } 122 context.Respond(response) 123 } else if aErr, ok := err.(*ActivatorError); ok { 124 response := &ActorPidResponse{ 125 StatusCode: aErr.Code, 126 } 127 context.Respond(response) 128 if !aErr.DoNotPanic { 129 panic(err) 130 } 131 } else { 132 response := &ActorPidResponse{ 133 StatusCode: ResponseStatusCodeERROR.ToInt32(), 134 } 135 context.Respond(response) 136 panic(err) 137 } 138 case actor.SystemMessage, actor.AutoReceiveMessage: 139 // ignore 140 default: 141 context.Logger().Error("Activator received unknown message", slog.Any("message", msg)) 142 } 143 }