github.com/asynkron/protoactor-go@v0.0.0-20240308120642-ef91a6abee75/cluster/identitylookup/disthash/placement_actor.go (about)

     1  package disthash
     2  
     3  import (
     4  	"github.com/asynkron/protoactor-go/actor"
     5  	clustering "github.com/asynkron/protoactor-go/cluster"
     6  	"log/slog"
     7  )
     8  
     9  type GrainMeta struct {
    10  	ID  *clustering.ClusterIdentity
    11  	PID *actor.PID
    12  }
    13  
    14  type placementActor struct {
    15  	cluster          *clustering.Cluster
    16  	partitionManager *Manager
    17  	actors           map[string]GrainMeta
    18  }
    19  
    20  func newPlacementActor(c *clustering.Cluster, pm *Manager) *placementActor {
    21  	return &placementActor{
    22  		cluster:          c,
    23  		partitionManager: pm,
    24  		actors:           map[string]GrainMeta{},
    25  	}
    26  }
    27  
    28  func (p *placementActor) Receive(ctx actor.Context) {
    29  	switch msg := ctx.Message().(type) {
    30  	case *actor.Started:
    31  		ctx.Logger().Info("Placement actor started")
    32  	case *actor.Stopping:
    33  		ctx.Logger().Info("Placement actor stopping")
    34  		p.onStopping(ctx)
    35  	case *actor.Stopped:
    36  		ctx.Logger().Info("Placement actor stopped")
    37  	case *actor.Terminated:
    38  		p.onTerminated(msg, ctx)
    39  	case *clustering.ActivationRequest:
    40  		p.onActivationRequest(msg, ctx)
    41  	case *clustering.ClusterTopology:
    42  		p.onClusterTopology(msg, ctx)
    43  	default:
    44  		ctx.Logger().Error("Invalid message", slog.Any("message", msg), slog.Any("sender", ctx.Sender()))
    45  	}
    46  }
    47  
    48  func (p *placementActor) onTerminated(msg *actor.Terminated, ctx actor.Context) {
    49  	found, key, meta := p.pidToMeta(msg.Who)
    50  
    51  	activationTerminated := &clustering.ActivationTerminated{
    52  		Pid:             msg.Who,
    53  		ClusterIdentity: meta.ID,
    54  	}
    55  	p.partitionManager.cluster.MemberList.BroadcastEvent(activationTerminated, true)
    56  
    57  	if found {
    58  		delete(p.actors, *key)
    59  	}
    60  }
    61  
    62  func (p *placementActor) onStopping(ctx actor.Context) {
    63  	futures := make(map[string]*actor.Future, len(p.actors))
    64  
    65  	for key, meta := range p.actors {
    66  		futures[key] = ctx.PoisonFuture(meta.PID)
    67  	}
    68  
    69  	for key, future := range futures {
    70  		err := future.Wait()
    71  		if err != nil {
    72  			ctx.Logger().Error("Failed to poison actor", slog.String("identity", key), slog.Any("error", err))
    73  		}
    74  	}
    75  }
    76  
    77  func (p *placementActor) onActivationRequest(msg *clustering.ActivationRequest, ctx actor.Context) {
    78  	key := msg.ClusterIdentity.AsKey()
    79  	meta, found := p.actors[key]
    80  	if found {
    81  		response := &clustering.ActivationResponse{
    82  			Pid: meta.PID,
    83  		}
    84  		ctx.Respond(response)
    85  		return
    86  	}
    87  
    88  	clusterKind := p.cluster.GetClusterKind(msg.ClusterIdentity.Kind)
    89  	if clusterKind == nil {
    90  		ctx.Logger().Error("Unknown cluster kind", slog.String("kind", msg.ClusterIdentity.Kind))
    91  
    92  		// TODO: what to do here?
    93  		ctx.Respond(nil)
    94  		return
    95  	}
    96  
    97  	props := clustering.WithClusterIdentity(clusterKind.Props, msg.ClusterIdentity)
    98  
    99  	pid := ctx.SpawnPrefix(props, msg.ClusterIdentity.Identity)
   100  
   101  	p.actors[key] = GrainMeta{
   102  		ID:  msg.ClusterIdentity,
   103  		PID: pid,
   104  	}
   105  
   106  	response := &clustering.ActivationResponse{
   107  		Pid: pid,
   108  	}
   109  
   110  	ctx.Respond(response)
   111  }
   112  
   113  func (p *placementActor) pidToMeta(pid *actor.PID) (bool, *string, *GrainMeta) {
   114  	for k, v := range p.actors {
   115  		if v.PID == pid {
   116  			return true, &k, &v
   117  		}
   118  	}
   119  	return false, nil, nil
   120  }
   121  
   122  func (p *placementActor) onClusterTopology(msg *clustering.ClusterTopology, ctx actor.Context) {
   123  	rdv := clustering.NewRendezvous()
   124  	rdv.UpdateMembers(msg.Members)
   125  	myAddress := p.cluster.ActorSystem.Address()
   126  	for identity, meta := range p.actors {
   127  		ownerAddress := rdv.GetByIdentity(identity)
   128  		if ownerAddress == myAddress {
   129  
   130  			ctx.Logger().Debug("Actor stays", slog.String("identity", identity), slog.String("owner", ownerAddress), slog.String("me", myAddress))
   131  			continue
   132  		}
   133  
   134  		ctx.Logger().Debug("Actor moved", slog.String("identity", identity), slog.String("owner", ownerAddress), slog.String("me", myAddress))
   135  
   136  		ctx.Poison(meta.PID)
   137  	}
   138  }