@@ -49,9 +49,15 @@ func (cp *multiServerPool) Next() (*Connection, error) {
4949 if cp .mu .activeCount > 0 { //nolint:nestif // warmup skip/accept/starvation requires nested branching
5050 var bestWarmingConn * Connection
5151 bestWarmingRemSkip := int (^ uint (0 ) >> 1 ) // max int
52+ var needsCapEnforce bool
5253
5354 for attempt := range cp .mu .activeCount {
54- conn := cp .getNextActiveConnWithLock ()
55+ conn , selectorCapEnforce := cp .getNextActiveConnWithLock ()
56+ needsCapEnforce = needsCapEnforce || selectorCapEnforce
57+
58+ if conn == nil {
59+ continue // selector error
60+ }
5561 state := conn .loadConnState ()
5662
5763 if state .lifecycle ()& (lcActive | lcStandby ) == 0 {
@@ -67,6 +73,9 @@ func (cp *multiServerPool) Next() (*Connection, error) {
6773 if ! state .isWarmingUp () {
6874 cp .mu .RUnlock ()
6975 cp .poolRequests .Add (1 )
76+ if needsCapEnforce {
77+ cp .triggerCapEnforcement ()
78+ }
7079 return conn , nil
7180 }
7281
@@ -77,11 +86,11 @@ func (cp *multiServerPool) Next() (*Connection, error) {
7786 // Check if warmup finished and cap enforcement is needed.
7887 warmupDone := ! conn .loadConnState ().isWarmingUp ()
7988 if warmupDone && cp .activeListCap > 0 && cp .mu .activeCount > cp .activeListCap {
89+ needsCapEnforce = true
8090 if debugLogger != nil {
8191 debugLogger .Logf ("[%s] Next: warmup complete for %s, triggering cap enforcement (active=%d, cap=%d)\n " ,
8292 cp .name , conn .URL , cp .mu .activeCount , cp .activeListCap )
8393 }
84- go cp .deferredCapEnforcement ()
8594 } else if warmupDone && debugLogger != nil {
8695 debugLogger .Logf ("[%s] Next: warmup complete for %s, no cap enforcement (active=%d, cap=%d)\n " ,
8796 cp .name , conn .URL , cp .mu .activeCount , cp .activeListCap )
@@ -95,12 +104,18 @@ func (cp *multiServerPool) Next() (*Connection, error) {
95104
96105 cp .mu .RUnlock ()
97106 cp .poolRequests .Add (1 )
107+ if needsCapEnforce {
108+ cp .triggerCapEnforcement ()
109+ }
98110 return conn , nil
99111
100112 case warmupInactive :
101113 // Warmup completed between our isWarmingUp() check and tryWarmupSkip() call.
102114 cp .mu .RUnlock ()
103115 cp .poolRequests .Add (1 )
116+ if needsCapEnforce {
117+ cp .triggerCapEnforcement ()
118+ }
104119 return conn , nil
105120
106121 case warmupSkipped :
@@ -126,6 +141,9 @@ func (cp *multiServerPool) Next() (*Connection, error) {
126141 }
127142 cp .mu .RUnlock ()
128143 cp .poolRequests .Add (1 )
144+ if needsCapEnforce {
145+ cp .triggerCapEnforcement ()
146+ }
129147 return bestWarmingConn , nil
130148 }
131149 }
@@ -141,15 +159,6 @@ func (cp *multiServerPool) Next() (*Connection, error) {
141159 return cp .nextFallback ()
142160}
143161
144- // deferredCapEnforcement acquires the pool write lock and trims the active
145- // partition if it exceeds activeListCap. Called as a goroutine when warmup
146- // completes and the active partition is temporarily over capacity.
147- func (cp * multiServerPool ) deferredCapEnforcement () {
148- cp .mu .Lock ()
149- defer cp .mu .Unlock ()
150- cp .enforceActiveCapWithLock ()
151- }
152-
153162// nextWithEviction acquires a write lock and iterates active connections,
154163// evicting any that were externally demoted (lifecycle != lcActive) by another
155164// pool's stats poller. Returns the first healthy connection found, or falls
@@ -168,10 +177,16 @@ func (cp *multiServerPool) nextWithEviction() (*Connection, error) {
168177 break
169178 }
170179
171- conn := cp .getNextActiveConnWithLock ()
180+ conn , needsCapEnforce := cp .getNextActiveConnWithLock ()
181+ if conn == nil {
182+ continue
183+ }
172184 state := conn .loadConnState ()
173185
174186 if state .lifecycle ()& (lcActive | lcStandby ) != 0 {
187+ if needsCapEnforce {
188+ cp .enforceActiveCapWithLock ()
189+ }
175190 cp .poolRequests .Add (1 )
176191 return conn , nil
177192 }
@@ -192,23 +207,34 @@ func (cp *multiServerPool) nextFallback() (*Connection, error) {
192207 // Double-check active after acquiring write lock
193208 if cp .mu .activeCount > 0 {
194209 // Re-check state on the selected connection under write lock
195- conn := cp .getNextActiveConnWithLock ()
196- state := conn .loadConnState ()
197- if state .lifecycle ()& (lcActive | lcStandby ) != 0 {
198- cp .poolRequests .Add (1 )
199- return conn , nil
210+ conn , needsCapEnforce := cp .getNextActiveConnWithLock ()
211+ if conn != nil {
212+ state := conn .loadConnState ()
213+ if state .lifecycle ()& (lcActive | lcStandby ) != 0 {
214+ if needsCapEnforce {
215+ cp .enforceActiveCapWithLock ()
216+ }
217+ cp .poolRequests .Add (1 )
218+ return conn , nil
219+ }
220+ // Externally killed (no position bits) -- evict and continue
221+ cp .evictExternallyDemotedWithLock (conn , state )
200222 }
201- // Externally killed (no position bits) -- evict and continue
202- cp .evictExternallyDemotedWithLock (conn , state )
203223 // Try remaining active connections
204224 maxSkips := cp .mu .activeCount
205225 for range maxSkips {
206226 if cp .mu .activeCount <= 0 {
207227 break
208228 }
209- conn = cp .getNextActiveConnWithLock ()
210- state = conn .loadConnState ()
229+ conn , needsCapEnforce = cp .getNextActiveConnWithLock ()
230+ if conn == nil {
231+ continue
232+ }
233+ state := conn .loadConnState ()
211234 if state .lifecycle ()& (lcActive | lcStandby ) != 0 {
235+ if needsCapEnforce {
236+ cp .enforceActiveCapWithLock ()
237+ }
212238 cp .poolRequests .Add (1 )
213239 return conn , nil
214240 }
0 commit comments