refactor(backend-defaults): clean up database creation retry loop

Refactor the postgres database creation retry loop to avoid an
unnecessary sleep after the final failed attempt, and improve
readability by using named variables.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@spotify.com>
This commit is contained in:
Fredrik Adelöw
2026-04-09 15:12:07 +02:00
parent c705d44e4b
commit 547258f67b
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();
}
}
}),
);
}
/**