mirror of
https://github.com/kmc7468/arkvault.git
synced 2026-02-04 16:16:55 +00:00
청크 업로드 성능 개선 및 네트워크 속도를 더 정확하게 측정하도록 개선
This commit is contained in:
44
src/lib/utils/concurrency/BoundedQueue.ts
Normal file
44
src/lib/utils/concurrency/BoundedQueue.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
export class BoundedQueue<T> {
|
||||
private isClosed = false;
|
||||
private reservedCount = 0;
|
||||
private items: T[] = [];
|
||||
|
||||
private waitersNotFull: (() => void)[] = [];
|
||||
private waitersNotEmpty: (() => void)[] = [];
|
||||
|
||||
constructor(private readonly maxSize: number) {}
|
||||
|
||||
async push(item: T) {
|
||||
if (this.isClosed) {
|
||||
throw new Error("Queue closed");
|
||||
}
|
||||
|
||||
while (this.reservedCount >= this.maxSize) {
|
||||
await new Promise<void>((resolve) => this.waitersNotFull.push(resolve));
|
||||
if (this.isClosed) throw new Error("Queue closed");
|
||||
}
|
||||
|
||||
this.reservedCount++;
|
||||
this.items.push(item);
|
||||
this.waitersNotEmpty.shift()?.();
|
||||
}
|
||||
|
||||
async pop() {
|
||||
while (this.items.length === 0) {
|
||||
if (this.isClosed) return null;
|
||||
await new Promise<void>((resolve) => this.waitersNotEmpty.push(resolve));
|
||||
}
|
||||
|
||||
const item = this.items.shift()!;
|
||||
this.reservedCount--;
|
||||
this.waitersNotFull.shift()?.();
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
close() {
|
||||
this.isClosed = true;
|
||||
while (this.waitersNotEmpty.length > 0) this.waitersNotEmpty.shift()!();
|
||||
while (this.waitersNotFull.length > 0) this.waitersNotFull.shift()!();
|
||||
}
|
||||
}
|
||||
93
src/lib/utils/concurrency/HybridPromise.ts
Normal file
93
src/lib/utils/concurrency/HybridPromise.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
type MaybePromise<T> = T | Promise<T> | HybridPromise<T>;
|
||||
|
||||
type HybridPromiseState<T> =
|
||||
| { mode: "sync"; status: "fulfilled"; value: T }
|
||||
| { mode: "sync"; status: "rejected"; reason: unknown }
|
||||
| { mode: "async"; promise: Promise<T> };
|
||||
|
||||
export class HybridPromise<T> implements PromiseLike<T> {
|
||||
private isConsumed = false;
|
||||
|
||||
private constructor(private readonly state: HybridPromiseState<T>) {
|
||||
if (state.mode === "sync" && state.status === "rejected") {
|
||||
queueMicrotask(() => {
|
||||
if (!this.isConsumed) {
|
||||
throw state.reason;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
isSync(): boolean {
|
||||
return this.state.mode === "sync";
|
||||
}
|
||||
|
||||
toPromise(): Promise<T> {
|
||||
this.isConsumed = true;
|
||||
|
||||
if (this.state.mode === "async") return this.state.promise;
|
||||
return this.state.status === "fulfilled"
|
||||
? Promise.resolve(this.state.value)
|
||||
: Promise.reject(this.state.reason);
|
||||
}
|
||||
|
||||
static resolve<T>(value: MaybePromise<T>): HybridPromise<T> {
|
||||
if (value instanceof HybridPromise) return value;
|
||||
return new HybridPromise(
|
||||
value instanceof Promise
|
||||
? { mode: "async", promise: value }
|
||||
: { mode: "sync", status: "fulfilled", value },
|
||||
);
|
||||
}
|
||||
|
||||
static reject<T = never>(reason?: unknown): HybridPromise<T> {
|
||||
return new HybridPromise({ mode: "sync", status: "rejected", reason });
|
||||
}
|
||||
|
||||
then<TResult1 = T, TResult2 = never>(
|
||||
onfulfilled?: ((value: T) => MaybePromise<TResult1>) | null | undefined,
|
||||
onrejected?: ((reason: unknown) => MaybePromise<TResult2>) | null | undefined,
|
||||
): HybridPromise<TResult1 | TResult2> {
|
||||
this.isConsumed = true;
|
||||
|
||||
if (this.state.mode === "async") {
|
||||
return new HybridPromise({
|
||||
mode: "async",
|
||||
promise: this.state.promise.then(onfulfilled, onrejected) as any,
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
if (this.state.status === "fulfilled") {
|
||||
if (!onfulfilled) return HybridPromise.resolve(this.state.value as any);
|
||||
return HybridPromise.resolve(onfulfilled(this.state.value));
|
||||
} else {
|
||||
if (!onrejected) return HybridPromise.reject(this.state.reason);
|
||||
return HybridPromise.resolve(onrejected(this.state.reason));
|
||||
}
|
||||
} catch (e) {
|
||||
return HybridPromise.reject(e);
|
||||
}
|
||||
}
|
||||
|
||||
catch<TResult = never>(
|
||||
onrejected?: ((reason: unknown) => MaybePromise<TResult>) | null | undefined,
|
||||
): HybridPromise<T | TResult> {
|
||||
return this.then<T, TResult>(null, onrejected);
|
||||
}
|
||||
|
||||
finally(onfinally?: (() => void) | null | undefined): HybridPromise<T> {
|
||||
this.isConsumed = true;
|
||||
|
||||
if (this.state.mode === "async") {
|
||||
return new HybridPromise({ mode: "async", promise: this.state.promise.finally(onfinally) });
|
||||
}
|
||||
|
||||
try {
|
||||
onfinally?.();
|
||||
return new HybridPromise(this.state);
|
||||
} catch (e) {
|
||||
return HybridPromise.reject(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
48
src/lib/utils/concurrency/Scheduler.ts
Normal file
48
src/lib/utils/concurrency/Scheduler.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
export class Scheduler<T = void> {
|
||||
private isEstimating = false;
|
||||
private memoryUsage = 0;
|
||||
private queue: (() => void)[] = [];
|
||||
|
||||
constructor(public readonly memoryLimit = 100 * 1024 * 1024 /* 100 MiB */) {}
|
||||
|
||||
private next() {
|
||||
if (!this.isEstimating && this.memoryUsage < this.memoryLimit) {
|
||||
const resolve = this.queue.shift();
|
||||
if (resolve) {
|
||||
this.isEstimating = true;
|
||||
resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async schedule(
|
||||
estimateMemoryUsage: number | (() => number | Promise<number>),
|
||||
task: () => Promise<T>,
|
||||
) {
|
||||
if (this.isEstimating || this.memoryUsage >= this.memoryLimit) {
|
||||
await new Promise<void>((resolve) => {
|
||||
this.queue.push(resolve);
|
||||
});
|
||||
} else {
|
||||
this.isEstimating = true;
|
||||
}
|
||||
|
||||
let taskMemoryUsage = 0;
|
||||
|
||||
try {
|
||||
taskMemoryUsage =
|
||||
typeof estimateMemoryUsage === "number" ? estimateMemoryUsage : await estimateMemoryUsage();
|
||||
this.memoryUsage += taskMemoryUsage;
|
||||
} finally {
|
||||
this.isEstimating = false;
|
||||
this.next();
|
||||
}
|
||||
|
||||
try {
|
||||
return await task();
|
||||
} finally {
|
||||
this.memoryUsage -= taskMemoryUsage;
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
3
src/lib/utils/concurrency/index.ts
Normal file
3
src/lib/utils/concurrency/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from "./BoundedQueue";
|
||||
export * from "./HybridPromise";
|
||||
export * from "./Scheduler";
|
||||
Reference in New Issue
Block a user