@@ -25,137 +25,109 @@ type BackoffConfig struct {
2525 MinStableDuration time.Duration
2626}
2727
28+ // DefaultBackoffConfig provides sensible defaults for BackoffConfig.
2829var DefaultBackoffConfig = BackoffConfig {
2930 BaseDelay : 2 * time .Second ,
3031 MaxDelay : time .Minute ,
3132 MinStableDuration : 5 * time .Second ,
3233}
3334
34- // dialState tracks dial state for a specific underlay type.
35- type dialState struct {
36- consecutiveFailures uint32
37- lastSuccess time.Time
38- }
39-
4035// BackoffDialPolicy wraps a DialUnderlayFactory with exponential backoff retry logic.
41- // It tracks consecutive failures per underlay type and detects short-lived connections.
36+ // It tracks consecutive failures and detects short-lived connections.
4237type BackoffDialPolicy struct {
4338 Dialer DialUnderlayFactory
4439 Backoff BackoffConfig
4540 mu sync.Mutex
46- state map [string ]* dialState
41+ consecutiveFailures uint32
42+ lastSuccess time.Time
4743}
4844
45+ // NewBackoffDialPolicy creates a BackoffDialPolicy with default configuration.
4946func NewBackoffDialPolicy (dialer DialUnderlayFactory ) * BackoffDialPolicy {
5047 return & BackoffDialPolicy {
5148 Dialer : dialer ,
5249 Backoff : DefaultBackoffConfig ,
53- state : map [string ]* dialState {},
5450 }
5551}
5652
53+ // NewBackoffDialPolicyWithConfig creates a BackoffDialPolicy with the given configuration.
5754func NewBackoffDialPolicyWithConfig (dialer DialUnderlayFactory , config BackoffConfig ) * BackoffDialPolicy {
5855 return & BackoffDialPolicy {
5956 Dialer : dialer ,
6057 Backoff : config ,
61- state : map [string ]* dialState {},
6258 }
6359}
6460
6561// recordShortLivedConnection checks if the last successful connection was short-lived
6662// (died before MinStableDuration elapsed) and treats it as a failure for backoff purposes.
6763// If the connection lived long enough to be considered stable, the failure count is reset.
68- func (self * BackoffDialPolicy ) recordShortLivedConnection (underlayType string ) {
64+ func (self * BackoffDialPolicy ) recordShortLivedConnection () {
6965 self .mu .Lock ()
7066 defer self .mu .Unlock ()
7167
72- s , ok := self .state [underlayType ]
73- if ! ok {
74- return
75- }
76-
77- if s .lastSuccess .IsZero () {
68+ if self .lastSuccess .IsZero () {
7869 return
7970 }
8071
81- if time .Since (s .lastSuccess ) < self .Backoff .MinStableDuration {
82- s .consecutiveFailures ++
72+ if time .Since (self .lastSuccess ) < self .Backoff .MinStableDuration {
73+ self .consecutiveFailures ++
8374 } else {
84- s .consecutiveFailures = 0
75+ self .consecutiveFailures = 0
8576 }
86- s .lastSuccess = time.Time {}
77+ self .lastSuccess = time.Time {}
8778}
8879
89- func (self * BackoffDialPolicy ) getBackoffDelay (underlayType string ) time.Duration {
80+ func (self * BackoffDialPolicy ) getBackoffDelay () time.Duration {
9081 self .mu .Lock ()
9182 defer self .mu .Unlock ()
9283
93- s , ok := self .state [underlayType ]
94- if ! ok {
95- return 0
96- }
97-
98- if s .consecutiveFailures == 0 {
84+ if self .consecutiveFailures == 0 {
9985 return 0
10086 }
10187
10288 // Exponential backoff: baseDelay * 2^(failures-1), capped at maxDelay
103- delay := self .Backoff .BaseDelay * (1 << min (s .consecutiveFailures - 1 , 30 ))
89+ delay := self .Backoff .BaseDelay * (1 << min (self .consecutiveFailures - 1 , 30 ))
10490 return min (delay , self .Backoff .MaxDelay )
10591}
10692
107- func (self * BackoffDialPolicy ) recordSuccess (underlayType string ) {
93+ func (self * BackoffDialPolicy ) recordSuccess () {
10894 self .mu .Lock ()
10995 defer self .mu .Unlock ()
110- s , ok := self .state [underlayType ]
111- if ! ok {
112- s = & dialState {}
113- self .state [underlayType ] = s
114- }
115- s .lastSuccess = time .Now ()
96+ self .lastSuccess = time .Now ()
11697 // Don't reset consecutiveFailures yet — wait to see if connection is stable.
117- // Checked on next call to getBackoffDelay .
98+ // Checked on next call to recordShortLivedConnection .
11899}
119100
120- func (self * BackoffDialPolicy ) recordFailure (underlayType string ) {
101+ func (self * BackoffDialPolicy ) recordFailure () {
121102 self .mu .Lock ()
122103 defer self .mu .Unlock ()
123- s , ok := self .state [underlayType ]
124- if ! ok {
125- s = & dialState {}
126- self .state [underlayType ] = s
127- }
128- s .consecutiveFailures ++
129- s .lastSuccess = time.Time {}
104+ self .consecutiveFailures ++
105+ self .lastSuccess = time.Time {}
130106}
131107
132- // MarkStable resets the failure count for an underlay type.
133- // Call this when a connection has been alive long enough to be considered stable.
134- func (self * BackoffDialPolicy ) MarkStable (underlayType string ) {
108+ // MarkStable resets the failure count. Call this when a connection has been alive long
109+ // enough to be considered stable.
110+ func (self * BackoffDialPolicy ) MarkStable () {
135111 self .mu .Lock ()
136112 defer self .mu .Unlock ()
137- if s , ok := self .state [underlayType ]; ok {
138- s .consecutiveFailures = 0
139- s .lastSuccess = time.Time {}
140- }
113+ self .consecutiveFailures = 0
114+ self .lastSuccess = time.Time {}
141115}
142116
143- // ConsecutiveFailures returns the number of consecutive failures for an underlay type .
144- func (self * BackoffDialPolicy ) ConsecutiveFailures (underlayType string ) uint32 {
117+ // ConsecutiveFailures returns the current consecutive failure count .
118+ func (self * BackoffDialPolicy ) ConsecutiveFailures () uint32 {
145119 self .mu .Lock ()
146120 defer self .mu .Unlock ()
147- if s , ok := self .state [underlayType ]; ok {
148- return s .consecutiveFailures
149- }
150- return 0
121+ return self .consecutiveFailures
151122}
152123
124+ // Dial attempts to create a new underlay, applying exponential backoff if previous attempts failed.
153125func (self * BackoffDialPolicy ) Dial (underlayType string , connectionId string , groupSecret []byte , connectTimeout time.Duration , cancel <- chan struct {}) (Underlay , error ) {
154126 log := pfxlog .Logger ().WithField ("underlayType" , underlayType ).WithField ("connectionId" , connectionId )
155127
156- self .recordShortLivedConnection (underlayType )
128+ self .recordShortLivedConnection ()
157129
158- if delay := self .getBackoffDelay (underlayType ); delay > 0 {
130+ if delay := self .getBackoffDelay (); delay > 0 {
159131 log .WithField ("delay" , delay ).Debug ("backing off before dial" )
160132 select {
161133 case <- cancel :
@@ -177,11 +149,11 @@ func (self *BackoffDialPolicy) Dial(underlayType string, connectionId string, gr
177149 IsGroupedHeader : {1 },
178150 })
179151 if err != nil {
180- self .recordFailure (underlayType )
152+ self .recordFailure ()
181153 log .WithError (err ).Info ("dial failed" )
182154 return nil , err
183155 }
184156
185- self .recordSuccess (underlayType )
157+ self .recordSuccess ()
186158 return underlay , nil
187159}
0 commit comments