fix and update
This commit is contained in:
@@ -677,13 +677,15 @@ export function buildSystemSettingPayload(setting: Pick<SystemSetting, 'key' | '
|
||||
}
|
||||
|
||||
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
||||
const GATEWAY_RETRY_ATTEMPTS = 2;
|
||||
const GATEWAY_RETRY_ATTEMPTS = 4;
|
||||
const GATEWAY_PROBE_MAX_ATTEMPTS = 48;
|
||||
const GATEWAY_PROBE_BASE_DELAY_MS = 450;
|
||||
const API_MAX_CONCURRENT = 8;
|
||||
const API_BURST_MAX_CONCURRENT = 3;
|
||||
const API_BURST_WINDOW_MS = 3000;
|
||||
const GATEWAY_STABLE_PROBES = 3;
|
||||
const API_MAX_CONCURRENT = 6;
|
||||
const API_BURST_MAX_CONCURRENT = 2;
|
||||
const API_BURST_WINDOW_MS = 5000;
|
||||
const API_READY_EVENT = 'idp-api-ready';
|
||||
const API_NOT_READY_EVENT = 'idp-api-not-ready';
|
||||
|
||||
function sleep(ms: number): Promise<void> {
|
||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
@@ -703,6 +705,9 @@ export function resetApiGatewayWarmup() {
|
||||
export function invalidateApiGatewayReady() {
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(new CustomEvent(API_NOT_READY_EVENT));
|
||||
}
|
||||
}
|
||||
|
||||
export function isApiGatewayReady() {
|
||||
@@ -728,6 +733,37 @@ export function subscribeApiReady(listener: () => void): () => void {
|
||||
return () => window.removeEventListener(API_READY_EVENT, listener);
|
||||
}
|
||||
|
||||
export function subscribeApiNotReady(listener: () => void): () => void {
|
||||
if (typeof window === 'undefined') return () => undefined;
|
||||
window.addEventListener(API_NOT_READY_EVENT, listener);
|
||||
return () => window.removeEventListener(API_NOT_READY_EVENT, listener);
|
||||
}
|
||||
|
||||
/** Несколько подряд успешных /health через nginx — защита от «один OK, потом 502». */
|
||||
export async function waitForStableGateway(stableProbes = GATEWAY_STABLE_PROBES): Promise<void> {
|
||||
if (typeof window === 'undefined') return;
|
||||
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
|
||||
let streak = 0;
|
||||
for (let attempt = 0; attempt < 40 && streak < stableProbes; attempt += 1) {
|
||||
if (await probeGatewayAlive()) {
|
||||
streak += 1;
|
||||
if (streak >= stableProbes) {
|
||||
markApiGatewayReady();
|
||||
return;
|
||||
}
|
||||
await sleep(400);
|
||||
} else {
|
||||
streak = 0;
|
||||
await sleep(Math.min(500 + attempt * 220, 2200));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ApiError('Сервер API временно недоступен. Попробуйте обновить страницу.', 503, 'GATEWAY_UNAVAILABLE');
|
||||
}
|
||||
|
||||
async function acquireApiSlot(): Promise<void> {
|
||||
const inBurst = gatewayReadyAt > 0 && Date.now() - gatewayReadyAt < API_BURST_WINDOW_MS;
|
||||
const limit = inBurst ? API_BURST_MAX_CONCURRENT : API_MAX_CONCURRENT;
|
||||
@@ -807,14 +843,24 @@ async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Re
|
||||
markApiGatewayReady();
|
||||
}
|
||||
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
invalidateApiGatewayReady();
|
||||
try {
|
||||
await waitForStableGateway(2);
|
||||
} catch {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return response;
|
||||
} catch (error) {
|
||||
lastNetworkError = error;
|
||||
if (attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
invalidateApiGatewayReady();
|
||||
try {
|
||||
await waitForStableGateway(2);
|
||||
} catch {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
throw mapFetchError(error);
|
||||
|
||||
Reference in New Issue
Block a user