| import type { Database, DatabaseWithPrimary } from "@midday/db/client"; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| export async function withRetryOnPrimary<T>( |
| db: Database, |
| fn: (db: Database) => Promise<T>, |
| options?: { |
| maxRetries?: number; |
| baseDelay?: number; |
| retryOnNull?: boolean; |
| }, |
| ): Promise<T> { |
| const { |
| maxRetries = 1, |
| baseDelay = 100, |
| retryOnNull = false, |
| } = options || {}; |
| const dbWithPrimary = db as DatabaseWithPrimary; |
| let lastError: unknown; |
|
|
| |
| try { |
| const result = await fn(db); |
|
|
| |
| if (retryOnNull && (result === null || result === undefined)) { |
| |
| if (!dbWithPrimary.usePrimaryOnly) { |
| return result; |
| } |
|
|
| |
| for (let attempt = 0; attempt <= maxRetries; attempt++) { |
| try { |
| const primaryDb = dbWithPrimary.usePrimaryOnly(); |
| const primaryResult = await fn(primaryDb); |
| |
| return primaryResult; |
| } catch (error) { |
| lastError = error; |
|
|
| |
| if (attempt === maxRetries) { |
| break; |
| } |
|
|
| |
| const delay = baseDelay * 2 ** attempt + Math.random() * 50; |
| await new Promise((resolve) => setTimeout(resolve, delay)); |
| } |
| } |
|
|
| |
| if (lastError) { |
| throw lastError; |
| } |
|
|
| |
| return result; |
| } |
|
|
| return result; |
| } catch (error) { |
| lastError = error; |
|
|
| |
| const errorMessage = error instanceof Error ? error.message : String(error); |
| const isRetryableError = |
| errorMessage.includes("timeout") || |
| errorMessage.includes("connection") || |
| errorMessage.includes("Failed query") || |
| errorMessage.includes("canceling statement") || |
| errorMessage.includes("cancelled"); |
|
|
| |
| if (!isRetryableError) { |
| throw error; |
| } |
|
|
| |
| if (!dbWithPrimary.usePrimaryOnly) { |
| throw error; |
| } |
| } |
|
|
| |
| for (let attempt = 0; attempt <= maxRetries; attempt++) { |
| try { |
| const primaryDb = dbWithPrimary.usePrimaryOnly(); |
| return await fn(primaryDb); |
| } catch (error) { |
| lastError = error; |
|
|
| |
| if (attempt === maxRetries) { |
| break; |
| } |
|
|
| |
| const delay = baseDelay * 2 ** attempt + Math.random() * 50; |
| await new Promise((resolve) => setTimeout(resolve, delay)); |
| } |
| } |
|
|
| throw lastError; |
| } |
|
|