( prismaClient: BarePrismaClient, sessionId: string, )
| 97 | // Method not used for now, |
| 98 | // Need a way to check LSN on the actual replica that will be used for the read. |
| 99 | async function waitForReplicaCatchup( |
| 100 | prismaClient: BarePrismaClient, |
| 101 | sessionId: string, |
| 102 | ): Promise<boolean> { |
| 103 | const expectedLsn = await getCachedWalLsn(sessionId); |
| 104 | |
| 105 | if (!expectedLsn) { |
| 106 | return true; |
| 107 | } |
| 108 | |
| 109 | for (let attempt = 0; attempt < MAX_RETRY_ATTEMPTS; attempt++) { |
| 110 | const currentLsn = await getCurrentWalLsn(prismaClient); |
| 111 | if (!currentLsn) { |
| 112 | return true; |
| 113 | } |
| 114 | |
| 115 | // Check if replica has caught up (current >= expected) |
| 116 | if (compareWalLsn(currentLsn, expectedLsn) >= 0) { |
| 117 | logger.debug( |
| 118 | { |
| 119 | attempt: attempt + 1, |
| 120 | currentLsn, |
| 121 | expectedLsn, |
| 122 | sessionId, |
| 123 | }, |
| 124 | 'Replica caught up', |
| 125 | ); |
| 126 | return true; |
| 127 | } |
| 128 | |
| 129 | // Exponential backoff |
| 130 | if (attempt < MAX_RETRY_ATTEMPTS - 1) { |
| 131 | const delayMs = INITIAL_RETRY_DELAY_MS * 2 ** attempt; |
| 132 | logger.debug( |
| 133 | { |
| 134 | attempt: attempt + 1, |
| 135 | delayMs, |
| 136 | currentLsn, |
| 137 | expectedLsn, |
| 138 | sessionId, |
| 139 | }, |
| 140 | 'Waiting for replica to catch up', |
| 141 | ); |
| 142 | await sleep(delayMs); |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | logger.warn( |
| 147 | { sessionId, expectedLsn }, |
| 148 | 'Replica did not catch up after max retries, falling back to primary', |
| 149 | ); |
| 150 | return false; |
| 151 | } |
| 152 | |
| 153 | /** |
| 154 | * Prisma extension for session-based read-after-write consistency. |
nothing calls this directly
no test coverage detected