AUDIT / src /lib /tools /requestQueue.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 4)
641b62c verified
Raw
History Blame Contribute Delete
3.82 kB
/**
* tools/requestQueue.ts — Request queue for mobile
*
* Gestisce:
* - Concorrenza limitata
* - Timeout e retry
* - Priority queue
* - Abort support
*/
export interface QueuedRequest {
id: string;
priority: number; // 0-10, higher = more important
fn: () => Promise<unknown>;
timeout: number;
retries: number;
resolve: (value: unknown) => void;
reject: (error: Error) => void;
abortSignal?: AbortSignal;
}
export class RequestQueue {
private queue: QueuedRequest[] = [];
private running = 0;
private maxConcurrent = 2; // iPhone-friendly
private requestId = 0;
constructor(maxConcurrent: number = 2) {
this.maxConcurrent = maxConcurrent;
}
/**
* Add request to queue
*/
add<T>(
fn: () => Promise<T>,
options: {
priority?: number;
timeout?: number;
retries?: number;
abortSignal?: AbortSignal;
} = {},
): Promise<T> {
return new Promise((resolve, reject) => {
const request: QueuedRequest = {
id: `req_${++this.requestId}`,
priority: options.priority ?? 5,
fn,
timeout: options.timeout ?? 30000,
retries: options.retries ?? 1,
resolve: resolve as (value: unknown) => void,
reject,
abortSignal: options.abortSignal,
};
this.queue.push(request);
this.queue.sort((a, b) => b.priority - a.priority);
this.process();
});
}
/**
* Process queue
*/
private process(): void {
while (
this.running < this.maxConcurrent &&
this.queue.length > 0
) {
const request = this.queue.shift();
if (!request) break;
this.running++;
this.executeRequest(request);
}
}
/**
* Execute single request with retry
*/
private async executeRequest(
request: QueuedRequest,
): Promise<void> {
let lastError: Error | undefined;
for (let attempt = 0; attempt <= request.retries; attempt++) {
try {
// Check abort signal
if (request.abortSignal?.aborted) {
throw new Error("Richiesta interrotta");
}
// Execute with timeout
const result = await Promise.race([
request.fn(),
this.timeout(request.timeout),
]);
request.resolve(result);
this.running--;
this.process();
return;
} catch (error) {
lastError = error as Error;
// Don't retry on abort
if (
lastError.message === "Richiesta interrotta"
) {
break;
}
// Wait before retry
if (attempt < request.retries) {
await new Promise((resolve) =>
setTimeout(resolve, 1000 * (attempt + 1)),
);
}
}
}
request.reject(
lastError || new Error("Errore sconosciuto"),
);
this.running--;
this.process();
}
/**
* Timeout promise
*/
private timeout(ms: number): Promise<never> {
return new Promise((_, reject) =>
setTimeout(
() => reject(new Error("Timeout della richiesta")),
ms,
),
);
}
/**
* Get queue size
*/
getSize(): number {
return this.queue.length;
}
/**
* Clear queue
*/
clear(): void {
for (const request of this.queue) {
request.reject(new Error("Coda svuotata"));
}
this.queue = [];
}
/**
* Set max concurrent
*/
setMaxConcurrent(max: number): void {
this.maxConcurrent = max;
this.process();
}
}
// Global queue instance
let globalQueue: RequestQueue | null = null;
export function getGlobalQueue(): RequestQueue {
if (!globalQueue) {
globalQueue = new RequestQueue(2);
}
return globalQueue;
}
export function setGlobalQueueConcurrency(
max: number,
): void {
getGlobalQueue().setMaxConcurrent(max);
}