Merge pull request #13971 from backstage/blam/get-client

Cache the `getDatabase` call in `DatabaseManager` based on `pluginId`
This commit is contained in:
Ben Lambert
2022-10-03 15:58:09 +02:00
committed by GitHub
3 changed files with 68 additions and 32 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-common': patch
---
Fixed an issue where `getClient()` for a `pluginId` would return different clients and not share them
@@ -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();
@@ -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<string, Promise<Knex>> = new Map(),
) {}
/**
@@ -307,45 +308,61 @@ export class DatabaseManager {
* plugin
*/
private async getDatabase(pluginId: string): Promise<Knex> {
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<Knex>(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 {