fix(proxies): skip inactive fallback targets

This commit is contained in:
Rain
2026-09-19 00:51:01 +08:00
parent efe9aab1e4
commit 4ccdead2a2
3 changed files with 75 additions and 2 deletions
@@ -0,0 +1,40 @@
//go:build integration
package repository
import (
"github.com/Wei-Shaw/sub2api/internal/service"
"time"
)
func (s *ProxyExpirySuite) TestSweep_SkipsInactiveBackup() {
for _, chain := range []bool{false, true} {
s.Run(map[bool]string{false: "unresolved", true: "next active backup"}[chain], func() {
now := time.Now()
past := now.Add(-time.Hour)
future := now.Add(time.Hour)
healthy := s.mkProxy("active-tail", service.FallbackModeNone, &future, nil)
backup := s.mkProxy("disabled-backup", service.FallbackModeNone, &future, nil)
disabled, err := s.repo.GetByID(s.ctx, backup)
s.Require().NoError(err)
disabled.Status = "inactive"
if chain {
disabled.FallbackMode = service.FallbackModeProxy
disabled.BackupProxyID = &healthy
}
s.Require().NoError(s.repo.Update(s.ctx, disabled))
source := s.mkProxy("expired-source", service.FallbackModeProxy, &past, &backup)
account := s.mkAccountWithProxy(source)
_, err = s.repo.SweepExpiredProxies(s.ctx, now)
s.Require().NoError(err)
if chain {
s.Equal(&healthy, s.accountProxyID(account))
} else {
s.Equal(&source, s.accountProxyID(account))
}
got, err := s.repo.GetByID(s.ctx, backup)
s.Require().NoError(err)
s.Equal("inactive", got.Status, "fallback traversal must not reactivate disabled proxies")
})
}
}
+2 -1
View File
@@ -27,7 +27,8 @@ func ResolveProxyFallbackTarget(start Proxy, byID map[int64]Proxy, now time.Time
if !ok {
return nil, false
}
if !(&p).IsExpired(now) && p.Status != StatusExpired {
// Disabled nodes may define a fallback, but cannot be selected as targets.
if p.IsActive() && !p.IsExpired(now) {
id := p.ID
return &id, true
}
@@ -10,7 +10,7 @@ import (
)
func mkProxy(id int64, mode string, backup *int64, expiresInDays *int, now time.Time) Proxy {
p := Proxy{ID: id, FallbackMode: mode, BackupProxyID: backup}
p := Proxy{ID: id, Status: StatusActive, FallbackMode: mode, BackupProxyID: backup}
if expiresInDays != nil {
t := now.AddDate(0, 0, *expiresInDays)
p.ExpiresAt = &t
@@ -71,3 +71,35 @@ func TestResolveFallbackTarget(t *testing.T) {
require.Nil(t, target)
})
}
func TestResolveFallbackSkipsInactiveBackup(t *testing.T) {
now := time.Now()
for _, mode := range []string{FallbackModeNone, FallbackModeProxy, FallbackModeDirect} {
t.Run(mode, func(t *testing.T) {
source := mkProxy(1, FallbackModeProxy, i64(2), di(-1), now)
disabled := mkProxy(2, mode, i64(3), di(30), now)
disabled.Status = "inactive"
healthy := mkProxy(3, FallbackModeNone, nil, di(30), now)
target, change := ResolveProxyFallbackTarget(source, map[int64]Proxy{1: source, 2: disabled, 3: healthy}, now)
switch mode {
case FallbackModeNone:
require.False(t, change)
require.Nil(t, target)
case FallbackModeProxy:
require.True(t, change)
require.Equal(t, i64(3), target)
case FallbackModeDirect:
require.True(t, change)
require.Nil(t, target)
}
})
}
t.Run("inactive cycle", func(t *testing.T) {
source := mkProxy(1, FallbackModeProxy, i64(2), di(-1), now)
disabled := mkProxy(2, FallbackModeProxy, i64(1), nil, now)
disabled.Status = "inactive"
target, change := ResolveProxyFallbackTarget(source, map[int64]Proxy{1: source, 2: disabled}, now)
require.False(t, change)
require.Nil(t, target)
})
}