From 74e8bb6d64b79b6d34c6dafd9b3813934fa93625 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 17 Mar 2026 11:59:50 +0000 Subject: [PATCH] fix(proxies): batch proxy removal during check to prevent backend overload Refactored the proxy deletion logic in both `AddButton` and `ExtraHeader` components to use sequential chunking. Failed proxies are now batched into chunks of 50 and deleted strictly sequentially, rather than firing concurrent API requests for every single failure. This prevents backend race conditions and potential timeouts when checking large lists of proxies. Co-authored-by: cobrayigamer <142711835+cobrayigamer@users.noreply.github.com> --- .../_dashboard/instance/$instance/proxies.tsx | 52 ++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/src/routes/_dashboard/instance/$instance/proxies.tsx b/src/routes/_dashboard/instance/$instance/proxies.tsx index f96a7f96..7bc2daf8 100644 --- a/src/routes/_dashboard/instance/$instance/proxies.tsx +++ b/src/routes/_dashboard/instance/$instance/proxies.tsx @@ -595,11 +595,30 @@ function AddButton() { }); const abortController = new AbortController(); + + const failedProxies: string[] = []; + let removalPromise: Promise = Promise.resolve(); + + const flushFailedProxies = async (force = false) => { + while (failedProxies.length >= 50 || (force && failedProxies.length > 0)) { + const chunk = failedProxies.splice(0, 50); + removalPromise = removalPromise.then(() => + removeProxiesBatchMutation(chunk).catch(console.error), + ); + await removalPromise; + } + }; + + const handleCompletion = async () => { + await flushFailedProxies(true); + }; + const loadingData: ExternalToast = { cancel: { label: t("common:cancel"), onClick: () => { abortController.abort(); + void handleCompletion(); }, }, }; @@ -629,6 +648,9 @@ function AddButton() { const data = r.data; switch (data.oneofKind) { case "end": { + if (!abortController.signal.aborted) { + await flushFailedProxies(true); + } toast.success( t("proxy.checkToast.success", { count: failed, @@ -651,7 +673,8 @@ function AddButton() { failed++; const proxyToRemove = data.single.proxy; if (proxyToRemove) { - await removeProxiesBatchMutation([proxyToRemove.address]); + failedProxies.push(proxyToRemove.address); + void flushFailedProxies(false); } } @@ -666,6 +689,7 @@ function AddButton() { }); responses.onError((e) => { console.error(e); + void handleCompletion(); toast.error(t("proxy.checkToast.error"), { id: toastId, cancel: undefined, @@ -862,11 +886,30 @@ function ExtraHeader(props: { table: ReactTable }) { .rows.map((r) => r.original); const abortController = new AbortController(); + + const failedProxies: string[] = []; + let removalPromise: Promise = Promise.resolve(); + + const flushFailedProxies = async (force = false) => { + while (failedProxies.length >= 50 || (force && failedProxies.length > 0)) { + const chunk = failedProxies.splice(0, 50); + removalPromise = removalPromise.then(() => + removeProxiesBatchMutation(chunk).catch(console.error), + ); + await removalPromise; + } + }; + + const handleCompletion = async () => { + await flushFailedProxies(true); + }; + const loadingData: ExternalToast = { cancel: { label: t("common:cancel"), onClick: () => { abortController.abort(); + void handleCompletion(); }, }, }; @@ -896,6 +939,9 @@ function ExtraHeader(props: { table: ReactTable }) { const data = r.data; switch (data.oneofKind) { case "end": { + if (!abortController.signal.aborted) { + await flushFailedProxies(true); + } toast.success( t("proxy.checkToast.success", { count: failed, @@ -919,7 +965,8 @@ function ExtraHeader(props: { table: ReactTable }) { // Remove the invalid proxy by address const proxyToRemove = data.single.proxy; if (proxyToRemove) { - await removeProxiesBatchMutation([proxyToRemove.address]); + failedProxies.push(proxyToRemove.address); + void flushFailedProxies(false); } } @@ -934,6 +981,7 @@ function ExtraHeader(props: { table: ReactTable }) { }); responses.onError((e) => { console.error(e); + void handleCompletion(); toast.error(t("proxy.checkToast.error"), { id: toastId, cancel: undefined,