Skip to content

Commit

Permalink
Implement exponential backoff for polling; improve logging
Browse files Browse the repository at this point in the history
  • Loading branch information
catlee committed Jan 7, 2025
1 parent 30464f0 commit 1974eb8
Showing 1 changed file with 20 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,23 @@ import {AdminSession} from '@shopify/cli-kit/node/session'
import {renderFatalError} from '@shopify/cli-kit/node/ui'
import {AbortError} from '@shopify/cli-kit/node/error'

const POLLING_INTERVAL = 3000
const BASE_POLLING_INTERVAL = 3000
// Maximum backoff of 60 seconds
const MAX_POLLING_INTERVAL = 60000
// Add up to 30% random jitter
const JITTER_FACTOR = 0.3
class PollingError extends Error {}

function calculateBackoffTime(attemptNumber: number): number {
// Calculate exponential backoff: BASE * 2^attempt
const exponentialDelay = BASE_POLLING_INTERVAL * (2 ** attemptNumber)

Check failure on line 20 in packages/theme/src/cli/utilities/theme-environment/theme-polling.ts

View workflow job for this annotation

GitHub Actions / ESLint Report Analysis

packages/theme/src/cli/utilities/theme-environment/theme-polling.ts#L20

[prettier/prettier] Replace `(2·**·attemptNumber)` with `2·**·attemptNumber`
// Cap it at the maximum interval
const cappedDelay = Math.min(exponentialDelay, MAX_POLLING_INTERVAL)
// Add random jitter
const jitter = cappedDelay * JITTER_FACTOR * Math.random()
return cappedDelay + jitter
}

export interface PollingOptions {
noDelete: boolean
}
Expand All @@ -32,17 +46,21 @@ export function pollThemeEditorChanges(
const poll = async () => {
// Asynchronously wait for the polling interval, similar to a setInterval
// but ensure the polling work is done before starting the next interval.
await new Promise((resolve) => setTimeout(resolve, POLLING_INTERVAL))
const backoffTime = calculateBackoffTime(failedPollingAttempts)
await new Promise((resolve) => setTimeout(resolve, backoffTime))

outputDebug('Fetching remote checksums')
// eslint-disable-next-line require-atomic-updates
latestChecksums = await pollRemoteJsonChanges(targetTheme, session, latestChecksums, localFileSystem, options)
.then((checksums) => {
outputDebug('Succesfully fetched checksums')
failedPollingAttempts = 0
lastError = ''

return checksums
})
.catch((error: Error) => {
outputDebug(`Error fetching checksums: ${error}`)
failedPollingAttempts++

if (error.message !== lastError) {
Expand Down

0 comments on commit 1974eb8

Please sign in to comment.