Skip to content

prepare calculateBackoff(failedAttempt:) to be used in HTTP2StateMachine #445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2021 Apple Inc. and the AsyncHTTPClient project authors
// Licensed under Apache License v2.0
//
// See LICENSE.txt for license information
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
//
// SPDX-License-Identifier: Apache-2.0
//
//===----------------------------------------------------------------------===//

import NIO
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
import NIO
import NIOCore


extension HTTPConnectionPool {
/// Calculates the delay for the next connection attempt after the given number of failed `attempts`.
///
/// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1 minute.
/// This means for:
/// - 1 failed attempt : 100ms
/// - 5 failed attempts: ~300ms
/// - 10 failed attempts: ~930ms
/// - 15 failed attempts: ~2.84s
/// - 20 failed attempts: ~8.67s
/// - 25 failed attempts: ~26s
/// - 29 failed attempts: ~60s (max out)
///
/// - Parameter attempts: number of failed attempts in a row
/// - Returns: time to wait until trying to establishing a new connection
static func calculateBackoff(failedAttempt attempts: Int) -> TimeAmount {
// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute
// This means for:
// - 1 failed attempt : 100ms
// - 5 failed attempts: ~300ms
// - 10 failed attempts: ~930ms
// - 15 failed attempts: ~2.84s
// - 20 failed attempts: ~8.67s
// - 25 failed attempts: ~26s
// - 29 failed attempts: ~60s (max out)

let start = Double(TimeAmount.milliseconds(100).nanoseconds)
let backoffNanoseconds = Int64(start * pow(1.25, Double(attempts - 1)))

let backoff: TimeAmount = min(.nanoseconds(backoffNanoseconds), .seconds(60))

// Calculate a 3% jitter range
let jitterRange = (backoff.nanoseconds / 100) * 3
// Pick a random element from the range +/- jitter range.
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
let jitteredBackoff = backoff + jitter
return jitteredBackoff
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ extension HTTPConnectionPool {
// decision about the retry will be made in `connectionCreationBackoffDone(_:)`
let eventLoop = self.connections.backoffNextConnectionAttempt(connectionID)

let backoff = self.calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts)
let backoff = calculateBackoff(failedAttempt: self.failedConsecutiveConnectionAttempts)
return .init(
request: .none,
connection: .scheduleBackoffTimer(connectionID, backoff: backoff, on: eventLoop)
Expand Down Expand Up @@ -444,30 +444,6 @@ extension HTTPConnectionPool {
self.connections.removeConnection(at: index)
return .none
}

private func calculateBackoff(failedAttempt attempts: Int) -> TimeAmount {
// Our backoff formula is: 100ms * 1.25^(attempts - 1) that is capped of at 1minute
// This means for:
// - 1 failed attempt : 100ms
// - 5 failed attempts: ~300ms
// - 10 failed attempts: ~930ms
// - 15 failed attempts: ~2.84s
// - 20 failed attempts: ~8.67s
// - 25 failed attempts: ~26s
// - 29 failed attempts: ~60s (max out)

let start = Double(TimeAmount.milliseconds(100).nanoseconds)
let backoffNanoseconds = Int64(start * pow(1.25, Double(attempts - 1)))

let backoff: TimeAmount = min(.nanoseconds(backoffNanoseconds), .seconds(60))

// Calculate a 3% jitter range
let jitterRange = (backoff.nanoseconds / 100) * 3
// Pick a random element from the range +/- jitter range.
let jitter: TimeAmount = .nanoseconds((-jitterRange...jitterRange).randomElement()!)
let jitteredBackoff = backoff + jitter
return jitteredBackoff
}
}
}

Expand Down