Skip to content

Commit bd761e7

Browse files
committed
Handle database not responding to new transaction
1 parent c0087cc commit bd761e7

File tree

2 files changed

+40
-9
lines changed

2 files changed

+40
-9
lines changed

data-migration/src/config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ module.exports = {
2828
// Migration behavior
2929
SKIP_MISSING_REQUIRED: process.env.SKIP_MISSING_REQUIRED === 'true',
3030
USE_TRANSACTIONS: process.env.USE_TRANSACTIONS !== 'false',
31+
TRANSACTION_RETRY_ATTEMPTS: parseInt(process.env.TRANSACTION_RETRY_ATTEMPTS || '3', 10),
32+
TRANSACTION_RETRY_DELAY_MS: parseInt(process.env.TRANSACTION_RETRY_DELAY_MS || '15000', 10),
3133

3234
// Incremental migration settings
3335
MIGRATION_MODE: parseMigrationMode(process.env.MIGRATION_MODE),

data-migration/src/migrationManager.js

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ const configModule = require('./config');
33
const fs = require('fs');
44
const path = require('path');
55

6+
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
7+
68
const { parseMigrationMode } = configModule;
79
const baseConfig = { ...configModule };
810

@@ -236,6 +238,41 @@ class MigrationManager {
236238
const batchSize = this.config.BATCH_SIZE;
237239
let processed = 0;
238240
let skipped = 0;
241+
const rawRetryAttempts = Number(this.config.TRANSACTION_RETRY_ATTEMPTS);
242+
const retryAttempts = Number.isFinite(rawRetryAttempts) && rawRetryAttempts > 0
243+
? Math.floor(rawRetryAttempts)
244+
: 0;
245+
const rawRetryDelayMs = Number(this.config.TRANSACTION_RETRY_DELAY_MS);
246+
const retryDelayMs = Number.isFinite(rawRetryDelayMs) && rawRetryDelayMs > 0
247+
? Math.floor(rawRetryDelayMs)
248+
: 0;
249+
const shouldRetryTransactionStart = error =>
250+
this.config.USE_TRANSACTIONS &&
251+
error instanceof Prisma.PrismaClientKnownRequestError &&
252+
error.code === 'P2028';
253+
const executeBatch = async (batch, attempt = 0) => {
254+
const uniqueTracker = new Map();
255+
256+
try {
257+
if (this.config.USE_TRANSACTIONS) {
258+
return await this.prisma.$transaction(tx => processFn(batch, tx, uniqueTracker));
259+
}
260+
return await processFn(batch, this.prisma, uniqueTracker);
261+
} catch (error) {
262+
if (attempt < retryAttempts && shouldRetryTransactionStart(error)) {
263+
const nextAttempt = attempt + 1;
264+
const waitMs = retryDelayMs;
265+
const waitSeconds = Math.round(waitMs / 1000) || 0;
266+
this.logger.warn(`Unable to start transaction for batch (P2028). Retrying attempt ${nextAttempt} of ${retryAttempts} in ${waitSeconds}s.`);
267+
if (waitMs > 0) {
268+
await sleep(waitMs);
269+
}
270+
return executeBatch(batch, nextAttempt);
271+
}
272+
273+
throw error;
274+
}
275+
};
239276

240277
// Create batches
241278
const batches = [];
@@ -249,15 +286,7 @@ class MigrationManager {
249286

250287
for (let i = 0; i < batches.length; i += concurrencyLimit) {
251288
const batchGroup = batches.slice(i, i + concurrencyLimit);
252-
const batchPromises = batchGroup.map(batch => {
253-
const uniqueTracker = new Map();
254-
255-
if (this.config.USE_TRANSACTIONS) {
256-
return this.prisma.$transaction(tx => processFn(batch, tx, uniqueTracker));
257-
} else {
258-
return processFn(batch, this.prisma, uniqueTracker);
259-
}
260-
});
289+
const batchPromises = batchGroup.map(batch => executeBatch(batch));
261290

262291
const batchResults = await Promise.all(batchPromises);
263292
results.push(...batchResults);

0 commit comments

Comments
 (0)