Circuit breaker
This commit is contained in:
parent
70df689701
commit
b7126b0d07
3 changed files with 234 additions and 3 deletions
|
|
@ -11,6 +11,40 @@ const PROCESSING_TIMEOUT_MS = 30000;
|
|||
const SANDBOX_URL = process.env.SANDBOX_URL || 'http://localhost:3002';
|
||||
const USE_SANDBOX = process.env.USE_SANDBOX !== 'false';
|
||||
|
||||
const CIRCUIT_BREAKER_THRESHOLD = 5;
|
||||
const CIRCUIT_BREAKER_RESET_MS = 60000;
|
||||
|
||||
class CircuitBreaker {
|
||||
private failures = 0;
|
||||
private lastFailure = 0;
|
||||
private open = false;
|
||||
|
||||
recordSuccess(): void {
|
||||
this.failures = 0;
|
||||
this.open = false;
|
||||
}
|
||||
|
||||
recordFailure(): void {
|
||||
this.failures++;
|
||||
this.lastFailure = Date.now();
|
||||
if (this.failures >= CIRCUIT_BREAKER_THRESHOLD) {
|
||||
this.open = true;
|
||||
}
|
||||
}
|
||||
|
||||
isOpen(): boolean {
|
||||
if (!this.open) return false;
|
||||
if (Date.now() - this.lastFailure > CIRCUIT_BREAKER_RESET_MS) {
|
||||
this.open = false;
|
||||
this.failures = 0;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const sandboxCircuit = new CircuitBreaker();
|
||||
|
||||
export const GEMINI_NATIVE_TYPES = [
|
||||
'application/pdf',
|
||||
'text/plain',
|
||||
|
|
@ -210,6 +244,10 @@ const LEGACY_TO_MODERN: Record<string, string> = {
|
|||
};
|
||||
|
||||
async function convertViaSandbox(buffer: Buffer, extension: string): Promise<Buffer> {
|
||||
if (sandboxCircuit.isOpen()) {
|
||||
throw new Error('CIRCUIT_OPEN');
|
||||
}
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), PROCESSING_TIMEOUT_MS);
|
||||
|
||||
|
|
@ -223,14 +261,20 @@ async function convertViaSandbox(buffer: Buffer, extension: string): Promise<Buf
|
|||
|
||||
if (!response.ok) {
|
||||
const error = await response.json().catch(() => ({ error: 'Conversion failed' }));
|
||||
sandboxCircuit.recordFailure();
|
||||
throw new Error(error.error || 'Document conversion failed');
|
||||
}
|
||||
|
||||
sandboxCircuit.recordSuccess();
|
||||
return Buffer.from(await response.arrayBuffer());
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.name === 'AbortError') {
|
||||
sandboxCircuit.recordFailure();
|
||||
throw new Error('Document conversion timed out. Try a smaller file.');
|
||||
}
|
||||
if (err instanceof Error && err.message !== 'CIRCUIT_OPEN') {
|
||||
sandboxCircuit.recordFailure();
|
||||
}
|
||||
throw err;
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
|
|
@ -282,9 +326,20 @@ async function convertLocally(buffer: Buffer, extension: string): Promise<Buffer
|
|||
}
|
||||
|
||||
async function extractWithLibreOffice(buffer: Buffer, extension: string, useOcr: boolean = false): Promise<string> {
|
||||
const convertedBuffer = USE_SANDBOX
|
||||
? await convertViaSandbox(buffer, extension)
|
||||
: await convertLocally(buffer, extension);
|
||||
let convertedBuffer: Buffer;
|
||||
|
||||
if (USE_SANDBOX) {
|
||||
try {
|
||||
convertedBuffer = await convertViaSandbox(buffer, extension);
|
||||
} catch (err) {
|
||||
if (err instanceof Error && err.message === 'CIRCUIT_OPEN') {
|
||||
throw new Error('Document conversion service temporarily unavailable. Please try again in a minute.');
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
} else {
|
||||
convertedBuffer = await convertLocally(buffer, extension);
|
||||
}
|
||||
|
||||
const config = useOcr ? { extractAttachments: true, ocr: true, ocrLanguage: 'eng' } : {};
|
||||
const ast = await officeParser.parseOffice(convertedBuffer, config);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue