mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 08:06:56 +00:00
Scheduler 클래스의 스케쥴링 로직 개선
This commit is contained in:
@@ -196,77 +196,74 @@ export const uploadFile = async (
|
|||||||
});
|
});
|
||||||
const state = uploadingFiles.at(-1)!;
|
const state = uploadingFiles.at(-1)!;
|
||||||
|
|
||||||
return await scheduler.schedule(
|
return await scheduler.schedule(file.size, async () => {
|
||||||
async () => file.size,
|
state.status = "encryption-pending";
|
||||||
async () => {
|
|
||||||
state.status = "encryption-pending";
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const { fileBuffer, fileSigned } = await requestDuplicateFileScan(
|
const { fileBuffer, fileSigned } = await requestDuplicateFileScan(
|
||||||
file,
|
file,
|
||||||
hmacSecret,
|
hmacSecret,
|
||||||
onDuplicate,
|
onDuplicate,
|
||||||
);
|
);
|
||||||
if (!fileBuffer || !fileSigned) {
|
if (!fileBuffer || !fileSigned) {
|
||||||
state.status = "canceled";
|
state.status = "canceled";
|
||||||
uploadingFiles = uploadingFiles.filter((file) => file !== state);
|
uploadingFiles = uploadingFiles.filter((file) => file !== state);
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {
|
const {
|
||||||
dataKeyWrapped,
|
dataKeyWrapped,
|
||||||
dataKeyVersion,
|
dataKeyVersion,
|
||||||
fileType,
|
fileType,
|
||||||
fileEncrypted,
|
fileEncrypted,
|
||||||
fileEncryptedHash,
|
fileEncryptedHash,
|
||||||
nameEncrypted,
|
nameEncrypted,
|
||||||
createdAtEncrypted,
|
createdAtEncrypted,
|
||||||
lastModifiedAtEncrypted,
|
lastModifiedAtEncrypted,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
} = await encryptFile(state, file, fileBuffer, masterKey);
|
} = await encryptFile(state, file, fileBuffer, masterKey);
|
||||||
|
|
||||||
const form = new FormData();
|
const form = new FormData();
|
||||||
form.set(
|
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",
|
"metadata",
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
parent: parentId,
|
|
||||||
mekVersion: masterKey.version,
|
|
||||||
dek: dataKeyWrapped,
|
|
||||||
dekVersion: dataKeyVersion.toISOString(),
|
dekVersion: dataKeyVersion.toISOString(),
|
||||||
hskVersion: hmacSecret.version,
|
contentIv: thumbnail.iv,
|
||||||
contentHmac: fileSigned,
|
} satisfies FileThumbnailUploadRequest),
|
||||||
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]));
|
thumbnailForm.set("content", new Blob([thumbnail.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;
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
);
|
const { fileId } = await requestFileUpload(state, form, thumbnailForm);
|
||||||
|
return { fileId, fileBuffer, thumbnailBuffer: thumbnail?.plaintext };
|
||||||
|
} catch (e) {
|
||||||
|
state.status = "error";
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,39 +1,46 @@
|
|||||||
export class Scheduler<T = void> {
|
export class Scheduler<T = void> {
|
||||||
private tasks = 0;
|
private isEstimating = false;
|
||||||
private memoryUsage = 0;
|
private memoryUsage = 0;
|
||||||
private queue: (() => void)[] = [];
|
private queue: (() => void)[] = [];
|
||||||
|
|
||||||
constructor(public memoryLimit = 100 * 1024 * 1024 /* 100 MiB */) {}
|
constructor(public readonly memoryLimit = 100 * 1024 * 1024 /* 100 MiB */) {}
|
||||||
|
|
||||||
private next() {
|
private next() {
|
||||||
if (this.memoryUsage < this.memoryLimit) {
|
if (!this.isEstimating && this.memoryUsage < this.memoryLimit) {
|
||||||
this.queue.shift()?.();
|
const resolve = this.queue.shift();
|
||||||
|
if (resolve) {
|
||||||
|
this.isEstimating = true;
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async schedule(estimateMemoryUsage: () => Promise<number>, task: () => Promise<T>) {
|
async schedule(
|
||||||
if (this.tasks++ > 0) {
|
estimateMemoryUsage: number | (() => number | Promise<number>),
|
||||||
|
task: () => Promise<T>,
|
||||||
|
) {
|
||||||
|
if (this.isEstimating || this.memoryUsage >= this.memoryLimit) {
|
||||||
await new Promise<void>((resolve) => {
|
await new Promise<void>((resolve) => {
|
||||||
this.queue.push(resolve);
|
this.queue.push(resolve);
|
||||||
});
|
});
|
||||||
}
|
} else {
|
||||||
|
this.isEstimating = true;
|
||||||
while (this.memoryUsage >= this.memoryLimit) {
|
|
||||||
await new Promise<void>((resolve) => {
|
|
||||||
this.queue.unshift(resolve);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let taskMemoryUsage = 0;
|
let taskMemoryUsage = 0;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
taskMemoryUsage = await estimateMemoryUsage();
|
taskMemoryUsage =
|
||||||
|
typeof estimateMemoryUsage === "number" ? estimateMemoryUsage : await estimateMemoryUsage();
|
||||||
this.memoryUsage += taskMemoryUsage;
|
this.memoryUsage += taskMemoryUsage;
|
||||||
|
} finally {
|
||||||
|
this.isEstimating = false;
|
||||||
this.next();
|
this.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
return await task();
|
return await task();
|
||||||
} finally {
|
} finally {
|
||||||
this.tasks--;
|
|
||||||
this.memoryUsage -= taskMemoryUsage;
|
this.memoryUsage -= taskMemoryUsage;
|
||||||
this.next();
|
this.next();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user