You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
135 lines
3.1 KiB
135 lines
3.1 KiB
package simulation |
|
|
|
import ( |
|
"hash/fnv" |
|
"math/rand" |
|
) |
|
|
|
type LoadBalancerLogic struct{} |
|
|
|
func (l LoadBalancerLogic) Tick(props map[string]any, queue []*Request, tick int) ([]*Request, bool) { |
|
algorithm := AsString(props["algorithm"]) |
|
|
|
targetIDs, ok := props["_targetIDs"].([]string) |
|
if !ok || len(targetIDs) == 0 || len(queue) == 0 { |
|
return nil, true |
|
} |
|
|
|
output := []*Request{} |
|
|
|
switch algorithm { |
|
case "least-connection": |
|
queueSizesRaw, ok := props["_queueSizes"].(map[string]interface{}) |
|
|
|
if !ok { |
|
return nil, true |
|
} |
|
|
|
for _, req := range queue { |
|
minTarget := targetIDs[0] |
|
minSize := int(AsFloat64(queueSizesRaw[minTarget])) |
|
for _, targetID := range targetIDs[1:] { |
|
size := int(AsFloat64(queueSizesRaw[targetID])) |
|
if size < minSize { |
|
minTarget = targetID |
|
minSize = size |
|
} |
|
} |
|
|
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, minTarget) |
|
output = append(output, &reqCopy) |
|
} |
|
|
|
case "random": |
|
for _, req := range queue { |
|
randomIndex := rand.Intn(len(targetIDs)) |
|
target := targetIDs[randomIndex] |
|
|
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, target) |
|
output = append(output, &reqCopy) |
|
} |
|
|
|
case "ip-hash": |
|
for _, req := range queue { |
|
h := fnv.New32a() |
|
h.Write([]byte(req.ID)) |
|
index := int(h.Sum32()) % len(targetIDs) |
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, targetIDs[index]) |
|
output = append(output, &reqCopy) |
|
} |
|
|
|
case "first-available": |
|
nodeHealth, ok := props["_nodeHealth"].(map[string]bool) |
|
if !ok { |
|
return nil, true |
|
} |
|
firstHealthy := "" |
|
for _, t := range targetIDs { |
|
if nodeHealth[t] { |
|
firstHealthy = t |
|
break |
|
} |
|
} |
|
if firstHealthy == "" { |
|
return nil, true |
|
} |
|
for _, req := range queue { |
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, firstHealthy) |
|
output = append(output, &reqCopy) |
|
} |
|
|
|
case "weighted-round-robin": |
|
// Create or reuse the weighted list of targets |
|
weighted, ok := props["_weightedTargets"].([]string) |
|
if !ok { |
|
weights, ok := props["_weights"].(map[string]float64) |
|
if !ok { |
|
return nil, true |
|
} |
|
flattened := []string{} |
|
for _, id := range targetIDs { |
|
w := int(weights[id]) |
|
for i := 0; i < w; i++ { |
|
flattened = append(flattened, id) |
|
} |
|
} |
|
weighted = flattened |
|
props["_weightedTargets"] = weighted |
|
} |
|
|
|
next := int(AsFloat64(props["_wrIndex"])) |
|
for _, req := range queue { |
|
target := weighted[next] |
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, target) |
|
output = append(output, &reqCopy) |
|
next = (next + 1) % len(weighted) |
|
} |
|
props["_wrIndex"] = float64(next) |
|
|
|
case "last": |
|
last := targetIDs[len(targetIDs)-1] |
|
for _, req := range queue { |
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, last) |
|
output = append(output, &reqCopy) |
|
} |
|
|
|
default: // round-robin |
|
next := int(AsFloat64(props["_rrIndex"])) |
|
for _, req := range queue { |
|
target := targetIDs[next] |
|
reqCopy := *req |
|
reqCopy.Path = append(reqCopy.Path, target) |
|
output = append(output, &reqCopy) |
|
next = (next + 1) % len(targetIDs) |
|
} |
|
props["_rrIndex"] = float64(next) |
|
} |
|
|
|
return output, true |
|
}
|
|
|