From 0eb1d2925956c488cc6c1f7de11df1659fe0235a Mon Sep 17 00:00:00 2001 From: static Date: Sun, 4 Jan 2026 17:54:42 +0900 Subject: [PATCH] =?UTF-8?q?Scheduler=20=ED=81=B4=EB=9E=98=EC=8A=A4?= =?UTF-8?q?=EC=9D=98=20=EC=8A=A4=EC=BC=80=EC=A5=B4=EB=A7=81=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/modules/file/upload.svelte.ts | 127 +++++++++++++------------- src/lib/modules/scheduler.ts | 35 ++++--- 2 files changed, 83 insertions(+), 79 deletions(-) diff --git a/src/lib/modules/file/upload.svelte.ts b/src/lib/modules/file/upload.svelte.ts index 5a23cc1..a632eb5 100644 --- a/src/lib/modules/file/upload.svelte.ts +++ b/src/lib/modules/file/upload.svelte.ts @@ -196,77 +196,74 @@ export const uploadFile = async ( }); const state = uploadingFiles.at(-1)!; - return await scheduler.schedule( - async () => file.size, - async () => { - state.status = "encryption-pending"; + return await scheduler.schedule(file.size, async () => { + state.status = "encryption-pending"; - try { - const { fileBuffer, fileSigned } = await requestDuplicateFileScan( - file, - hmacSecret, - onDuplicate, - ); - if (!fileBuffer || !fileSigned) { - state.status = "canceled"; - uploadingFiles = uploadingFiles.filter((file) => file !== state); - return undefined; - } + try { + const { fileBuffer, fileSigned } = await requestDuplicateFileScan( + file, + hmacSecret, + onDuplicate, + ); + if (!fileBuffer || !fileSigned) { + state.status = "canceled"; + uploadingFiles = uploadingFiles.filter((file) => file !== state); + return undefined; + } - const { - dataKeyWrapped, - dataKeyVersion, - fileType, - fileEncrypted, - fileEncryptedHash, - nameEncrypted, - createdAtEncrypted, - lastModifiedAtEncrypted, - thumbnail, - } = await encryptFile(state, file, fileBuffer, masterKey); + const { + dataKeyWrapped, + dataKeyVersion, + fileType, + fileEncrypted, + fileEncryptedHash, + nameEncrypted, + createdAtEncrypted, + lastModifiedAtEncrypted, + thumbnail, + } = await encryptFile(state, file, fileBuffer, masterKey); - const form = new FormData(); - form.set( + const form = new FormData(); + form.set( + "metadata", + JSON.stringify({ + parent: parentId, + mekVersion: masterKey.version, + dek: dataKeyWrapped, + dekVersion: dataKeyVersion.toISOString(), + hskVersion: hmacSecret.version, + contentHmac: fileSigned, + contentType: fileType, + contentIv: fileEncrypted.iv, + name: nameEncrypted.ciphertext, + nameIv: nameEncrypted.iv, + createdAt: createdAtEncrypted?.ciphertext, + createdAtIv: createdAtEncrypted?.iv, + lastModifiedAt: lastModifiedAtEncrypted.ciphertext, + lastModifiedAtIv: lastModifiedAtEncrypted.iv, + } satisfies FileUploadRequest), + ); + form.set("content", new Blob([fileEncrypted.ciphertext])); + form.set("checksum", fileEncryptedHash); + + let thumbnailForm = null; + if (thumbnail) { + thumbnailForm = new FormData(); + thumbnailForm.set( "metadata", JSON.stringify({ - parent: parentId, - mekVersion: masterKey.version, - dek: dataKeyWrapped, dekVersion: dataKeyVersion.toISOString(), - hskVersion: hmacSecret.version, - contentHmac: fileSigned, - contentType: fileType, - contentIv: fileEncrypted.iv, - name: nameEncrypted.ciphertext, - nameIv: nameEncrypted.iv, - createdAt: createdAtEncrypted?.ciphertext, - createdAtIv: createdAtEncrypted?.iv, - lastModifiedAt: lastModifiedAtEncrypted.ciphertext, - lastModifiedAtIv: lastModifiedAtEncrypted.iv, - } satisfies FileUploadRequest), + contentIv: thumbnail.iv, + } satisfies FileThumbnailUploadRequest), ); - form.set("content", new Blob([fileEncrypted.ciphertext])); - form.set("checksum", fileEncryptedHash); - - let thumbnailForm = null; - if (thumbnail) { - thumbnailForm = new FormData(); - thumbnailForm.set( - "metadata", - JSON.stringify({ - dekVersion: dataKeyVersion.toISOString(), - contentIv: thumbnail.iv, - } satisfies FileThumbnailUploadRequest), - ); - thumbnailForm.set("content", new Blob([thumbnail.ciphertext])); - } - - const { fileId } = await requestFileUpload(state, form, thumbnailForm); - return { fileId, fileBuffer, thumbnailBuffer: thumbnail?.plaintext }; - } catch (e) { - state.status = "error"; - throw e; + thumbnailForm.set("content", new Blob([thumbnail.ciphertext])); } - }, - ); + + const { fileId } = await requestFileUpload(state, form, thumbnailForm); + return { fileId, fileBuffer, thumbnailBuffer: thumbnail?.plaintext }; + } catch (e) { + state.status = "error"; + throw e; + } + }); }; diff --git a/src/lib/modules/scheduler.ts b/src/lib/modules/scheduler.ts index 31ce449..4216db3 100644 --- a/src/lib/modules/scheduler.ts +++ b/src/lib/modules/scheduler.ts @@ -1,39 +1,46 @@ export class Scheduler { - private tasks = 0; + private isEstimating = false; private memoryUsage = 0; private queue: (() => void)[] = []; - constructor(public memoryLimit = 100 * 1024 * 1024 /* 100 MiB */) {} + constructor(public readonly memoryLimit = 100 * 1024 * 1024 /* 100 MiB */) {} private next() { - if (this.memoryUsage < this.memoryLimit) { - this.queue.shift()?.(); + if (!this.isEstimating && this.memoryUsage < this.memoryLimit) { + const resolve = this.queue.shift(); + if (resolve) { + this.isEstimating = true; + resolve(); + } } } - async schedule(estimateMemoryUsage: () => Promise, task: () => Promise) { - if (this.tasks++ > 0) { + async schedule( + estimateMemoryUsage: number | (() => number | Promise), + task: () => Promise, + ) { + if (this.isEstimating || this.memoryUsage >= this.memoryLimit) { await new Promise((resolve) => { this.queue.push(resolve); }); - } - - while (this.memoryUsage >= this.memoryLimit) { - await new Promise((resolve) => { - this.queue.unshift(resolve); - }); + } else { + this.isEstimating = true; } let taskMemoryUsage = 0; try { - taskMemoryUsage = await estimateMemoryUsage(); + taskMemoryUsage = + typeof estimateMemoryUsage === "number" ? estimateMemoryUsage : await estimateMemoryUsage(); this.memoryUsage += taskMemoryUsage; + } finally { + this.isEstimating = false; this.next(); + } + try { return await task(); } finally { - this.tasks--; this.memoryUsage -= taskMemoryUsage; this.next(); }