diff --git a/.changeset/fresh-penguins-cry.md b/.changeset/fresh-penguins-cry.md new file mode 100644 index 0000000000..457025a518 --- /dev/null +++ b/.changeset/fresh-penguins-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Limit the database creation concurrency to one, defensively diff --git a/packages/backend-common/package.json b/packages/backend-common/package.json index d0b9a99e2b..546d73eaa0 100644 --- a/packages/backend-common/package.json +++ b/packages/backend-common/package.json @@ -96,6 +96,7 @@ "minimatch": "^5.0.0", "mysql2": "^2.2.5", "node-fetch": "^2.6.7", + "p-limit": "^3.1.0", "pg": "^8.11.3", "raw-body": "^2.4.1", "tar": "^6.1.12", diff --git a/packages/backend-common/src/database/connection.ts b/packages/backend-common/src/database/connection.ts index ff10d349dd..e369323e04 100644 --- a/packages/backend-common/src/database/connection.ts +++ b/packages/backend-common/src/database/connection.ts @@ -18,6 +18,7 @@ import { Config } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { InputError } from '@backstage/errors'; import knexFactory, { Knex } from 'knex'; +import limiterFactory from 'p-limit'; import { mergeDatabaseConfig } from './config'; import { DatabaseConnector } from './types'; @@ -35,6 +36,12 @@ type DatabaseClient = | 'mysql2' | string; +// This limits the number of concurrent CREATE DATABASE and CREATE SCHEMA +// commands, globally, to just one. This is overly defensive, and was added as +// an attempt to counteract the pool issues on recent node versions. See +// https://github.com/backstage/backstage/pull/19988 +const ddlLimiter = limiterFactory(1); + /** * Mapping of client type to supported database connectors * @@ -83,9 +90,8 @@ export async function ensureDatabaseExists( ): Promise { const client: DatabaseClient = dbConfig.getString('client'); - return ConnectorMapping[client]?.ensureDatabaseExists?.( - dbConfig, - ...databases, + return await ddlLimiter(() => + ConnectorMapping[client]?.ensureDatabaseExists?.(dbConfig, ...databases), ); } @@ -100,9 +106,8 @@ export async function ensureSchemaExists( ): Promise { const client: DatabaseClient = dbConfig.getString('client'); - return await ConnectorMapping[client]?.ensureSchemaExists?.( - dbConfig, - ...schemas, + return await ddlLimiter(() => + ConnectorMapping[client]?.ensureSchemaExists?.(dbConfig, ...schemas), ); } diff --git a/yarn.lock b/yarn.lock index fc0f278da3..52dc2ceb21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3560,6 +3560,7 @@ __metadata: msw: ^1.0.0 mysql2: ^2.2.5 node-fetch: ^2.6.7 + p-limit: ^3.1.0 pg: ^8.11.3 raw-body: ^2.4.1 supertest: ^6.1.3