| 29 | } |
| 30 | |
| 31 | async function retryWithBackoff<T>( |
| 32 | fn: () => Promise<T>, |
| 33 | maxRetries: number, |
| 34 | initialDelay: number, |
| 35 | context: string |
| 36 | ): Promise<T> { |
| 37 | let lastError: Error | undefined |
| 38 | for (let attempt = 0; attempt <= maxRetries; attempt++) { |
| 39 | try { |
| 40 | return await fn() |
| 41 | } catch (err) { |
| 42 | lastError = err instanceof Error ? err : new Error(String(err)) |
| 43 | if (attempt < maxRetries) { |
| 44 | const delay = initialDelay * Math.pow(2, attempt) |
| 45 | console.warn( |
| 46 | `[PersistenceQueue] ${context} failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms:`, |
| 47 | lastError.message |
| 48 | ) |
| 49 | await new Promise((resolve) => setTimeout(resolve, delay)) |
| 50 | } |
| 51 | } |
| 52 | } |
| 53 | throw lastError |
| 54 | } |
| 55 | |
| 56 | export interface PersistenceQueue<TKey extends string, TData> { |
| 57 | enqueue: (key: TKey, data: TData) => void |