cleanup compute core/server dead paths
This commit is contained in:
parent
6f68c169b6
commit
e6e029cf8a
6 changed files with 1 additions and 121 deletions
|
|
@ -1,11 +1,9 @@
|
||||||
import type {
|
import type {
|
||||||
WorkerOperationEvent,
|
WorkerOperationEvent,
|
||||||
WorkerOperationKind,
|
WorkerOperationKind,
|
||||||
WorkerOperationRequest,
|
|
||||||
WorkerOperationState,
|
WorkerOperationState,
|
||||||
} from '../api-contracts';
|
} from '../api-contracts';
|
||||||
|
|
||||||
export type OperationRequest = WorkerOperationRequest;
|
|
||||||
export type OperationState<Result = unknown> = WorkerOperationState<Result>;
|
export type OperationState<Result = unknown> = WorkerOperationState<Result>;
|
||||||
export type OperationEvent<Result = unknown> = WorkerOperationEvent<Result>;
|
export type OperationEvent<Result = unknown> = WorkerOperationEvent<Result>;
|
||||||
|
|
||||||
|
|
@ -63,5 +61,3 @@ export interface OperationLifecycleConfig {
|
||||||
opStaleMs: number;
|
opStaleMs: number;
|
||||||
maxCasRetries?: number;
|
maxCasRetries?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type OperationTransitionStatus = 'queued' | 'running' | 'succeeded' | 'failed';
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,3 @@ function resolveDocstoreDir(): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DOCSTORE_DIR = resolveDocstoreDir();
|
export const DOCSTORE_DIR = resolveDocstoreDir();
|
||||||
|
|
||||||
export function getDocstoreDir(): string {
|
|
||||||
return DOCSTORE_DIR;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,41 +0,0 @@
|
||||||
import { getComputeJobConcurrency } from '@openreader/compute-core';
|
|
||||||
|
|
||||||
export class ConcurrencyLimiter {
|
|
||||||
private readonly maxInFlight: number;
|
|
||||||
private inFlight = 0;
|
|
||||||
private readonly queue: Array<() => void> = [];
|
|
||||||
|
|
||||||
constructor(limit: number) {
|
|
||||||
this.maxInFlight = Number.isFinite(limit) && limit >= 1 ? Math.floor(limit) : 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
async run<T>(fn: () => Promise<T>): Promise<T> {
|
|
||||||
await this.acquire();
|
|
||||||
try {
|
|
||||||
return await fn();
|
|
||||||
} finally {
|
|
||||||
this.release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private acquire(): Promise<void> {
|
|
||||||
if (this.inFlight < this.maxInFlight) {
|
|
||||||
this.inFlight += 1;
|
|
||||||
return Promise.resolve();
|
|
||||||
}
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
this.queue.push(() => {
|
|
||||||
this.inFlight += 1;
|
|
||||||
resolve();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private release(): void {
|
|
||||||
this.inFlight = Math.max(0, this.inFlight - 1);
|
|
||||||
const next = this.queue.shift();
|
|
||||||
if (next) next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const LOCAL_COMPUTE_LIMITER = new ConcurrencyLimiter(getComputeJobConcurrency());
|
|
||||||
|
|
@ -3,13 +3,9 @@ import { isWorkerClientConfigAvailable, WorkerComputeBackend } from '@/lib/serve
|
||||||
|
|
||||||
let backendPromise: Promise<ComputeBackend> | null = null;
|
let backendPromise: Promise<ComputeBackend> | null = null;
|
||||||
|
|
||||||
async function createBackend(): Promise<ComputeBackend> {
|
|
||||||
return new WorkerComputeBackend();
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCompute(): Promise<ComputeBackend> {
|
export async function getCompute(): Promise<ComputeBackend> {
|
||||||
if (!backendPromise) {
|
if (!backendPromise) {
|
||||||
backendPromise = createBackend().catch((error) => {
|
backendPromise = Promise.resolve(new WorkerComputeBackend()).catch((error) => {
|
||||||
backendPromise = null;
|
backendPromise = null;
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,6 @@ import type {
|
||||||
import type { PdfLayoutProgress, WhisperAlignJobBase } from '@openreader/compute-core/api-contracts';
|
import type { PdfLayoutProgress, WhisperAlignJobBase } from '@openreader/compute-core/api-contracts';
|
||||||
import type { PdfLayoutJobResult, WorkerOperationState } from '@openreader/compute-core/api-contracts';
|
import type { PdfLayoutJobResult, WorkerOperationState } from '@openreader/compute-core/api-contracts';
|
||||||
|
|
||||||
export type ComputeMode = 'worker';
|
|
||||||
|
|
||||||
export interface WhisperAlignInput extends WhisperAlignJobBase {
|
export interface WhisperAlignInput extends WhisperAlignJobBase {
|
||||||
audioBuffer?: TTSAudioBuffer;
|
audioBuffer?: TTSAudioBuffer;
|
||||||
audioObjectKey?: string;
|
audioObjectKey?: string;
|
||||||
|
|
|
||||||
|
|
@ -1,65 +0,0 @@
|
||||||
import { expect, test } from '@playwright/test';
|
|
||||||
import { ConcurrencyLimiter } from '../../src/lib/server/compute/concurrency-limiter';
|
|
||||||
|
|
||||||
function sleep(ms: number): Promise<void> {
|
|
||||||
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
||||||
}
|
|
||||||
|
|
||||||
test.describe('compute-concurrency-limiter', () => {
|
|
||||||
test('caps active jobs at configured limit', async () => {
|
|
||||||
const limiter = new ConcurrencyLimiter(2);
|
|
||||||
let inFlight = 0;
|
|
||||||
let maxInFlightSeen = 0;
|
|
||||||
|
|
||||||
const jobs = Array.from({ length: 6 }, (_, i) => limiter.run(async () => {
|
|
||||||
inFlight += 1;
|
|
||||||
maxInFlightSeen = Math.max(maxInFlightSeen, inFlight);
|
|
||||||
await sleep(20 + (i % 2) * 5);
|
|
||||||
inFlight -= 1;
|
|
||||||
return i;
|
|
||||||
}));
|
|
||||||
|
|
||||||
const result = await Promise.all(jobs);
|
|
||||||
expect(result).toEqual([0, 1, 2, 3, 4, 5]);
|
|
||||||
expect(maxInFlightSeen).toBe(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('queues in FIFO order when saturated', async () => {
|
|
||||||
const limiter = new ConcurrencyLimiter(1);
|
|
||||||
const starts: number[] = [];
|
|
||||||
|
|
||||||
const one = limiter.run(async () => {
|
|
||||||
starts.push(1);
|
|
||||||
await sleep(25);
|
|
||||||
});
|
|
||||||
const two = limiter.run(async () => {
|
|
||||||
starts.push(2);
|
|
||||||
await sleep(5);
|
|
||||||
});
|
|
||||||
const three = limiter.run(async () => {
|
|
||||||
starts.push(3);
|
|
||||||
await sleep(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all([one, two, three]);
|
|
||||||
expect(starts).toEqual([1, 2, 3]);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('releases slot after failure', async () => {
|
|
||||||
const limiter = new ConcurrencyLimiter(1);
|
|
||||||
let startedSecond = false;
|
|
||||||
|
|
||||||
const first = limiter.run(async () => {
|
|
||||||
await sleep(10);
|
|
||||||
throw new Error('boom');
|
|
||||||
});
|
|
||||||
const second = limiter.run(async () => {
|
|
||||||
startedSecond = true;
|
|
||||||
return 'ok';
|
|
||||||
});
|
|
||||||
|
|
||||||
await expect(first).rejects.toThrow('boom');
|
|
||||||
await expect(second).resolves.toBe('ok');
|
|
||||||
expect(startedSecond).toBeTruthy();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Reference in a new issue