Merge pull request #33822 from backstage/freben/refactor-postgres-retry-loop

refactor(backend-defaults): clean up database creation retry loop
This commit is contained in:
Fredrik Adelöw
2026-04-09 17:11:30 +02:00
committed by GitHub
2 changed files with 39 additions and 30 deletions
@@ -0,0 +1,5 @@
---
'@backstage/backend-defaults': patch
---
Refactored the database creation retry loop to avoid an unnecessary delay after the final failed attempt.
@@ -323,18 +323,20 @@ export async function ensurePgDatabaseExists(
dbConfig: Config,
...databases: Array<string>
) {
const admin = await createPgDatabaseClient(dbConfig, {
connection: {
database: 'postgres',
},
pool: {
min: 0,
acquireTimeoutMillis: 10000,
},
});
// Implements a single existence check attempt
const ensureDatabase = async (database: string) => {
const admin = await createPgDatabaseClient(dbConfig, {
connection: {
database: 'postgres',
},
pool: {
min: 0,
max: 1,
acquireTimeoutMillis: 10000,
},
});
try {
const ensureDatabase = async (database: string) => {
try {
const result = await admin
.from('pg_database')
.where('datname', database)
@@ -345,28 +347,30 @@ export async function ensurePgDatabaseExists(
}
await admin.raw(`CREATE DATABASE ??`, [database]);
};
} finally {
await admin.destroy();
}
};
await Promise.all(
databases.map(async database => {
// For initial setup we use a smaller timeout but several retries. Given that this
// is a separate connection pool we should never really run into issues with connection
// acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.
let lastErr: Error | undefined = undefined;
for (let i = 0; i < 3; i++) {
try {
return await ddlLimiter(() => ensureDatabase(database));
} catch (err) {
lastErr = err;
await Promise.all(
databases.map(async database => {
// For initial setup we use a smaller timeout but several retries. Given that this
// is a separate connection pool we should never really run into issues with connection
// acquisition timeouts, but we do anyway. This might be a bug in knex or some other dependency.
const maxAttempts = 3;
for (let attempt = 1; ; attempt++) {
try {
return await ddlLimiter(() => ensureDatabase(database));
} catch (err) {
if (attempt >= maxAttempts) {
throw err;
} else {
await new Promise(resolve => setTimeout(resolve, 100));
}
await new Promise(resolve => setTimeout(resolve, 100));
}
throw lastErr;
}),
);
} finally {
await admin.destroy();
}
}
}),
);
}
/**