Skip to content

Commit bdd23e1

Browse files
committed
Code review part 1
1 parent a9edf15 commit bdd23e1

File tree

3 files changed

+48
-111
lines changed

3 files changed

+48
-111
lines changed

Diff for: Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1Connections.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ extension HTTPConnectionPool {
376376
}
377377

378378
mutating func parkConnection(at index: Int) -> (Connection.ID, EventLoop) {
379-
assert(self.connections[index].isIdle)
379+
precondition(self.connections[index].isIdle)
380380
return (self.connections[index].connectionID, self.connections[index].eventLoop)
381381
}
382382

Diff for: Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift

+32-10
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ extension HTTPConnectionPool {
6363
}
6464
}
6565

66-
mutating func executeRequestOnPreferredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action {
66+
private mutating func executeRequestOnPreferredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action {
6767
if let connection = self.connections.leaseConnection(onPreferred: eventLoop) {
6868
return .init(
6969
request: .executeRequest(request, connection, cancelTimeout: nil),
@@ -76,7 +76,7 @@ extension HTTPConnectionPool {
7676
let requestAction: StateMachine.RequestAction = .scheduleRequestTimeout(
7777
request.connectionDeadline,
7878
for: requestID,
79-
on: request.preferredEventLoop
79+
on: eventLoop
8080
)
8181

8282
if !self.connections.canGrow {
@@ -101,7 +101,7 @@ extension HTTPConnectionPool {
101101
)
102102
}
103103

104-
mutating func executeRequestOnRequiredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action {
104+
private mutating func executeRequestOnRequiredEventLoop(_ request: Request, eventLoop: EventLoop) -> Action {
105105
if let connection = self.connections.leaseConnection(onRequired: eventLoop) {
106106
return .init(
107107
request: .executeRequest(request, connection, cancelTimeout: nil),
@@ -114,7 +114,7 @@ extension HTTPConnectionPool {
114114
let requestAction: StateMachine.RequestAction = .scheduleRequestTimeout(
115115
request.connectionDeadline,
116116
for: requestID,
117-
on: request.preferredEventLoop
117+
on: eventLoop
118118
)
119119

120120
let starting = self.connections.startingEventLoopConnections(on: eventLoop)
@@ -156,12 +156,10 @@ extension HTTPConnectionPool {
156156
// decision about the retry will be made in `connectionCreationBackoffDone(_:)`
157157
let eventLoop = self.connections.backoffNextConnectionAttempt(connectionID)
158158

159-
let backoff = TimeAmount.milliseconds(100) * Int(pow(1.25, Double(self.failedConsecutiveConnectionAttempts)))
160-
let jitterRange = backoff.nanoseconds / 100 * 5
161-
let jitteredBackoff = backoff + .nanoseconds((-jitterRange...jitterRange).randomElement()!)
159+
let backoff = self.calculateBackoff(for: self.failedConsecutiveConnectionAttempts)
162160
return .init(
163161
request: .none,
164-
connection: .scheduleBackoffTimer(connectionID, backoff: jitteredBackoff, on: eventLoop)
162+
connection: .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop)
165163
)
166164

167165
case .shuttingDown:
@@ -186,7 +184,7 @@ extension HTTPConnectionPool {
186184
return .none
187185
}
188186

189-
assert(self.state == .running, "If we are shutting down, we must not have any idle connections")
187+
precondition(self.state == .running, "If we are shutting down, we must not have any idle connections")
190188

191189
return .init(
192190
request: .none,
@@ -382,7 +380,7 @@ extension HTTPConnectionPool {
382380
return .none
383381

384382
case .shutDown:
385-
preconditionFailure("It the pool is already shutdown, all connections must have been torn down.")
383+
preconditionFailure("If the pool is already shutdown, all connections must have been torn down.")
386384
}
387385
}
388386

@@ -419,6 +417,30 @@ extension HTTPConnectionPool {
419417
self.connections.removeConnection(at: index)
420418
return .none
421419
}
420+
421+
private mutating func calculateBackoff(for attempt: Int) -> TimeAmount {
422+
// Our backoff formula is: 100ms * 1.25^attempt that is capped of at 1minute
423+
// This means for:
424+
// - 1 failed attempt : 100ms
425+
// - 5 failed attempts: ~300ms
426+
// - 10 failed attempts: ~930ms
427+
// - 15 failed attempts: ~2.84s
428+
// - 20 failed attempts: ~8.67s
429+
// - 25 failed attempts: ~26s
430+
// - 29 failed attempts: ~60s (max out)
431+
432+
let start = Double(TimeAmount.milliseconds(100).nanoseconds)
433+
let backoffNanoseconds = Int64(start * pow(1.25, Double(self.failedConsecutiveConnectionAttempts)))
434+
435+
let backoff: TimeAmount = .nanoseconds(backoffNanoseconds)
436+
437+
// Calculate a 3% jitter range
438+
let jitterRange = (backoff.nanoseconds / 100) * 3
439+
// Pick a random element from the range +/- jitter range.
440+
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
441+
let jitteredBackoff = backoff + jitter
442+
return jitteredBackoff
443+
}
422444
}
423445
}
424446

Diff for: Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+StateMachine.swift

+15-100
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,6 @@ extension HTTPConnectionPool {
6262

6363
enum HTTPTypeStateMachine {
6464
case http1(HTTP1StateMachine)
65-
66-
case _modifying
6765
}
6866

6967
var state: HTTPTypeStateMachine
@@ -85,59 +83,31 @@ extension HTTPConnectionPool {
8583
mutating func executeRequest(_ request: Request) -> Action {
8684
switch self.state {
8785
case .http1(var http1StateMachine):
88-
return self.state.modify { state -> Action in
89-
let action = http1StateMachine.executeRequest(request)
90-
state = .http1(http1StateMachine)
91-
return action
92-
}
93-
94-
case ._modifying:
95-
preconditionFailure("Invalid state: \(self.state)")
86+
return http1StateMachine.executeRequest(request)
9687
}
9788
}
9889

9990
mutating func newHTTP1ConnectionCreated(_ connection: Connection) -> Action {
10091
switch self.state {
10192
case .http1(var httpStateMachine):
102-
return self.state.modify { state -> Action in
103-
let action = httpStateMachine.newHTTP1ConnectionEstablished(connection)
104-
state = .http1(httpStateMachine)
105-
return action
106-
}
107-
108-
case ._modifying:
109-
preconditionFailure("Invalid state: \(self.state)")
93+
return httpStateMachine.newHTTP1ConnectionEstablished(connection)
11094
}
11195
}
11296

11397
mutating func failedToCreateNewConnection(_ error: Error, connectionID: Connection.ID) -> Action {
11498
switch self.state {
11599
case .http1(var http1StateMachine):
116-
return self.state.modify { state -> Action in
117-
let action = http1StateMachine.failedToCreateNewConnection(
118-
error,
119-
connectionID: connectionID
120-
)
121-
state = .http1(http1StateMachine)
122-
return action
123-
}
124-
125-
case ._modifying:
126-
preconditionFailure("Invalid state: \(self.state)")
100+
return http1StateMachine.failedToCreateNewConnection(
101+
error,
102+
connectionID: connectionID
103+
)
127104
}
128105
}
129106

130107
mutating func connectionCreationBackoffDone(_ connectionID: Connection.ID) -> Action {
131108
switch self.state {
132109
case .http1(var http1StateMachine):
133-
return self.state.modify { state -> Action in
134-
let action = http1StateMachine.connectionCreationBackoffDone(connectionID)
135-
state = .http1(http1StateMachine)
136-
return action
137-
}
138-
139-
case ._modifying:
140-
preconditionFailure("Invalid state: \(self.state)")
110+
return http1StateMachine.connectionCreationBackoffDone(connectionID)
141111
}
142112
}
143113

@@ -149,14 +119,7 @@ extension HTTPConnectionPool {
149119
mutating func timeoutRequest(_ requestID: Request.ID) -> Action {
150120
switch self.state {
151121
case .http1(var http1StateMachine):
152-
return self.state.modify { state -> Action in
153-
let action = http1StateMachine.timeoutRequest(requestID)
154-
state = .http1(http1StateMachine)
155-
return action
156-
}
157-
158-
case ._modifying:
159-
preconditionFailure("Invalid state: \(self.state)")
122+
return http1StateMachine.timeoutRequest(requestID)
160123
}
161124
}
162125

@@ -168,55 +131,29 @@ extension HTTPConnectionPool {
168131
mutating func cancelRequest(_ requestID: Request.ID) -> Action {
169132
switch self.state {
170133
case .http1(var http1StateMachine):
171-
return self.state.modify { state -> Action in
172-
let action = http1StateMachine.cancelRequest(requestID)
173-
state = .http1(http1StateMachine)
174-
return action
175-
}
176-
177-
case ._modifying:
178-
preconditionFailure("Invalid state: \(self.state)")
134+
return http1StateMachine.cancelRequest(requestID)
179135
}
180136
}
181137

182138
mutating func connectionIdleTimeout(_ connectionID: Connection.ID) -> Action {
183139
switch self.state {
184140
case .http1(var http1StateMachine):
185-
return self.state.modify { state -> Action in
186-
let action = http1StateMachine.connectionIdleTimeout(connectionID)
187-
state = .http1(http1StateMachine)
188-
return action
189-
}
190-
191-
case ._modifying:
192-
preconditionFailure("Invalid state: \(self.state)")
141+
return http1StateMachine.connectionIdleTimeout(connectionID)
193142
}
194143
}
195144

196145
/// A connection has been closed
197146
mutating func connectionClosed(_ connectionID: Connection.ID) -> Action {
198147
switch self.state {
199148
case .http1(var http1StateMachine):
200-
return self.state.modify { state -> Action in
201-
let action = http1StateMachine.connectionClosed(connectionID)
202-
state = .http1(http1StateMachine)
203-
return action
204-
}
205-
206-
case ._modifying:
207-
preconditionFailure("Invalid state: \(self.state)")
149+
return http1StateMachine.connectionClosed(connectionID)
208150
}
209151
}
210152

211153
mutating func http1ConnectionReleased(_ connectionID: Connection.ID) -> Action {
212-
guard case .http1(var http1StateMachine) = self.state else {
213-
preconditionFailure("Invalid state: \(self.state)")
214-
}
215-
216-
return self.state.modify { state -> Action in
217-
let action = http1StateMachine.http1ConnectionReleased(connectionID)
218-
state = .http1(http1StateMachine)
219-
return action
154+
switch self.state {
155+
case .http1(var http1StateMachine):
156+
return http1StateMachine.http1ConnectionReleased(connectionID)
220157
}
221158
}
222159

@@ -229,14 +166,7 @@ extension HTTPConnectionPool {
229166

230167
switch self.state {
231168
case .http1(var http1StateMachine):
232-
return self.state.modify { state -> Action in
233-
let action = http1StateMachine.shutdown()
234-
state = .http1(http1StateMachine)
235-
return action
236-
}
237-
238-
case ._modifying:
239-
preconditionFailure("Invalid state: \(self.state)")
169+
return http1StateMachine.shutdown()
240170
}
241171
}
242172
}
@@ -262,26 +192,11 @@ extension HTTPConnectionPool {
262192
}
263193
}
264194

265-
extension HTTPConnectionPool.StateMachine.HTTPTypeStateMachine {
266-
mutating func modify<T>(_ closure: (inout Self) throws -> (T)) rethrows -> T {
267-
self = ._modifying
268-
defer {
269-
if case ._modifying = self {
270-
preconditionFailure("Invalid state. Use closure to modify state")
271-
}
272-
}
273-
return try closure(&self)
274-
}
275-
}
276-
277195
extension HTTPConnectionPool.StateMachine: CustomStringConvertible {
278196
var description: String {
279197
switch self.state {
280198
case .http1(let http1):
281199
return ".http1(\(http1))"
282-
283-
case ._modifying:
284-
preconditionFailure("Invalid state: \(self.state)")
285200
}
286201
}
287202
}

0 commit comments

Comments
 (0)