fix and update
This commit is contained in:
@@ -680,8 +680,6 @@ export function buildSystemSettingPayload(setting: Pick<SystemSetting, 'key' | '
|
||||
|
||||
const GATEWAY_RETRY_STATUS = new Set([502, 503, 504]);
|
||||
const GATEWAY_RETRY_ATTEMPTS = 4;
|
||||
const GATEWAY_PROBE_MAX_ATTEMPTS = 48;
|
||||
const GATEWAY_PROBE_BASE_DELAY_MS = 450;
|
||||
const GATEWAY_STABLE_PROBE_INTERVAL_MS = 800;
|
||||
const GATEWAY_STABLE_MAX_ROUNDS = 18;
|
||||
const API_MAX_CONCURRENT = 6;
|
||||
@@ -696,6 +694,7 @@ function sleep(ms: number): Promise<void> {
|
||||
|
||||
let gatewayReadyResolved = false;
|
||||
let gatewayReadyPromise: Promise<void> | null = null;
|
||||
let stableGatewayPromise: Promise<void> | null = null;
|
||||
let gatewayReadyAt = 0;
|
||||
let activeApiRequests = 0;
|
||||
const apiRequestWaiters: Array<() => void> = [];
|
||||
@@ -703,11 +702,13 @@ const apiRequestWaiters: Array<() => void> = [];
|
||||
export function resetApiGatewayWarmup() {
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyPromise = null;
|
||||
stableGatewayPromise = null;
|
||||
}
|
||||
|
||||
export function invalidateApiGatewayReady() {
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
stableGatewayPromise = null;
|
||||
if (typeof window !== 'undefined') {
|
||||
window.dispatchEvent(new CustomEvent(API_NOT_READY_EVENT));
|
||||
}
|
||||
@@ -745,26 +746,34 @@ export function subscribeApiNotReady(listener: () => void): () => void {
|
||||
/** Несколько подряд успешных /health через nginx — защита от «один OK, потом 502». */
|
||||
export async function waitForStableGateway(stableProbes = 3): Promise<void> {
|
||||
if (typeof window === 'undefined') return;
|
||||
if (gatewayReadyResolved && stableProbes <= 1) return;
|
||||
if (stableGatewayPromise) return stableGatewayPromise;
|
||||
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
stableGatewayPromise = (async () => {
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
|
||||
let streak = 0;
|
||||
for (let attempt = 0; attempt < GATEWAY_STABLE_MAX_ROUNDS && streak < stableProbes; attempt += 1) {
|
||||
if (await probeGatewayAlive()) {
|
||||
streak += 1;
|
||||
if (streak >= stableProbes) {
|
||||
markApiGatewayReady();
|
||||
return;
|
||||
let streak = 0;
|
||||
for (let attempt = 0; attempt < GATEWAY_STABLE_MAX_ROUNDS && streak < stableProbes; attempt += 1) {
|
||||
if (await probeGatewayAlive()) {
|
||||
streak += 1;
|
||||
if (streak >= stableProbes) {
|
||||
markApiGatewayReady();
|
||||
return;
|
||||
}
|
||||
await sleep(GATEWAY_STABLE_PROBE_INTERVAL_MS);
|
||||
} else {
|
||||
streak = 0;
|
||||
await sleep(Math.min(900 + attempt * 300, 2800));
|
||||
}
|
||||
await sleep(GATEWAY_STABLE_PROBE_INTERVAL_MS);
|
||||
} else {
|
||||
streak = 0;
|
||||
await sleep(Math.min(900 + attempt * 300, 2800));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ApiError('Сервер API временно недоступен. Попробуйте обновить страницу.', 503, 'GATEWAY_UNAVAILABLE');
|
||||
throw new ApiError('Сервер API временно недоступен. Попробуйте обновить страницу.', 503, 'GATEWAY_UNAVAILABLE');
|
||||
})().finally(() => {
|
||||
stableGatewayPromise = null;
|
||||
});
|
||||
|
||||
return stableGatewayPromise;
|
||||
}
|
||||
|
||||
async function acquireApiSlot(): Promise<void> {
|
||||
@@ -799,23 +808,7 @@ async function probeGatewayAlive(): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
async function waitForGatewayAlive(): Promise<void> {
|
||||
if (gatewayReadyResolved) return;
|
||||
|
||||
for (let attempt = 0; attempt < GATEWAY_PROBE_MAX_ATTEMPTS; attempt += 1) {
|
||||
if (await probeGatewayAlive()) {
|
||||
markApiGatewayReady();
|
||||
return;
|
||||
}
|
||||
if (attempt + 1 < GATEWAY_PROBE_MAX_ATTEMPTS) {
|
||||
await sleep(Math.min(GATEWAY_PROBE_BASE_DELAY_MS + attempt * 280, 2800));
|
||||
}
|
||||
}
|
||||
|
||||
throw new ApiError('Сервер API временно недоступен. Попробуйте обновить страницу.', 503, 'GATEWAY_UNAVAILABLE');
|
||||
}
|
||||
|
||||
/** Один общий прогрев: все apiFetch ждут успешный /health, без раннего «fail-open». */
|
||||
/** Один общий прогрев: все apiFetch ждут стабильный /health через nginx. */
|
||||
export async function ensureApiGatewayReady(force = false): Promise<void> {
|
||||
if (typeof window === 'undefined') return;
|
||||
if (gatewayReadyResolved && !force) return;
|
||||
@@ -823,9 +816,12 @@ export async function ensureApiGatewayReady(force = false): Promise<void> {
|
||||
|
||||
if (force) {
|
||||
gatewayReadyResolved = false;
|
||||
gatewayReadyAt = 0;
|
||||
stableGatewayPromise = null;
|
||||
}
|
||||
|
||||
gatewayReadyPromise = waitForGatewayAlive().finally(() => {
|
||||
const probes = force ? 2 : 1;
|
||||
gatewayReadyPromise = waitForStableGateway(probes).finally(() => {
|
||||
gatewayReadyPromise = null;
|
||||
});
|
||||
|
||||
@@ -848,7 +844,7 @@ async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Re
|
||||
if (GATEWAY_RETRY_STATUS.has(response.status) && attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||
invalidateApiGatewayReady();
|
||||
try {
|
||||
await waitForStableGateway(2);
|
||||
await ensureApiGatewayReady(true);
|
||||
} catch {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
}
|
||||
@@ -860,7 +856,7 @@ async function fetchWithGatewayRetry(url: string, init: RequestInit): Promise<Re
|
||||
if (attempt + 1 < GATEWAY_RETRY_ATTEMPTS) {
|
||||
invalidateApiGatewayReady();
|
||||
try {
|
||||
await waitForStableGateway(2);
|
||||
await ensureApiGatewayReady(true);
|
||||
} catch {
|
||||
await sleep(gatewayRetryDelay(attempt));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user