github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/identitylookup/disthash/manager.go (about) 1 package disthash 2 3 import ( 4 "log/slog" 5 "time" 6 7 "github.com/asynkron/protoactor-go/actor" 8 clustering "github.com/asynkron/protoactor-go/cluster" 9 "github.com/asynkron/protoactor-go/eventstream" 10 ) 11 12 const ( 13 PartitionActivatorActorName = "partition-activator" 14 ) 15 16 type Manager struct { 17 cluster *clustering.Cluster 18 topologySub *eventstream.Subscription 19 placementActor *actor.PID 20 rdv *clustering.Rendezvous 21 } 22 23 func newPartitionManager(c *clustering.Cluster) *Manager { 24 return &Manager{ 25 cluster: c, 26 rdv: clustering.NewRendezvous(), 27 } 28 } 29 30 func (pm *Manager) Start() { 31 pm.cluster.Logger().Info("Started partition manager") 32 system := pm.cluster.ActorSystem 33 34 activatorProps := actor.PropsFromProducer(func() actor.Actor { return newPlacementActor(pm.cluster, pm) }) 35 pm.placementActor, _ = system.Root.SpawnNamed(activatorProps, PartitionActivatorActorName) 36 pm.cluster.Logger().Info("Started partition placement actor") 37 38 pm.topologySub = system.EventStream. 39 Subscribe(func(ev interface{}) { 40 if topology, ok := ev.(*clustering.ClusterTopology); ok { 41 pm.onClusterTopology(topology) 42 } 43 }) 44 } 45 46 func (pm *Manager) Stop() { 47 system := pm.cluster.ActorSystem 48 system.EventStream.Unsubscribe(pm.topologySub) 49 50 err := system.Root.PoisonFuture(pm.placementActor).Wait() 51 if err != nil { 52 pm.cluster.Logger().Error("Failed to shutdown partition placement actor", slog.Any("error", err)) 53 } 54 55 pm.cluster.Logger().Info("Stopped PartitionManager") 56 } 57 58 func (pm *Manager) PidOfActivatorActor(addr string) *actor.PID { 59 return actor.NewPID(addr, PartitionActivatorActorName) 60 } 61 62 func (pm *Manager) onClusterTopology(tplg *clustering.ClusterTopology) { 63 pm.cluster.Logger().Info("onClusterTopology", slog.Uint64("topology-hash", tplg.TopologyHash)) 64 65 for _, m := range tplg.Members { 66 pm.cluster.Logger().Info("Got member", slog.Any("member", m)) 67 } 68 69 pm.rdv = clustering.NewRendezvous() 70 pm.rdv.UpdateMembers(tplg.Members) 71 pm.cluster.ActorSystem.Root.Send(pm.placementActor, tplg) 72 } 73 74 func (pm *Manager) Get(identity *clustering.ClusterIdentity) *actor.PID { 75 ownerAddress := pm.rdv.GetByClusterIdentity(identity) 76 77 if ownerAddress == "" { 78 return nil 79 } 80 81 identityOwnerPid := pm.PidOfActivatorActor(ownerAddress) 82 request := &clustering.ActivationRequest{ 83 ClusterIdentity: identity, 84 RequestId: "", 85 } 86 future := pm.cluster.ActorSystem.Root.RequestFuture(identityOwnerPid, request, 5*time.Second) 87 res, err := future.Result() 88 if err != nil { 89 return nil 90 } 91 typed, ok := res.(*clustering.ActivationResponse) 92 if !ok { 93 return nil 94 } 95 return typed.Pid 96 }