feat(route): implement PreferOver method for deterministic route replacement

This commit is contained in:
yusing
2025-11-06 20:19:49 +08:00
parent b90f2409ab
commit ef893974ea
2 changed files with 54 additions and 3 deletions

View File

@@ -497,6 +497,45 @@ func (r *Route) DisplayName() string {
return r.Homepage.Name
}
// PreferOver implements pool.Preferable to resolve duplicate route keys deterministically.
// Preference policy:
// - Prefer routes with rules over routes without rules.
// - If rules tie, prefer non-docker routes (explicit config) over docker-discovered routes.
// - Otherwise, prefer the new route to preserve existing semantics.
func (r *Route) PreferOver(other any) bool {
// Try to get the underlying *Route of the other value
var or *Route
switch v := other.(type) {
case *Route:
or = v
case *ReveseProxyRoute:
or = v.Route
case *FileServer:
or = v.Route
case *StreamRoute:
or = v.Route
default:
// Unknown type, allow replacement
return true
}
// Prefer routes that have rules
if len(r.Rules) > 0 && len(or.Rules) == 0 {
return true
}
if len(r.Rules) == 0 && len(or.Rules) > 0 {
return false
}
// Prefer explicit (non-docker) over docker auto-discovered
if (r.Container == nil) != (or.Container == nil) {
return r.Container == nil
}
// Default: allow replacement
return true
}
func (r *Route) ContainerInfo() *types.Container {
return r.Container
}

View File

@@ -14,6 +14,12 @@ type (
name string
disableLog atomic.Bool
}
// Preferable allows an object to express deterministic replacement preference
// when multiple objects with the same key are added to the pool.
// If new.PreferOver(old) returns true, the new object replaces the old one.
Preferable interface {
PreferOver(other any) bool
}
Object interface {
Key() string
Name() string
@@ -37,12 +43,18 @@ func (p *Pool[T]) Name() string {
}
func (p *Pool[T]) Add(obj T) {
p.checkExists(obj.Key())
p.m.Store(obj.Key(), obj)
p.logAction("added", obj)
p.AddKey(obj.Key(), obj)
}
func (p *Pool[T]) AddKey(key string, obj T) {
if cur, exists := p.m.Load(key); exists {
if newPref, ok := any(obj).(Preferable); ok {
if !newPref.PreferOver(cur) {
// keep existing
return
}
}
}
p.checkExists(key)
p.m.Store(key, obj)
p.logAction("added", obj)