@@ -27,7 +27,7 @@ extension HTTPConnectionPool {
2727 private var connections : HTTP1Connections
2828 private var failedConsecutiveConnectionAttempts : Int = 0
2929
30- private var queue : RequestQueue
30+ private var requests : RequestQueue
3131 private var state : State = . running
3232
3333 init ( idGenerator: Connection . ID . Generator , maximumConcurrentConnections: Int ) {
@@ -36,7 +36,7 @@ extension HTTPConnectionPool {
3636 generator: idGenerator
3737 )
3838
39- self . queue = RequestQueue ( )
39+ self . requests = RequestQueue ( )
4040 }
4141
4242 // MARK: - Events -
@@ -72,7 +72,7 @@ extension HTTPConnectionPool {
7272 }
7373
7474 // No matter what we do now, the request will need to wait!
75- self . queue . push ( request)
75+ self . requests . push ( request)
7676 let requestAction : StateMachine . RequestAction = . scheduleRequestTimeout(
7777 for: request,
7878 on: eventLoop
@@ -84,7 +84,7 @@ extension HTTPConnectionPool {
8484 }
8585
8686 // if we are not at max connections, we may want to create a new connection
87- if self . connections. startingGeneralPurposeConnections >= self . queue . count {
87+ if self . connections. startingGeneralPurposeConnections >= self . requests . generalPurposeCount {
8888 // If there are at least as many connections starting as we have request queued, we
8989 // don't need to create a new connection. we just need to wait.
9090 return . init( request: requestAction, connection: . none)
@@ -109,14 +109,14 @@ extension HTTPConnectionPool {
109109 }
110110
111111 // No matter what we do now, the request will need to wait!
112- self . queue . push ( request)
112+ self . requests . push ( request)
113113 let requestAction : StateMachine . RequestAction = . scheduleRequestTimeout(
114114 for: request,
115115 on: eventLoop
116116 )
117117
118118 let starting = self . connections. startingEventLoopConnections ( on: eventLoop)
119- let waiting = self . queue . count ( for: eventLoop)
119+ let waiting = self . requests . count ( for: eventLoop)
120120
121121 if starting >= waiting {
122122 // There are already as many connections starting as we need for the waiting
@@ -141,11 +141,7 @@ extension HTTPConnectionPool {
141141 }
142142
143143 mutating func failedToCreateNewConnection( _ error: Error , connectionID: Connection . ID ) -> Action {
144- defer {
145- // After we have processed this call, we have one more failed connection attempt,
146- // that we need to consider when computing the jitter.
147- self . failedConsecutiveConnectionAttempts += 1
148- }
144+ self . failedConsecutiveConnectionAttempts += 1
149145
150146 switch self . state {
151147 case . running:
@@ -154,14 +150,16 @@ extension HTTPConnectionPool {
154150 // decision about the retry will be made in `connectionCreationBackoffDone(_:)`
155151 let eventLoop = self . connections. backoffNextConnectionAttempt ( connectionID)
156152
157- let backoff = self . calculateBackoff ( for : self . failedConsecutiveConnectionAttempts)
153+ let backoff = self . calculateBackoff ( failedAttempt : self . failedConsecutiveConnectionAttempts)
158154 return . init(
159155 request: . none,
160156 connection: . scheduleBackoffTimer( connectionID, backoff: backoff, on: eventLoop)
161157 )
162158
163159 case . shuttingDown:
164- let ( index, context) = self . connections. failConnection ( connectionID)
160+ guard let ( index, context) = self . connections. failConnection ( connectionID) else {
161+ preconditionFailure ( " Failed to create a connection that is unknown to us? " )
162+ }
165163 return self . nextActionForFailedConnection ( at: index, context: context)
166164
167165 case . shutDown:
@@ -174,7 +172,9 @@ extension HTTPConnectionPool {
174172 // The naming of `failConnection` is a little confusing here. All it does is moving the
175173 // connection state from `.backingOff` to `.closed` here. It also returns the
176174 // connection's index.
177- let ( index, context) = self . connections. failConnection ( connectionID)
175+ guard let ( index, context) = self . connections. failConnection ( connectionID) else {
176+ preconditionFailure ( " Backing off a connection that is unknown to us? " )
177+ }
178178 // In `nextActionForFailedConnection` a decision will be made whether the failed
179179 // connection should be replaced or removed.
180180 return self . nextActionForFailedConnection ( at: index, context: context)
@@ -202,13 +202,18 @@ extension HTTPConnectionPool {
202202
203203 /// A connection has been unexpectedly closed
204204 mutating func connectionClosed( _ connectionID: Connection . ID ) -> Action {
205- let ( index, context) = self . connections. failConnection ( connectionID)
205+ guard let ( index, context) = self . connections. failConnection ( connectionID) else {
206+ // When a connection close is initiated by the connection pool, the connection will
207+ // still report its close to the state machine. In those cases we must ignore the
208+ // event.
209+ return . none
210+ }
206211 return self . nextActionForFailedConnection ( at: index, context: context)
207212 }
208213
209214 mutating func timeoutRequest( _ requestID: Request . ID ) -> Action {
210215 // 1. check requests in queue
211- if let request = self . queue . remove ( requestID) {
216+ if let request = self . requests . remove ( requestID) {
212217 return . init(
213218 request: . failRequest( request, HTTPClientError . getConnectionFromPoolTimeout, cancelTimeout: false ) ,
214219 connection: . none
@@ -223,7 +228,7 @@ extension HTTPConnectionPool {
223228
224229 mutating func cancelRequest( _ requestID: Request . ID ) -> Action {
225230 // 1. check requests in queue
226- if self . queue . remove ( requestID) != nil {
231+ if self . requests . remove ( requestID) != nil {
227232 return . init(
228233 request: . cancelRequestTimeout( requestID) ,
229234 connection: . none
@@ -241,7 +246,7 @@ extension HTTPConnectionPool {
241246
242247 // If we have remaining request queued, we should fail all of them with a cancelled
243248 // error.
244- let waitingRequests = self . queue . removeAll ( )
249+ let waitingRequests = self . requests . removeAll ( )
245250
246251 var requestAction : StateMachine . RequestAction = . none
247252 if !waitingRequests. isEmpty {
@@ -285,7 +290,7 @@ extension HTTPConnectionPool {
285290 return self . nextActionForIdleEventLoopConnection ( at: index, context: context)
286291 }
287292 case . shuttingDown( let unclean) :
288- assert ( self . queue . isEmpty)
293+ assert ( self . requests . isEmpty)
289294 let connection = self . connections. closeConnection ( at: index)
290295 if self . connections. isEmpty {
291296 return . init(
@@ -308,15 +313,15 @@ extension HTTPConnectionPool {
308313 context: HTTP1Connections . IdleConnectionContext
309314 ) -> Action {
310315 // 1. Check if there are waiting requests in the general purpose queue
311- if let request = self . queue . popFirst ( for: nil ) {
316+ if let request = self . requests . popFirst ( for: nil ) {
312317 return . init(
313318 request: . executeRequest( request, self . connections. leaseConnection ( at: index) , cancelTimeout: true ) ,
314319 connection: . none
315320 )
316321 }
317322
318323 // 2. Check if there are waiting requests in the matching eventLoop queue
319- if let request = self . queue . popFirst ( for: context. eventLoop) {
324+ if let request = self . requests . popFirst ( for: context. eventLoop) {
320325 return . init(
321326 request: . executeRequest( request, self . connections. leaseConnection ( at: index) , cancelTimeout: true ) ,
322327 connection: . none
@@ -337,7 +342,7 @@ extension HTTPConnectionPool {
337342 context: HTTP1Connections . IdleConnectionContext
338343 ) -> Action {
339344 // Check if there are waiting requests in the matching eventLoop queue
340- if let request = self . queue . popFirst ( for: context. eventLoop) {
345+ if let request = self . requests . popFirst ( for: context. eventLoop) {
341346 return . init(
342347 request: . executeRequest( request, self . connections. leaseConnection ( at: index) , cancelTimeout: true ) ,
343348 connection: . none
@@ -372,7 +377,7 @@ extension HTTPConnectionPool {
372377 }
373378
374379 case . shuttingDown( let unclean) :
375- assert ( self . queue . isEmpty)
380+ assert ( self . requests . isEmpty)
376381 self . connections. removeConnection ( at: index)
377382 if self . connections. isEmpty {
378383 return . init(
@@ -391,7 +396,7 @@ extension HTTPConnectionPool {
391396 at index: Int ,
392397 context: HTTP1Connections . FailedConnectionContext
393398 ) -> Action {
394- if context. connectionsStartingForUseCase < self . queue . count ( for : nil ) {
399+ if context. connectionsStartingForUseCase < self . requests . generalPurposeCount {
395400 // if we have more requests queued up, than we have starting connections, we should
396401 // create a new connection
397402 let ( newConnectionID, newEventLoop) = self . connections. replaceConnection ( at: index)
@@ -408,7 +413,7 @@ extension HTTPConnectionPool {
408413 at index: Int ,
409414 context: HTTP1Connections . FailedConnectionContext
410415 ) -> Action {
411- if context. connectionsStartingForUseCase < self . queue . count ( for: context. eventLoop) {
416+ if context. connectionsStartingForUseCase < self . requests . count ( for: context. eventLoop) {
412417 // if we have more requests queued up, than we have starting connections, we should
413418 // create a new connection
414419 let ( newConnectionID, newEventLoop) = self . connections. replaceConnection ( at: index)
@@ -421,8 +426,8 @@ extension HTTPConnectionPool {
421426 return . none
422427 }
423428
424- private func calculateBackoff( for attempt : Int ) -> TimeAmount {
425- // Our backoff formula is: 100ms * 1.25^attempt that is capped of at 1minute
429+ private func calculateBackoff( failedAttempt attempts : Int ) -> TimeAmount {
430+ // Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute
426431 // This means for:
427432 // - 1 failed attempt : 100ms
428433 // - 5 failed attempts: ~300ms
@@ -433,7 +438,7 @@ extension HTTPConnectionPool {
433438 // - 29 failed attempts: ~60s (max out)
434439
435440 let start = Double ( TimeAmount . milliseconds ( 100 ) . nanoseconds)
436- let backoffNanoseconds = Int64 ( start * pow( 1.25 , Double ( attempt ) ) )
441+ let backoffNanoseconds = Int64 ( start * pow( 1.25 , Double ( attempts - 1 ) ) )
437442
438443 let backoff : TimeAmount = min ( . nanoseconds( backoffNanoseconds) , . seconds( 60 ) )
439444
@@ -450,7 +455,7 @@ extension HTTPConnectionPool {
450455extension HTTPConnectionPool . HTTP1StateMachine : CustomStringConvertible {
451456 var description : String {
452457 let stats = self . connections. stats
453- let queued = self . queue . count
458+ let queued = self . requests . count
454459
455460 return " connections: [connecting: \( stats. connecting) | backoff: \( stats. backingOff) | leased: \( stats. leased) | idle: \( stats. idle) ], queued: \( queued) "
456461 }
0 commit comments