diff --git a/.changeset/plenty-laws-end.md b/.changeset/plenty-laws-end.md new file mode 100644 index 0000000000..e026ca3fca --- /dev/null +++ b/.changeset/plenty-laws-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Fixed an issue where `getClient()` for a `pluginId` would return different clients and not share them diff --git a/packages/backend-common/src/database/DatabaseManager.test.ts b/packages/backend-common/src/database/DatabaseManager.test.ts index 9e8af44fc0..95511d5aba 100644 --- a/packages/backend-common/src/database/DatabaseManager.test.ts +++ b/packages/backend-common/src/database/DatabaseManager.test.ts @@ -113,7 +113,11 @@ describe('DatabaseManager', () => { }, }, }; - const manager = DatabaseManager.fromConfig(new ConfigReader(config)); + let manager: DatabaseManager; + + beforeEach(() => { + manager = DatabaseManager.fromConfig(new ConfigReader(config)); + }); it('connects to a plugin database using default config', async () => { const pluginId = 'pluginwithoutconfig'; @@ -340,6 +344,16 @@ describe('DatabaseManager', () => { ); }); + it('returns the same client for the same pluginId', async () => { + const [client1, client2] = await Promise.all([ + manager.forPlugin('plugin1').getClient(), + manager.forPlugin('plugin1').getClient(), + ]); + expect(mocked(createDatabaseClient)).toHaveBeenCalledTimes(1); + + expect(client1).toBe(client2); + }); + it('uses plugin connection as base if default client is different from plugin client', async () => { const pluginId = 'differentclient'; await manager.forPlugin(pluginId).getClient(); diff --git a/packages/backend-common/src/database/DatabaseManager.ts b/packages/backend-common/src/database/DatabaseManager.ts index 363bcac30b..a58799a724 100644 --- a/packages/backend-common/src/database/DatabaseManager.ts +++ b/packages/backend-common/src/database/DatabaseManager.ts @@ -84,6 +84,7 @@ export class DatabaseManager { private readonly config: Config, private readonly prefix: string = 'backstage_plugin_', private readonly options?: DatabaseManagerOptions, + private readonly databaseCache: Map> = new Map(), ) {} /** @@ -307,45 +308,61 @@ export class DatabaseManager { * plugin */ private async getDatabase(pluginId: string): Promise { - const pluginConfig = new ConfigReader( - this.getConfigForPlugin(pluginId) as JsonObject, - ); + if (this.databaseCache.has(pluginId)) { + return this.databaseCache.get(pluginId)!; + } - const databaseName = this.getDatabaseName(pluginId); - if (databaseName && this.getEnsureExistsConfig(pluginId)) { + const clientPromise = new Promise(async (resolve, reject) => { try { - await ensureDatabaseExists(pluginConfig, databaseName); - } catch (error) { - throw new Error( - `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + const pluginConfig = new ConfigReader( + this.getConfigForPlugin(pluginId) as JsonObject, ); - } - } - let schemaOverrides; - if (this.getPluginDivisionModeConfig() === 'schema') { - schemaOverrides = this.getSchemaOverrides(pluginId); - if (this.getEnsureExistsConfig(pluginId)) { - try { - await ensureSchemaExists(pluginConfig, pluginId); - } catch (error) { - throw new Error( - `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, - ); + const databaseName = this.getDatabaseName(pluginId); + if (databaseName && this.getEnsureExistsConfig(pluginId)) { + try { + await ensureDatabaseExists(pluginConfig, databaseName); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that '${databaseName}' exists, ${error}`, + ); + } } + + let schemaOverrides; + if (this.getPluginDivisionModeConfig() === 'schema') { + schemaOverrides = this.getSchemaOverrides(pluginId); + if (this.getEnsureExistsConfig(pluginId)) { + try { + await ensureSchemaExists(pluginConfig, pluginId); + } catch (error) { + throw new Error( + `Failed to connect to the database to make sure that schema for plugin '${pluginId}' exists, ${error}`, + ); + } + } + } + + const databaseClientOverrides = mergeDatabaseConfig( + {}, + this.getDatabaseOverrides(pluginId), + schemaOverrides, + ); + + const client = createDatabaseClient( + pluginConfig, + databaseClientOverrides, + ); + this.startKeepaliveLoop(pluginId, client); + resolve(client); + } catch (e) { + reject(e); } - } + }); - const databaseClientOverrides = mergeDatabaseConfig( - {}, - this.getDatabaseOverrides(pluginId), - schemaOverrides, - ); + this.databaseCache.set(pluginId, clientPromise); - const client = createDatabaseClient(pluginConfig, databaseClientOverrides); - this.startKeepaliveLoop(pluginId, client); - - return client; + return clientPromise; } private startKeepaliveLoop(pluginId: string, client: Knex): void {