implement cluster stubs: cross-node routing, rebalancing, and actor migration #158

Open
HugoNijhuis wants to merge 3 commits from implement-cluster-stubs into main
3 changed files with 32 additions and 12 deletions
Showing only changes of commit 443425d6e3 - Show all commits
+20 -5
View File
@@ -151,8 +151,10 @@ func (dvm *DistributedVM) SendMessage(message RuntimeMessage) error {
// routeMessageToNode sends a message to another node for delivery to the target actor // routeMessageToNode sends a message to another node for delivery to the target actor
func (dvm *DistributedVM) routeMessageToNode(actorID string, message RuntimeMessage) error { func (dvm *DistributedVM) routeMessageToNode(actorID string, message RuntimeMessage) error {
hops := 0 hops := 0
var body map[string]interface{}
if mp, ok := message.(*MessagePayload); ok { if mp, ok := message.(*MessagePayload); ok {
hops = mp.Hops hops = mp.Hops
body = mp.Body
} }
if hops >= MaxRouteHops { if hops >= MaxRouteHops {
dvm.cluster.logger.Printf("Dropping message for actor %s: exceeded max hops (%d)", actorID, MaxRouteHops) dvm.cluster.logger.Printf("Dropping message for actor %s: exceeded max hops (%d)", actorID, MaxRouteHops)
@@ -167,6 +169,7 @@ func (dvm *DistributedVM) routeMessageToNode(actorID string, message RuntimeMess
TargetActorID: actorID, TargetActorID: actorID,
Type: message.GetType(), Type: message.GetType(),
Hops: hops + 1, Hops: hops + 1,
Body: body,
}, },
Timestamp: time.Now(), Timestamp: time.Now(),
} }
@@ -219,8 +222,10 @@ func (dvm *DistributedVM) handleClusterMessage(msg *nats.Msg) {
dvm.localRuntime.LoadModel(&model) dvm.localRuntime.LoadModel(&model)
case "route_message": case "route_message":
// Handle message routing from other nodes if clusterMsg.From == dvm.nodeID {
// Re-marshal and unmarshal to convert map[string]interface{} to concrete type return
}
payloadBytes, err := json.Marshal(clusterMsg.Payload) payloadBytes, err := json.Marshal(clusterMsg.Payload)
if err != nil { if err != nil {
return return
@@ -236,11 +241,16 @@ func (dvm *DistributedVM) handleClusterMessage(msg *nats.Msg) {
} }
targetActor := message.TargetActorID targetActor := message.TargetActorID
msg := &MessagePayload{
TargetActorID: targetActor,
Type: message.Type,
Hops: message.Hops,
Body: message.Body,
}
if dvm.IsLocalActor(targetActor) { if dvm.IsLocalActor(targetActor) {
dvm.localRuntime.SendMessage(&message) dvm.localRuntime.SendMessage(msg)
} else { } else {
// Relay to the correct node dvm.routeMessageToNode(targetActor, msg)
dvm.routeMessageToNode(targetActor, &message)
} }
case "rebalance": case "rebalance":
@@ -260,6 +270,11 @@ func (dvm *DistributedVM) handleRebalanceRequest(msg ClusterMessage) {
return return
} }
if dvm.cluster.shardMap == nil {
dvm.cluster.logger.Printf("Shard map is nil, skipping rebalance")
return
}
payloadBytes, err := json.Marshal(msg.Payload) payloadBytes, err := json.Marshal(msg.Payload)
if err != nil { if err != nil {
dvm.cluster.logger.Printf("Failed to marshal rebalance payload: %v", err) dvm.cluster.logger.Printf("Failed to marshal rebalance payload: %v", err)
+8 -4
View File
@@ -195,10 +195,12 @@ func (chp *ConsistentHashPlacement) PlaceActor(actorID string, shardMap *ShardMa
node := ring.GetNode(actorID) node := ring.GetNode(actorID)
if node == "" { if node == "" {
sortedNodeIDs := make([]string, 0, len(nodes))
for nodeID := range nodes { for nodeID := range nodes {
return nodeID, nil sortedNodeIDs = append(sortedNodeIDs, nodeID)
} }
return "", fmt.Errorf("failed to place actor") sort.Strings(sortedNodeIDs)
return sortedNodeIDs[0], nil
} }
return node, nil return node, nil
@@ -231,10 +233,12 @@ func (chp *ConsistentHashPlacement) RebalanceShards(currentMap *ShardMap, nodes
for shardID := range currentMap.Shards { for shardID := range currentMap.Shards {
primaryNode := ring.GetNode(fmt.Sprintf("shard-%d", shardID)) primaryNode := ring.GetNode(fmt.Sprintf("shard-%d", shardID))
if primaryNode == "" { if primaryNode == "" {
sortedNodeIDs := make([]string, 0, len(nodes))
for nodeID := range nodes { for nodeID := range nodes {
primaryNode = nodeID sortedNodeIDs = append(sortedNodeIDs, nodeID)
break
} }
sort.Strings(sortedNodeIDs)
primaryNode = sortedNodeIDs[0]
} }
var replicaNodes []string var replicaNodes []string
+4 -3
View File
@@ -197,9 +197,10 @@ const MaxRouteHops = 10
// MessagePayload is a concrete type for JSON-unmarshaling RuntimeMessage payloads. // MessagePayload is a concrete type for JSON-unmarshaling RuntimeMessage payloads.
// Use this when receiving message data over the network. // Use this when receiving message data over the network.
type MessagePayload struct { type MessagePayload struct {
TargetActorID string `json:"targetActorId"` TargetActorID string `json:"targetActorId"`
Type string `json:"type"` Type string `json:"type"`
Hops int `json:"hops,omitempty"` Hops int `json:"hops,omitempty"`
Body map[string]interface{} `json:"body,omitempty"`
} }
// GetTargetActorID implements RuntimeMessage // GetTargetActorID implements RuntimeMessage