feat: enhance relay failure handling and selection logic
rabbitprincess committed
Apr 12, 2026 at 22:03 UTC
0add2c1bf48f88167e9a8576064b205ce20bf923
3 files changed
+27
-4
portal/discovery/policy.go
+3
@@ -71,6 +71,9 @@ func (p DefaultRelayPolicy) SelectPriority(states []RelayState, clientState Clie
71
72
out := selected[:0]
73
for _, state := range selected {
74
+ if state.consecutiveFailures > 0 && !state.Reachable {
75
+ continue
76
+ }
77
if clientState.RequireUDP && state.hasDescriptor() && !state.Descriptor.SupportsUDP {
78
continue
79
}
portal/discovery/relayset.go
+18
-4
@@ -316,6 +316,23 @@ func (s *RelaySet) RecordDiscoveryRTT(relayURL string, rtt time.Duration, measur
316
s.relays[relayURL] = state
317
}
318
319
+func (s *RelaySet) recordRelayFailureLocked(relayURL string, state RelayState, err error, recoveryFailures int) (expired bool, expireReason string, consecutiveFailures int) {
320
+ state, expired, expireReason = s.policy.OnFailure(state, err, recoveryFailures)
321
+ s.relays[relayURL] = state
322
+ return expired, expireReason, state.consecutiveFailures
323
+}
324
+
325
+func (s *RelaySet) RecordRelayFailure(relayURL string, err error, recoveryFailures int) (expired bool, expireReason string, consecutiveFailures int) {
326
+ s.mu.Lock()
327
+ defer s.mu.Unlock()
328
+
329
+ state, ok := s.relays[relayURL]
330
+ if !ok {
331
+ return false, "", 0
332
+ }
333
+ return s.recordRelayFailureLocked(relayURL, state, err, recoveryFailures)
334
+}
335
+
336
func (s *RelaySet) RecordDiscoveryFailure(identity types.Identity, relayURL string, err error, recoveryFailures int) (expired bool, expireReason string, consecutiveFailures int) {
337
relayKey := identity.Key()
338
if relayKey == "" {
@@ -343,8 +360,5 @@ func (s *RelaySet) RecordDiscoveryFailure(identity types.Identity, relayURL stri
360
if !ok {
361
return false, "", 0
362
}
346
-
347
- state, expired, expireReason = s.policy.OnFailure(state, err, recoveryFailures)
348
- s.relays[relayURL] = state
349
- return expired, expireReason, state.consecutiveFailures
363
+ return s.recordRelayFailureLocked(relayURL, state, err, recoveryFailures)
364
}
sdk/listener.go
+6
@@ -147,6 +147,9 @@ func (l *Listener) runStartup(ctx context.Context) {
147
errors.Is(err, &types.APIRequestError{Code: types.APIErrorCodeTransportMismatch}) ||
148
errors.Is(err, &types.APIRequestError{Code: types.APIErrorCodeHostnameConflict}) ||
149
errors.Is(err, &types.APIRequestError{Code: types.APIErrorCodeIPBanned}) {
150
+ if l.relaySet != nil && l.api != nil && l.api.baseURL != nil {
151
+ l.relaySet.RecordRelayFailure(l.api.baseURL.String(), err, 1)
152
+ }
153
log.Error().
154
Err(err).
155
Str("relay_url", l.api.baseURL.String()).
@@ -669,6 +672,9 @@ func (l *Listener) retryOrClose(ctx context.Context, operation string, err error
672
Logger()
673
674
if l.retryCount > 0 && retries > l.retryCount {
675
+ if l.relaySet != nil && l.api != nil && l.api.baseURL != nil {
676
+ l.relaySet.RecordRelayFailure(l.api.baseURL.String(), err, 1)
677
+ }
678
if operation != "lease renewal" {
679
logger.Error().
680
Err(err).