perf: prevent import hanging with timeouts and batching
- verifyAndFixInteractionModesCount: paginated DB queries (500/batch) instead of loading all items into memory at once - withFurniDataLock: add 60s chain timeout to prevent deadlocks when a lock holder stalls or crashes - withGamedataLock: same timeout protection for gamedata locks - conversion-pool: add 60s per-job timeout, fall back to main thread - furni/batch: abort signal + post_import progress events + skip post-import steps when client disconnects - furni/batch-regen: abort signal + early exit when disconnected - clone/sync-all: pre-fetch furnidata once per source instead of per item (eliminates 2000+ redundant fetches) - sse-client: add 60s idle timeout to prevent infinite hangs
This commit is contained in:
1 parent
f70d96b81c
commit
7556b1f3fa
5 files changed
+222
-93
No files matched your search
@@ -47,9 +47,16 @@ export const POST = withAdmin(
|
||||
const { swfDir, nitroDir } = await getFurniAssetDirs();
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
// Wire client disconnect to abort controller
|
||||
let aborted = false;
|
||||
request.signal.addEventListener("abort", () => {
|
||||
aborted = true;
|
||||
});
|
||||
|
||||
const stream = new ReadableStream({
|
||||
async start(controller) {
|
||||
const send = (data: unknown) => {
|
||||
if (aborted) return;
|
||||
try {
|
||||
controller.enqueue(
|
||||
encoder.encode(`data: ${JSON.stringify(data)}\n\n`),
|
||||
@@ -240,6 +247,18 @@ export const POST = withAdmin(
|
||||
await new Promise((r) => setImmediate(r));
|
||||
}
|
||||
|
||||
if (aborted) {
|
||||
send({
|
||||
type: "regen_complete",
|
||||
succeeded,
|
||||
failed,
|
||||
skipped,
|
||||
aborted: true,
|
||||
});
|
||||
controller.close();
|
||||
return;
|
||||
}
|
||||
|
||||
// Batch write FurnitureData.json
|
||||
if (furniDataEntries.length > 0) {
|
||||
try {
|
||||
@@ -291,25 +310,27 @@ export const POST = withAdmin(
|
||||
);
|
||||
}
|
||||
|
||||
send({
|
||||
type: "regen_complete",
|
||||
succeeded,
|
||||
failed,
|
||||
skipped,
|
||||
furniDataAdded: furniDataEntries.length,
|
||||
ownershipFixed: ownership.fixed,
|
||||
nitrosSynced: assetsSynced.copiedNitros,
|
||||
iconsSynced: assetsSynced.copiedIcons,
|
||||
furniDataFixedIds: furniReconcile.fixedIds,
|
||||
furniDataMissing: furniReconcile.missing,
|
||||
furniDataConflicts: furniReconcile.conflicts,
|
||||
offerRebuildChecked: offerRebuild.checked,
|
||||
offerRebuildFixed: offerRebuild.fixed,
|
||||
spriteChecked: spriteVerify.checked,
|
||||
spriteFixed: spriteVerify.fixed,
|
||||
languages,
|
||||
duration: Date.now() - startTime,
|
||||
});
|
||||
if (!aborted) {
|
||||
send({
|
||||
type: "regen_complete",
|
||||
succeeded,
|
||||
failed,
|
||||
skipped,
|
||||
furniDataAdded: furniDataEntries.length,
|
||||
ownershipFixed: ownership.fixed,
|
||||
nitrosSynced: assetsSynced.copiedNitros,
|
||||
iconsSynced: assetsSynced.copiedIcons,
|
||||
furniDataFixedIds: furniReconcile.fixedIds,
|
||||
furniDataMissing: furniReconcile.missing,
|
||||
furniDataConflicts: furniReconcile.conflicts,
|
||||
offerRebuildChecked: offerRebuild.checked,
|
||||
offerRebuildFixed: offerRebuild.fixed,
|
||||
spriteChecked: spriteVerify.checked,
|
||||
spriteFixed: spriteVerify.fixed,
|
||||
languages,
|
||||
duration: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
|
||||
controller.close();
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user