feat: configure queue retry delays with min/max bounds and update exponential backoff defaults

This commit is contained in:
2025-12-17 09:25:07 -05:00
parent 6f795bdde0
commit 6570c25617
2 changed files with 43 additions and 34 deletions

View File

@@ -123,9 +123,13 @@ export default {
},
} satisfies ExportedHandler<Env>;
const retryDelayConfig: Record<QueueName, DurationLike> = {
ANILIST_UPDATES: Duration.fromObject({ minutes: 1 }),
NEW_EPISODE: Duration.fromObject({ hours: 1 }),
const retryDelayConfig: Partial<
Record<QueueName, { min: DurationLike; max: DurationLike }>
> = {
NEW_EPISODE: {
min: Duration.fromObject({ hours: 1 }),
max: Duration.fromObject({ hours: 12 }),
},
};
function onMessageQueue<QN extends QueueName>(
@@ -144,7 +148,8 @@ function onMessageQueue<QN extends QueueName>(
message.retry({
delaySeconds: calculateExponentialBackoff({
attempt: message.attempts,
baseMin: retryDelayConfig[messageBatch.queue as QN],
baseMin: retryDelayConfig[messageBatch.queue as QN]?.min,
absCap: retryDelayConfig[messageBatch.queue as QN]?.max,
}),
});
}

View File

@@ -18,11 +18,14 @@ interface CalculateExponentialBackoffOptions {
*
* @returns A random duration between the nominal minimum and maximum, in seconds.
*/
export function calculateExponentialBackoff(
{ attempt, baseMin: baseMinDuration = Duration.fromObject({ seconds: 1 }), absCap: absCapDuration = Duration.fromObject({ seconds: 60 }), fuzzFactor = 0.2 }: CalculateExponentialBackoffOptions
): number {
const baseMin = Duration.fromDurationLike(baseMinDuration).as('seconds');
const absCap = Duration.fromDurationLike(absCapDuration).as('seconds');
export function calculateExponentialBackoff({
attempt,
baseMin: baseMinDuration = Duration.fromObject({ minutes: 1 }),
absCap: absCapDuration = Duration.fromObject({ hours: 1 }),
fuzzFactor = 0.2,
}: CalculateExponentialBackoffOptions): number {
const baseMin = Duration.fromDurationLike(baseMinDuration).as("seconds");
const absCap = Duration.fromDurationLike(absCapDuration).as("seconds");
// 1. Calculate nominal boundaries
// Example: If baseMin is 1s, the nominal boundaries are 1s, 2s, 4s, 8s... (The 'ceiling' grows exponentially)
@@ -37,7 +40,8 @@ export function calculateExponentialBackoff(
// 3. Fuzz the Max (The Ceiling)
// Example: If ceiling is 4s (and fuzz is 0.2), it becomes random between 3.2s and 4.8s
const maxFuzz = nominalCeiling * fuzzFactor;
const fuzzedCeiling = nominalCeiling + (Math.random() * 2 * maxFuzz - maxFuzz);
const fuzzedCeiling =
nominalCeiling + (Math.random() * 2 * maxFuzz - maxFuzz);
// Safety: Ensure we don't return a negative number or cross boundaries weirdly
// (e.g. if fuzz makes min > max, we swap or clamp)