signal-desktop/ts/util/exponentialBackoff.ts

64 lines
1.8 KiB
TypeScript

// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import * as durations from './durations';
const BACKOFF_FACTOR = 1.9;
const MAX_BACKOFF = 15 * durations.MINUTE;
const FIRST_BACKOFF = 100 * BACKOFF_FACTOR;
/**
* For a given attempt, how long should we sleep (in milliseconds)?
*
* The attempt should be a positive integer, and it is 1-indexed. The first attempt is 1,
* the second is 2, and so on.
*
* This is modified from [iOS's codebase][0].
*
* [0]: https://github.com/signalapp/Signal-iOS/blob/6069741602421744edfb59923d2fb3a66b1b23c1/SignalServiceKit/src/Util/OWSOperation.swift
*/
export type ExponentialBackoffOptionsType = {
maxBackoffTime: number;
multiplier: number;
firstBackoffTime: number;
};
export function exponentialBackoffSleepTime(
attempt: number,
options: ExponentialBackoffOptionsType = {
maxBackoffTime: MAX_BACKOFF,
multiplier: BACKOFF_FACTOR,
firstBackoffTime: FIRST_BACKOFF,
}
): number {
if (attempt === 1) {
return 0;
}
return Math.min(
options.maxBackoffTime,
(options.firstBackoffTime / options.multiplier) *
options.multiplier ** (attempt - 1)
);
}
/**
* If I want to retry for X milliseconds, how many attempts is that, roughly? For example,
* 24 hours (86,400,000 milliseconds) is 111 attempts.
*
* `desiredDurationMs` should be at least 1.
*/
export function exponentialBackoffMaxAttempts(
desiredDurationMs: number,
options?: ExponentialBackoffOptionsType
): number {
let attempts = 0;
let total = 0;
// There's probably some algebra we could do here instead of this loop, but this is
// fast even for giant numbers, and is typically called just once at startup.
do {
attempts += 1;
total += exponentialBackoffSleepTime(attempts, options);
} while (total < desiredDurationMs);
return attempts;
}