fix: prevent import hanging by adding abort signals and idle timeouts
- Furni batch: wire request.signal to abort controller, send post_import progress events, skip post-import steps when aborted - Clone sync-all: pre-fetch furnidata once per source instead of per item (eliminates 2000+ redundant DB reads + HTTP requests) - SSE client: add 60s idle timeout to prevent infinite hangs when server stops responding
This commit is contained in:
1 parent
96c33efced
commit
f70d96b81c
3 files changed
+179
-85
No files matched your search
+24
-2
@@ -3,26 +3,46 @@ import { adminFetch } from "@/lib/admin-fetch";
|
||||
|
||||
export type SseEvent = Record<string, unknown>;
|
||||
|
||||
/** Default idle timeout: if no SSE event arrives for this long, abort. */
|
||||
const DEFAULT_IDLE_TIMEOUT_MS = 60_000;
|
||||
|
||||
/**
|
||||
* Read an SSE response body and invoke `onEvent` for each `data:` JSON payload.
|
||||
* Aborts if no event arrives within `idleTimeoutMs` (default 60s).
|
||||
*/
|
||||
export async function readSseStream(
|
||||
body: ReadableStream<Uint8Array>,
|
||||
onEvent: (event: SseEvent) => void,
|
||||
signal?: AbortSignal,
|
||||
idleTimeoutMs: number = DEFAULT_IDLE_TIMEOUT_MS,
|
||||
): Promise<void> {
|
||||
const reader = body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buf = "";
|
||||
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let done = false;
|
||||
|
||||
const resetIdleTimer = () => {
|
||||
if (idleTimer) clearTimeout(idleTimer);
|
||||
if (idleTimeoutMs > 0) {
|
||||
idleTimer = setTimeout(() => {
|
||||
if (!done) {
|
||||
reader.cancel("SSE idle timeout").catch(() => {});
|
||||
}
|
||||
}, idleTimeoutMs);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
resetIdleTimer();
|
||||
while (true) {
|
||||
if (signal?.aborted) {
|
||||
await reader.cancel();
|
||||
break;
|
||||
}
|
||||
const { value, done } = await reader.read();
|
||||
if (done) break;
|
||||
const { value, done: streamDone } = await reader.read();
|
||||
if (streamDone) break;
|
||||
resetIdleTimer();
|
||||
buf += decoder.decode(value, { stream: true });
|
||||
const parts = buf.split("\n\n");
|
||||
buf = parts.pop() ?? "";
|
||||
@@ -36,6 +56,8 @@ export async function readSseStream(
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
done = true;
|
||||
if (idleTimer) clearTimeout(idleTimer);
|
||||
reader.releaseLock();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user