Skip to content

Commit 628b942

Browse files
authored
prepare calculateBackoff(failedAttempt:) to be used in HTTP2StateMachine (#445)
1 parent d0d4598 commit 628b942

File tree

2 files changed

+61
-25
lines changed

2 files changed

+61
-25
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//===----------------------------------------------------------------------===//
2+
//
3+
// This source file is part of the AsyncHTTPClient open source project
4+
//
5+
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
6+
// Licensed under Apache License v2.0
7+
//
8+
// See LICENSE.txt for license information
9+
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
10+
//
11+
// SPDX-License-Identifier: Apache-2.0
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
import NIOCore
16+
#if canImport(Darwin)
17+
import func Darwin.pow
18+
#else
19+
import func Glibc.pow
20+
#endif
21+
22+
extension HTTPConnectionPool {
23+
/// Calculates the delay for the next connection attempt after the given number of failed `attempts`.
24+
///
25+
/// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1 minute.
26+
/// This means for:
27+
/// - 1 failed attempt : 100ms
28+
/// - 5 failed attempts: ~300ms
29+
/// - 10 failed attempts: ~930ms
30+
/// - 15 failed attempts: ~2.84s
31+
/// - 20 failed attempts: ~8.67s
32+
/// - 25 failed attempts: ~26s
33+
/// - 29 failed attempts: ~60s (max out)
34+
///
35+
/// - Parameter attempts: number of failed attempts in a row
36+
/// - Returns: time to wait until trying to establishing a new connection
37+
static func calculateBackoff(failedAttempt attempts: Int) -> TimeAmount {
38+
// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute
39+
// This means for:
40+
// - 1 failed attempt : 100ms
41+
// - 5 failed attempts: ~300ms
42+
// - 10 failed attempts: ~930ms
43+
// - 15 failed attempts: ~2.84s
44+
// - 20 failed attempts: ~8.67s
45+
// - 25 failed attempts: ~26s
46+
// - 29 failed attempts: ~60s (max out)
47+
48+
let start = Double(TimeAmount.milliseconds(100).nanoseconds)
49+
let backoffNanoseconds = Int64(start * pow(1.25, Double(attempts - 1)))
50+
51+
let backoff: TimeAmount = min(.nanoseconds(backoffNanoseconds), .seconds(60))
52+
53+
// Calculate a 3% jitter range
54+
let jitterRange = (backoff.nanoseconds / 100) * 3
55+
// Pick a random element from the range +/- jitter range.
56+
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
57+
let jitteredBackoff = backoff + jitter
58+
return jitteredBackoff
59+
}
60+
}

Sources/AsyncHTTPClient/ConnectionPool/State Machine/HTTPConnectionPool+HTTP1StateMachine.swift

+1-25
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ extension HTTPConnectionPool {
154154
// decision about the retry will be made in `connectionCreationBackoffDone(_:)`
155155
let eventLoop = self.connections.backoffNextConnectionAttempt(connectionID)
156156

157-
let backoff = self.calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts)
157+
let backoff = calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts)
158158
return .init(
159159
request: .none,
160160
connection: .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop)
@@ -444,30 +444,6 @@ extension HTTPConnectionPool {
444444
self.connections.removeConnection(at: index)
445445
return .none
446446
}
447-
448-
private func calculateBackoff(failedAttempt attempts: Int) -> TimeAmount {
449-
// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute
450-
// This means for:
451-
// - 1 failed attempt : 100ms
452-
// - 5 failed attempts: ~300ms
453-
// - 10 failed attempts: ~930ms
454-
// - 15 failed attempts: ~2.84s
455-
// - 20 failed attempts: ~8.67s
456-
// - 25 failed attempts: ~26s
457-
// - 29 failed attempts: ~60s (max out)
458-
459-
let start = Double(TimeAmount.milliseconds(100).nanoseconds)
460-
let backoffNanoseconds = Int64(start * pow(1.25, Double(attempts - 1)))
461-
462-
let backoff: TimeAmount = min(.nanoseconds(backoffNanoseconds), .seconds(60))
463-
464-
// Calculate a 3% jitter range
465-
let jitterRange = (backoff.nanoseconds / 100) * 3
466-
// Pick a random element from the range +/- jitter range.
467-
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
468-
let jitteredBackoff = backoff + jitter
469-
return jitteredBackoff
470-
}
471447
}
472448
}
473449

0 commit comments

Comments
 (0)