implement cluster stubs: cross-node routing, rebalancing, and actor migration
CI / build (pull_request) Successful in 42s
CI / build (pull_request) Successful in 42s
- Fix ConsistentHashPlacement.PlaceActor() to use consistent hash ring - Implement ConsistentHashPlacement.RebalanceShards() to redistribute shards - Implement ClusterManager.handleRebalanceRequest() with actual rebalancing - Implement ClusterManager.handleMigrationRequest() for actor state transfer - Implement ClusterManager.triggerShardRebalancing() to compute and broadcast - Implement DistributedVM.SendMessage() with cross-node NATS routing - Implement DistributedVM.handleRebalanceRequest() to update shard map - Fix route_message handler to check if actor is local before delivery - Update ConsistentHashPlacement.RebalanceShards() test for new behavior - Add handleShardMapUpdate() and broadcastShardMap() to ClusterManager
This commit is contained in:
+97
-19
@@ -6,6 +6,8 @@ import (
|
||||
"fmt"
|
||||
"hash"
|
||||
"hash/fnv"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MigrationStatus tracks actor migration progress
|
||||
@@ -180,36 +182,112 @@ func (sm *ShardManager) GetReplicationFactor() int {
|
||||
// ConsistentHashPlacement implements PlacementStrategy using consistent hashing
|
||||
type ConsistentHashPlacement struct{}
|
||||
|
||||
// PlaceActor places an actor using consistent hashing
|
||||
// PlaceActor places an actor using the consistent hash ring
|
||||
func (chp *ConsistentHashPlacement) PlaceActor(actorID string, shardMap *ShardMap, nodes map[string]*NodeInfo) (string, error) {
|
||||
if len(nodes) == 0 {
|
||||
return "", fmt.Errorf("no nodes available for placement")
|
||||
}
|
||||
|
||||
// Simple consistent hash placement - in a real implementation,
|
||||
// this would use the consistent hash ring
|
||||
h := sha256.Sum256([]byte(actorID))
|
||||
nodeIndex := binary.BigEndian.Uint32(h[:4]) % uint32(len(nodes))
|
||||
|
||||
i := 0
|
||||
ring := NewConsistentHashRing()
|
||||
for nodeID := range nodes {
|
||||
if i == int(nodeIndex) {
|
||||
ring.AddNode(nodeID)
|
||||
}
|
||||
|
||||
node := ring.GetNode(actorID)
|
||||
if node == "" {
|
||||
for nodeID := range nodes {
|
||||
return nodeID, nil
|
||||
}
|
||||
i++
|
||||
return "", fmt.Errorf("failed to place actor")
|
||||
}
|
||||
|
||||
// Fallback to first node
|
||||
for nodeID := range nodes {
|
||||
return nodeID, nil
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("failed to place actor")
|
||||
return node, nil
|
||||
}
|
||||
|
||||
// RebalanceShards rebalances shards across nodes
|
||||
// RebalanceShards redistributes shards across nodes using consistent hashing
|
||||
func (chp *ConsistentHashPlacement) RebalanceShards(currentMap *ShardMap, nodes map[string]*NodeInfo) (*ShardMap, error) {
|
||||
// This is a simplified implementation
|
||||
// In practice, this would implement sophisticated rebalancing logic
|
||||
return currentMap, nil
|
||||
if len(nodes) == 0 {
|
||||
return nil, fmt.Errorf("no nodes available for rebalancing")
|
||||
}
|
||||
|
||||
ring := NewConsistentHashRing()
|
||||
for nodeID := range nodes {
|
||||
ring.AddNode(nodeID)
|
||||
}
|
||||
|
||||
replicaCount := chp.deriveReplicaCount(currentMap)
|
||||
|
||||
newMap := &ShardMap{
|
||||
Version: currentMap.Version + 1,
|
||||
Shards: make(map[int][]string),
|
||||
Nodes: make(map[string]NodeInfo),
|
||||
UpdateTime: time.Now(),
|
||||
}
|
||||
|
||||
for nodeID, nodeInfo := range nodes {
|
||||
newMap.Nodes[nodeID] = *nodeInfo
|
||||
}
|
||||
|
||||
for shardID := range currentMap.Shards {
|
||||
primaryNode := ring.GetNode(fmt.Sprintf("shard-%d", shardID))
|
||||
if primaryNode == "" {
|
||||
for nodeID := range nodes {
|
||||
primaryNode = nodeID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var replicaNodes []string
|
||||
candidates := make([]string, 0, len(nodes))
|
||||
for nodeID := range nodes {
|
||||
if nodeID != primaryNode {
|
||||
candidates = append(candidates, nodeID)
|
||||
}
|
||||
}
|
||||
sort.Strings(candidates)
|
||||
|
||||
for i := 0; i < replicaCount && len(replicaNodes) < replicaCount; i++ {
|
||||
node := ring.GetNode(fmt.Sprintf("shard-%d-replica-%d", shardID, i))
|
||||
if node != "" && node != primaryNode {
|
||||
found := false
|
||||
for _, existing := range replicaNodes {
|
||||
if existing == node {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
replicaNodes = append(replicaNodes, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(replicaNodes) == 0 && len(candidates) > 0 {
|
||||
replicaNodes = append(replicaNodes, candidates[0])
|
||||
}
|
||||
|
||||
if len(replicaNodes) > replicaCount {
|
||||
replicaNodes = replicaNodes[:replicaCount]
|
||||
}
|
||||
|
||||
shardNodes := []string{primaryNode}
|
||||
shardNodes = append(shardNodes, replicaNodes...)
|
||||
newMap.Shards[shardID] = shardNodes
|
||||
}
|
||||
|
||||
return newMap, nil
|
||||
}
|
||||
|
||||
// deriveReplicaCount extracts the replication factor from the current shard map
|
||||
func (chp *ConsistentHashPlacement) deriveReplicaCount(currentMap *ShardMap) int {
|
||||
maxNodes := 0
|
||||
for _, nodes := range currentMap.Shards {
|
||||
if len(nodes) > maxNodes {
|
||||
maxNodes = len(nodes)
|
||||
}
|
||||
}
|
||||
if maxNodes <= 1 {
|
||||
return 1
|
||||
}
|
||||
return maxNodes - 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user