This commit is contained in:
Manav Rathi 2024-11-21 06:53:24 +05:30
parent aa3c212b26
commit 3578ed1eef
No known key found for this signature in database
6 changed files with 41 additions and 81 deletions

View File

@ -1,6 +1,6 @@
import log from "@/base/log";
import { apiURL } from "@/base/origins";
import { retryHTTPCall } from "@/gallery/retry-async";
import { retryAsyncOperation } from "@/gallery/retry-async";
import { EnteFile } from "@/media/file";
import { CustomError, handleUploadError } from "@ente/shared/error";
import HTTPService from "@ente/shared/network/HTTPService";
@ -21,7 +21,7 @@ class PublicUploadHttpClient {
throw Error(CustomError.TOKEN_MISSING);
}
const url = await apiURL("/public-collection/file");
const response = await retryHTTPCall(
const response = await retryAsyncOperation(
() =>
HTTPService.post(url, uploadFile, null, {
"X-Auth-Access-Token": token,

View File

@ -1,6 +1,6 @@
import log from "@/base/log";
import { apiURL, uploaderOrigin } from "@/base/origins";
import { retryHTTPCall } from "@/gallery/retry-async";
import { retryAsyncOperation } from "@/gallery/retry-async";
import { EnteFile } from "@/media/file";
import { CustomError, handleUploadError } from "@ente/shared/error";
import HTTPService from "@ente/shared/network/HTTPService";
@ -17,7 +17,7 @@ class UploadHttpClient {
return;
}
const url = await apiURL("/files");
const response = await retryHTTPCall(
const response = await retryAsyncOperation(
() =>
HTTPService.post(url, uploadFile, null, {
"X-Auth-Token": token,
@ -90,7 +90,7 @@ class UploadHttpClient {
progressTracker,
): Promise<string> {
try {
await retryHTTPCall(
await retryAsyncOperation(
() =>
HTTPService.put(
fileUploadURL.url,
@ -117,7 +117,7 @@ class UploadHttpClient {
): Promise<string> {
try {
const origin = await uploaderOrigin();
await retryHTTPCall(() =>
await retryAsyncOperation(() =>
HTTPService.put(
`${origin}/file-upload`,
file,
@ -143,7 +143,7 @@ class UploadHttpClient {
progressTracker,
) {
try {
const response = await retryHTTPCall(async () => {
const response = await retryAsyncOperation(async () => {
const resp = await HTTPService.put(
partUploadURL,
filePart,
@ -174,7 +174,7 @@ class UploadHttpClient {
) {
try {
const origin = await uploaderOrigin();
const response = await retryHTTPCall(async () => {
const response = await retryAsyncOperation(async () => {
const resp = await HTTPService.put(
`${origin}/multipart-upload`,
filePart,
@ -202,7 +202,7 @@ class UploadHttpClient {
async completeMultipartUpload(completeURL: string, reqBody: any) {
try {
await retryHTTPCall(() =>
await retryAsyncOperation(() =>
HTTPService.post(completeURL, reqBody, null, {
"content-type": "text/xml",
}),
@ -216,7 +216,7 @@ class UploadHttpClient {
async completeMultipartUploadV2(completeURL: string, reqBody: any) {
try {
const origin = await uploaderOrigin();
await retryHTTPCall(() =>
await retryAsyncOperation(() =>
HTTPService.post(
`${origin}/multipart-complete`,
reqBody,

View File

@ -1,48 +1,37 @@
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-inferrable-types */
/* eslint-disable @typescript-eslint/no-explicit-any */
import { wait } from "@/utils/promise";
const retrySleepTimeInMilliSeconds = [2000, 5000, 10000];
/**
* Retry a HTTP request 3 (+ 1 original) times with exponential backoff.
* Retry a async operation like a HTTP request 3 (+ 1 original) times with
* exponential backoff.
*
* @param func A function that perform the operation, returning the promise for
* @param op A function that performs the operation, returning the promise for
* its completion.
*
* @param checkForBreakingError A function that is passed the error with which
* {@link func} rejects. It should throw the error if the retries should
* immediately be aborted.
* @param abortIfNeeded An optional function that is called with the
* corresponding error whenever {@link op} rejects. It should throw the error if
* the retries should immediately be aborted.
*
* @returns A promise that fulfills with to the result of a successfully
* fulfilled promise of the 4 (1 + 3) attempts, or rejects with the error of
* either when {@link checkForBreakingError} throws, or with the error from the
* @returns A promise that fulfills with to the result of a first successfully
* fulfilled promise of the 4 (1 + 3) attempts, or rejects with the error
* obtained either when {@link abortIfNeeded} throws, or with the error from the
* last attempt otherwise.
*/
export async function retryHTTPCall(
func: () => Promise<any>,
checkForBreakingError?: (error: unknown) => void,
): Promise<any> {
const retrier = async (
func: () => Promise<any>,
attemptNumber: number = 0,
) => {
export const retryAsyncOperation = async <T>(
op: () => Promise<T>,
abortIfNeeded?: (error: unknown) => void,
): Promise<T> => {
const waitTimeBeforeNextTry = [2000, 5000, 10000];
while (true) {
try {
const resp = await func();
return resp;
return await op();
} catch (e) {
if (checkForBreakingError) {
checkForBreakingError(e);
}
if (attemptNumber < retrySleepTimeInMilliSeconds.length) {
await wait(retrySleepTimeInMilliSeconds[attemptNumber]!);
return await retrier(func, attemptNumber + 1);
} else {
throw e;
if (abortIfNeeded) {
abortIfNeeded(e);
}
const t = waitTimeBeforeNextTry.shift();
if (!t) throw e;
await wait(t);
}
};
return await retrier(func);
}
}
};

View File

@ -4,6 +4,7 @@
"private": true,
"dependencies": {
"@/base": "*",
"@/gallery": "*",
"@/utils": "*",
"@ente/shared": "*",
"@mui/x-date-pickers": "^7.16.0",

View File

@ -18,7 +18,7 @@ import * as ffmpeg from "@/new/photos/services/ffmpeg";
import { renderableImageBlob } from "@/new/photos/utils/file";
import { CustomError } from "@ente/shared/error";
import HTTPService from "@ente/shared/network/HTTPService";
import { retryAsyncFunction } from "@ente/shared/utils";
import { retryAsyncOperation } from "@/gallery/retry-async";
export type OnDownloadProgress = (event: {
loaded: number;
@ -638,7 +638,7 @@ class PhotosDownloadClient implements DownloadClient {
}
};
const resp = await retryAsyncFunction(getThumbnail);
const resp = await retryAsyncOperation(getThumbnail);
if (resp.data === undefined) throw Error("request failed");
// TODO: Remove this cast (it won't be needed when we migrate this from
// axios to fetch).
@ -680,7 +680,7 @@ class PhotosDownloadClient implements DownloadClient {
}
};
const resp = await retryAsyncFunction(getFile);
const resp = await retryAsyncOperation(getFile);
if (resp.data === undefined) throw Error("request failed");
// TODO: Remove this cast (it won't be needed when we migrate this from
// axios to fetch).
@ -744,7 +744,7 @@ class PhotosDownloadClient implements DownloadClient {
}
};
return retryAsyncFunction(getFile);
return retryAsyncOperation(getFile);
}
}
@ -848,7 +848,7 @@ class PublicAlbumsDownloadClient implements DownloadClient {
}
};
const resp = await retryAsyncFunction(getFile);
const resp = await retryAsyncOperation(getFile);
if (resp.data === undefined) throw Error("request failed");
// TODO: Remove this cast (it won't be needed when we migrate this from
// axios to fetch).
@ -887,6 +887,6 @@ class PublicAlbumsDownloadClient implements DownloadClient {
}
};
return retryAsyncFunction(getFile);
return retryAsyncOperation(getFile);
}
}

View File

@ -1,30 +0,0 @@
import { wait } from "@/utils/promise";
export async function retryAsyncFunction<T>(
request: (abort?: () => void) => Promise<T>,
waitTimeBeforeNextTry?: number[],
// Need to use @ts-ignore since this same file is currently included with
// varying tsconfigs, and the error is only surfaced in the stricter ones of
// them.
//
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore TSC fails to detect that the exit of the loop is unreachable
): Promise<T> {
if (!waitTimeBeforeNextTry) waitTimeBeforeNextTry = [2000, 5000, 10000];
for (
let attemptNumber = 0;
attemptNumber <= waitTimeBeforeNextTry.length;
attemptNumber++
) {
try {
const resp = await request();
return resp;
} catch (e) {
if (attemptNumber === waitTimeBeforeNextTry.length) {
throw e;
}
await wait(waitTimeBeforeNextTry[attemptNumber]!);
}
}
}