Refactor RDP proxy handling and update related tests
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
package mesh
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type FabricRouteHealthTracker struct {
|
||||
mu sync.Mutex
|
||||
QuarantineTTL time.Duration
|
||||
routes map[string]FabricRouteHealthEntry
|
||||
}
|
||||
|
||||
type FabricRouteHealthEntry struct {
|
||||
Reason string `json:"reason,omitempty"`
|
||||
Failures uint64 `json:"failures"`
|
||||
LastFailure time.Time `json:"last_failure,omitempty"`
|
||||
RetryAfter time.Time `json:"retry_after,omitempty"`
|
||||
}
|
||||
|
||||
type FabricRouteHealthSnapshot struct {
|
||||
Quarantined map[string]FabricRouteHealthEntry `json:"quarantined,omitempty"`
|
||||
}
|
||||
|
||||
func NewFabricRouteHealthTracker(ttl time.Duration) *FabricRouteHealthTracker {
|
||||
if ttl <= 0 {
|
||||
ttl = 30 * time.Second
|
||||
}
|
||||
return &FabricRouteHealthTracker{QuarantineTTL: ttl, routes: map[string]FabricRouteHealthEntry{}}
|
||||
}
|
||||
|
||||
func (t *FabricRouteHealthTracker) MarkFailure(routeID string, reason string, now time.Time) {
|
||||
routeID = strings.TrimSpace(routeID)
|
||||
if t == nil || routeID == "" {
|
||||
return
|
||||
}
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
}
|
||||
ttl := t.QuarantineTTL
|
||||
if ttl <= 0 {
|
||||
ttl = 30 * time.Second
|
||||
}
|
||||
t.mu.Lock()
|
||||
entry := t.routes[routeID]
|
||||
entry.Failures++
|
||||
entry.Reason = strings.TrimSpace(reason)
|
||||
entry.LastFailure = now
|
||||
entry.RetryAfter = now.Add(ttl)
|
||||
if t.routes == nil {
|
||||
t.routes = map[string]FabricRouteHealthEntry{}
|
||||
}
|
||||
t.routes[routeID] = entry
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
func (t *FabricRouteHealthTracker) MarkSuccess(routeID string) {
|
||||
routeID = strings.TrimSpace(routeID)
|
||||
if t == nil || routeID == "" {
|
||||
return
|
||||
}
|
||||
t.mu.Lock()
|
||||
delete(t.routes, routeID)
|
||||
t.mu.Unlock()
|
||||
}
|
||||
|
||||
func (t *FabricRouteHealthTracker) Apply(routeSet FabricRouteSet, now time.Time) FabricRouteSet {
|
||||
if t == nil {
|
||||
return routeSet
|
||||
}
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
}
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
if len(t.routes) == 0 {
|
||||
return routeSet
|
||||
}
|
||||
return mapFabricRouteSet(routeSet, func(route FabricRoute) FabricRoute {
|
||||
entry, ok := t.routes[route.RouteID]
|
||||
if !ok {
|
||||
return route
|
||||
}
|
||||
if !entry.RetryAfter.IsZero() && !now.Before(entry.RetryAfter) {
|
||||
delete(t.routes, route.RouteID)
|
||||
return route
|
||||
}
|
||||
route.Healthy = false
|
||||
route.Degraded = true
|
||||
return route
|
||||
})
|
||||
}
|
||||
|
||||
func (t *FabricRouteHealthTracker) Snapshot(now time.Time) FabricRouteHealthSnapshot {
|
||||
if t == nil {
|
||||
return FabricRouteHealthSnapshot{}
|
||||
}
|
||||
if now.IsZero() {
|
||||
now = time.Now().UTC()
|
||||
}
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
out := map[string]FabricRouteHealthEntry{}
|
||||
for routeID, entry := range t.routes {
|
||||
if !entry.RetryAfter.IsZero() && !now.Before(entry.RetryAfter) {
|
||||
continue
|
||||
}
|
||||
out[routeID] = entry
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return FabricRouteHealthSnapshot{}
|
||||
}
|
||||
return FabricRouteHealthSnapshot{Quarantined: out}
|
||||
}
|
||||
|
||||
func mapFabricRouteSet(routeSet FabricRouteSet, fn func(FabricRoute) FabricRoute) FabricRouteSet {
|
||||
if strings.TrimSpace(routeSet.Primary.RouteID) != "" {
|
||||
routeSet.Primary = fn(routeSet.Primary)
|
||||
}
|
||||
for i := range routeSet.WarmStandby {
|
||||
routeSet.WarmStandby[i] = fn(routeSet.WarmStandby[i])
|
||||
}
|
||||
for i := range routeSet.ColdFallbacks {
|
||||
routeSet.ColdFallbacks[i] = fn(routeSet.ColdFallbacks[i])
|
||||
}
|
||||
return routeSet
|
||||
}
|
||||
Reference in New Issue
Block a user