145 lines
3.4 KiB
Go
145 lines
3.4 KiB
Go
package model
|
|
|
|
import "strings"
|
|
|
|
// SelectGatewayByPreference picks a gateway candidate using persisted affinity hints.
|
|
// Matching order:
|
|
// 1) gateway ID + instance ID
|
|
// 2) invoke URI
|
|
// 3) gateway ID
|
|
// 4) instance ID
|
|
// 5) first candidate fallback
|
|
func SelectGatewayByPreference(
|
|
candidates []*GatewayInstanceDescriptor,
|
|
gatewayID string,
|
|
instanceID string,
|
|
invokeURI string,
|
|
) (*GatewayInstanceDescriptor, string) {
|
|
if len(candidates) == 0 {
|
|
return nil, ""
|
|
}
|
|
|
|
gatewayID = strings.TrimSpace(gatewayID)
|
|
instanceID = strings.TrimSpace(instanceID)
|
|
invokeURI = strings.TrimSpace(invokeURI)
|
|
|
|
if gatewayID != "" && instanceID != "" {
|
|
for _, entry := range candidates {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(entry.ID), gatewayID) &&
|
|
strings.EqualFold(strings.TrimSpace(entry.InstanceID), instanceID) {
|
|
return entry, "exact"
|
|
}
|
|
}
|
|
}
|
|
|
|
if invokeURI != "" {
|
|
for _, entry := range candidates {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(entry.InvokeURI), invokeURI) {
|
|
return entry, "invoke_uri"
|
|
}
|
|
}
|
|
}
|
|
|
|
if gatewayID != "" {
|
|
for _, entry := range candidates {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(entry.ID), gatewayID) {
|
|
return entry, "gateway_id"
|
|
}
|
|
}
|
|
}
|
|
|
|
if instanceID != "" {
|
|
for _, entry := range candidates {
|
|
if entry == nil {
|
|
continue
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(entry.InstanceID), instanceID) {
|
|
return entry, "instance_id"
|
|
}
|
|
}
|
|
}
|
|
|
|
for _, entry := range candidates {
|
|
if entry != nil {
|
|
return entry, "rail_fallback"
|
|
}
|
|
}
|
|
|
|
return nil, ""
|
|
}
|
|
|
|
// GatewayDescriptorIdentityKey returns a stable dedupe key for gateway entries.
|
|
// The key is composed from logical gateway ID + instance identity; invoke URI is
|
|
// used as a fallback identity when instance ID is missing.
|
|
func GatewayDescriptorIdentityKey(entry *GatewayInstanceDescriptor) string {
|
|
if entry == nil {
|
|
return ""
|
|
}
|
|
return GatewayIdentityKey(entry.ID, entry.InstanceID, entry.InvokeURI)
|
|
}
|
|
|
|
// GatewayIdentityKey composes a stable identity key from gateway affinity fields.
|
|
func GatewayIdentityKey(gatewayID string, instanceID string, invokeURI string) string {
|
|
id := strings.ToLower(strings.TrimSpace(gatewayID))
|
|
if id == "" {
|
|
return ""
|
|
}
|
|
instance := strings.ToLower(strings.TrimSpace(instanceID))
|
|
if instance == "" {
|
|
instance = strings.ToLower(strings.TrimSpace(invokeURI))
|
|
}
|
|
if instance == "" {
|
|
return id
|
|
}
|
|
return id + "|" + instance
|
|
}
|
|
|
|
// LessGatewayDescriptor orders gateway descriptors deterministically.
|
|
func LessGatewayDescriptor(left *GatewayInstanceDescriptor, right *GatewayInstanceDescriptor) bool {
|
|
if left == nil {
|
|
return right != nil
|
|
}
|
|
if right == nil {
|
|
return false
|
|
}
|
|
|
|
if cmp := compareFolded(left.ID, right.ID); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
if cmp := compareFolded(left.InstanceID, right.InstanceID); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
if cmp := compareFolded(left.InvokeURI, right.InvokeURI); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
if cmp := compareFolded(string(left.Rail), string(right.Rail)); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
if cmp := compareFolded(left.Network, right.Network); cmp != 0 {
|
|
return cmp < 0
|
|
}
|
|
return compareFolded(left.Version, right.Version) < 0
|
|
}
|
|
|
|
func compareFolded(left string, right string) int {
|
|
l := strings.ToLower(strings.TrimSpace(left))
|
|
r := strings.ToLower(strings.TrimSpace(right))
|
|
switch {
|
|
case l < r:
|
|
return -1
|
|
case l > r:
|
|
return 1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|