From 33d387a719236bb88ed744d4d0af50b551a50d39 Mon Sep 17 00:00:00 2001 From: vineyardbovines Date: Wed, 29 Jul 2026 12:43:34 -0400 Subject: [PATCH] Deduplicate multipart retry checks --- src/lib/media/video/multipart/upload.ts | 16 +++------------- src/lib/media/video/multipart/uploadParts.ts | 16 ++-------------- src/lib/media/video/multipart/utils.ts | 12 ++++++++++++ 3 files changed, 17 insertions(+), 27 deletions(-) diff --git a/src/lib/media/video/multipart/upload.ts b/src/lib/media/video/multipart/upload.ts index 52bf1fc8c8..af2d71d596 100644 --- a/src/lib/media/video/multipart/upload.ts +++ b/src/lib/media/video/multipart/upload.ts @@ -3,7 +3,7 @@ import {nanoid} from 'nanoid/non-secure' import {AbortError} from '#/lib/async/cancelable' import {type CompressedVideo} from '#/lib/media/video/types' -import {isRetryableHttpStatus, shouldRetryError} from '#/lib/strings/errors' +import {shouldRetryError} from '#/lib/strings/errors' import {getServiceAuthToken} from '../upload.shared' import {mimeToExt} from '../util' import { @@ -19,7 +19,7 @@ import {getMissingParts, planParts} from './planParts' import {createChunkReader} from './readChunk' import {createUploadPart} from './uploadPart' import {uploadParts} from './uploadParts' -import {delay} from './utils' +import {delay, isRetryableMultipartError} from './utils' export class MultipartFallbackError extends Error {} @@ -208,7 +208,7 @@ async function getUploadStatusWithRetry( return await getUploadStatus(jobId, token, signal) } catch (err) { throwIfAborted(signal) - if (!isRetryableStatusError(err)) throw err + if (!isRetryableMultipartError(err)) throw err lastError = err if (attempt < 3) await delay(500 * 2 ** (attempt - 1), signal) } @@ -216,16 +216,6 @@ async function getUploadStatusWithRetry( throw lastError } -function isRetryableStatusError(err: unknown) { - return ( - err instanceof TypeError || - (err instanceof MultipartUploadError && - (err.error === 'ServiceOverloaded' || - err.status === undefined || - isRetryableHttpStatus(err.status))) - ) -} - async function abortThenFallbackOrResolve( jobId: string, token: string, diff --git a/src/lib/media/video/multipart/uploadParts.ts b/src/lib/media/video/multipart/uploadParts.ts index 67ab0ac7b4..cda502fb54 100644 --- a/src/lib/media/video/multipart/uploadParts.ts +++ b/src/lib/media/video/multipart/uploadParts.ts @@ -1,7 +1,5 @@ import {AbortError} from '#/lib/async/cancelable' -import {isRetryableHttpStatus} from '#/lib/strings/errors' import {createProgressAggregator} from './aggregateProgress' -import {MultipartUploadError} from './api' import {MULTIPART_CONCURRENCY, MULTIPART_MAX_ATTEMPTS} from './constants' import { type ChunkReader, @@ -9,7 +7,7 @@ import { type PartUploadResult, type UploadPartFn, } from './types' -import {delay} from './utils' +import {delay, isRetryableMultipartError} from './utils' /** * Uploads every part with a concurrency cap and per-part retry, aggregating @@ -121,7 +119,7 @@ async function uploadPartWithRetry({ throw new AbortError() } lastError = err - if (!isRetryablePartError(err)) throw err + if (!isRetryableMultipartError(err)) throw err if (attempt < maxAttempts) { await delay(500 * 2 ** (attempt - 1), signal) } @@ -129,13 +127,3 @@ async function uploadPartWithRetry({ } throw lastError } - -function isRetryablePartError(err: unknown) { - return ( - err instanceof TypeError || - (err instanceof MultipartUploadError && - (err.error === 'ServiceOverloaded' || - err.status === undefined || - isRetryableHttpStatus(err.status))) - ) -} diff --git a/src/lib/media/video/multipart/utils.ts b/src/lib/media/video/multipart/utils.ts index e81e05642c..4184ab5666 100644 --- a/src/lib/media/video/multipart/utils.ts +++ b/src/lib/media/video/multipart/utils.ts @@ -1,4 +1,16 @@ import {AbortError} from '#/lib/async/cancelable' +import {isRetryableHttpStatus} from '#/lib/strings/errors' +import {MultipartUploadError} from './api' + +export function isRetryableMultipartError(err: unknown) { + return ( + err instanceof TypeError || + (err instanceof MultipartUploadError && + (err.error === 'ServiceOverloaded' || + err.status === undefined || + isRetryableHttpStatus(err.status))) + ) +} export function delay(ms: number, signal: AbortSignal) { return new Promise((resolve, reject) => {