Skip to content

Commit

Permalink
fix!: Rename Exponential backoff to Quadratic
Browse files Browse the repository at this point in the history
The `gix_utils::backoff::Exponential` type actually implemented
quadratic, not exponential, backoff. This renames it from
`Exponential` to `Quadratic`.

In exponential backoff, delays are a fixed base, often 2, raised to
a power of a number that increases by one with each attempt. When
the number that increases by one with each attempt is the base,
raised to a fixed power, that is quadratic backoff.

The intended behavior here was quadratic, as implemented. For
example, in the tests, `EXPECTED_TILL_SECOND` lists the values 1,
4, 9, 16, 25, 36, 49, 64, 81, 100, and so on, which are ascending
squares. If they were an exponential sequence, then they would look
like 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, and so on.

Thus, it is only the named that needed to be changed: the
implementation was already as intended.
  • Loading branch information
EliahKagan committed Jan 26, 2025
1 parent 38a0d9a commit 5b33ded
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 17 deletions.
4 changes: 2 additions & 2 deletions gix-lock/src/acquire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub enum Fail {
/// Fail after the first unsuccessful attempt of obtaining a lock.
#[default]
Immediately,
/// Retry after failure with exponentially longer sleep times to block the current thread.
/// Retry after failure with quadratically longer sleep times to block the current thread.
/// Fail once the given duration is exceeded, similar to [Fail::Immediately]
AfterDurationWithBackoff(Duration),
}
Expand Down Expand Up @@ -176,7 +176,7 @@ fn lock_with_mode<T>(
match mode {
Fail::Immediately => try_lock(&lock_path, directory, cleanup),
Fail::AfterDurationWithBackoff(time) => {
for wait in backoff::Exponential::default_with_random().until_no_remaining(time) {
for wait in backoff::Quadratic::default_with_random().until_no_remaining(time) {
attempts += 1;
match try_lock(&lock_path, directory, cleanup.clone()) {
Ok(v) => return Ok((lock_path, v)),
Expand Down
18 changes: 9 additions & 9 deletions gix-utils/src/backoff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,17 @@ fn randomize(backoff_ms: usize) -> usize {
}
}

/// A utility to calculate steps for exponential backoff similar to how it's done in `git`.
pub struct Exponential<Fn> {
/// A utility to calculate steps for quadratic backoff similar to how it's done in `git`.
pub struct Quadratic<Fn> {
multiplier: usize,
max_multiplier: usize,
exponent: usize,
transform: Fn,
}

impl Default for Exponential<fn(usize) -> usize> {
impl Default for Quadratic<fn(usize) -> usize> {
fn default() -> Self {
Exponential {
Quadratic {
multiplier: 1,
max_multiplier: 1000,
exponent: 1,
Expand All @@ -28,10 +28,10 @@ impl Default for Exponential<fn(usize) -> usize> {
}
}

impl Exponential<fn(usize) -> usize> {
/// Create a new exponential backoff iterator that backs off in randomized, ever increasing steps.
impl Quadratic<fn(usize) -> usize> {
/// Create a new quadratic backoff iterator that backs off in randomized, ever increasing steps.
pub fn default_with_random() -> Self {
Exponential {
Quadratic {
multiplier: 1,
max_multiplier: 1000,
exponent: 1,
Expand All @@ -40,7 +40,7 @@ impl Exponential<fn(usize) -> usize> {
}
}

impl<Transform> Exponential<Transform>
impl<Transform> Quadratic<Transform>
where
Transform: Fn(usize) -> usize,
{
Expand All @@ -62,7 +62,7 @@ where
}
}

impl<Transform> Iterator for Exponential<Transform>
impl<Transform> Iterator for Quadratic<Transform>
where
Transform: Fn(usize) -> usize,
{
Expand Down
12 changes: 6 additions & 6 deletions gix-utils/tests/backoff/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
use std::time::Duration;

use gix_utils::backoff::Exponential;
use gix_utils::backoff::Quadratic;

const EXPECTED_TILL_SECOND: &[usize] = &[
1usize, 4, 9, 16, 25, 36, 49, 64, 81, 100, 121, 144, 169, 196, 225, 256, 289, 324, 361, 400, 441, 484, 529, 576,
625, 676, 729, 784, 841, 900, 961, 1000, 1000,
];

#[test]
fn random_exponential_produces_values_in_the_correct_range() {
fn random_quadratic_produces_values_in_the_correct_range() {
let mut num_identities = 0;
for (actual, expected) in Exponential::default_with_random().zip(EXPECTED_TILL_SECOND) {
for (actual, expected) in Quadratic::default_with_random().zip(EXPECTED_TILL_SECOND) {
let actual: usize = actual.as_millis().try_into().unwrap();
if actual == *expected {
num_identities += 1;
Expand All @@ -33,9 +33,9 @@ fn random_exponential_produces_values_in_the_correct_range() {
#[test]
fn how_many_iterations_for_a_second_of_waittime() {
let max = Duration::from_millis(1000);
assert_eq!(Exponential::default().until_no_remaining(max).count(), 14);
assert_eq!(Quadratic::default().until_no_remaining(max).count(), 14);
assert_eq!(
Exponential::default()
Quadratic::default()
.until_no_remaining(max)
.reduce(|acc, n| acc + n)
.unwrap(),
Expand All @@ -47,7 +47,7 @@ fn how_many_iterations_for_a_second_of_waittime() {
#[test]
fn output_with_default_settings() {
assert_eq!(
Exponential::default().take(33).collect::<Vec<_>>(),
Quadratic::default().take(33).collect::<Vec<_>>(),
EXPECTED_TILL_SECOND
.iter()
.map(|n| Duration::from_millis(*n as u64))
Expand Down

0 comments on commit 5b33ded

Please sign in to comment.