add performance test

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
This commit is contained in:
Fredrik Adelöw
2024-11-19 20:58:16 +01:00
parent f159b258c9
commit 23be284e5b
5 changed files with 121 additions and 6 deletions
@@ -75,10 +75,10 @@ export class DefaultProviderDatabase implements ProviderDatabase {
}
async replaceUnprocessedEntities(
txOpaque: Transaction,
txOpaque: Knex | Transaction,
options: ReplaceUnprocessedEntitiesOptions,
): Promise<void> {
const tx = txOpaque as Knex.Transaction;
const tx = txOpaque as Knex | Knex.Transaction;
const { toAdd, toUpsert, toRemove } = await this.createDelta(tx, options);
if (toRemove.length) {
@@ -205,7 +205,7 @@ export class DefaultProviderDatabase implements ProviderDatabase {
}
private async createDelta(
tx: Knex.Transaction,
tx: Knex | Knex.Transaction,
options: ReplaceUnprocessedEntitiesOptions,
): Promise<{
toAdd: { deferred: DeferredEntity; hash: string }[];
@@ -24,7 +24,7 @@ import { DbRefreshStateRow } from '../../tables';
* @returns The conflicting key if there is one.
*/
export async function checkLocationKeyConflict(options: {
tx: Knex.Transaction;
tx: Knex | Knex.Transaction;
entityRef: string;
locationKey?: string;
}): Promise<string | undefined> {
@@ -28,7 +28,7 @@ import {
* true if successful and false if there was a conflict.
*/
export async function insertUnprocessedEntity(options: {
tx: Knex.Transaction;
tx: Knex | Knex.Transaction;
entity: Entity;
hash: string;
locationKey?: string;
@@ -25,7 +25,7 @@ import { DbRefreshStateRow } from '../../tables';
* Updating the entity will also cause it to be scheduled for immediate processing.
*/
export async function updateUnprocessedEntity(options: {
tx: Knex.Transaction;
tx: Knex | Knex.Transaction;
entity: Entity;
hash: string;
locationKey?: string;
@@ -0,0 +1,115 @@
/*
* Copyright 2024 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { TestDatabases, mockServices } from '@backstage/backend-test-utils';
import { Knex } from 'knex';
import { applyDatabaseMigrations } from '../../database/migrations';
import { describePerformanceTest, performanceTraceEnabled } from './lib/env';
import { DefaultProviderDatabase } from '../../database/DefaultProviderDatabase';
import { DeferredEntity } from '@backstage/plugin-catalog-node';
// #region Helpers
jest.setTimeout(600_000);
const databases = TestDatabases.create({
ids: [/* 'MYSQL_8', */ 'POSTGRES_16', /* 'POSTGRES_12',*/ 'SQLITE_3'],
disableDocker: false,
});
const traceLog: typeof console.log = performanceTraceEnabled
? console.log
: () => {};
// #endregion
// #region Tests
describePerformanceTest('providerDeltaMutations', () => {
let knex: Knex;
describe.each(databases.eachSupportedId())('%p', databaseId => {
beforeAll(async () => {
knex = await databases.init(databaseId);
await applyDatabaseMigrations(knex);
});
afterAll(async () => {
await knex.destroy();
});
it.each([200, 50_000])(
'inserts and then overwrites identical sets using delta mutations, batch size %p',
async batchSize => {
const sut = new DefaultProviderDatabase({
database: knex,
logger: mockServices.logger.mock(),
});
const sourceKey = 'source-key';
const entities: DeferredEntity[] = Array.from(
{ length: batchSize },
(_, i) => ({
entity: {
apiVersion: 'backstage.io/v1alpha1',
kind: 'Component',
metadata: {
name: `component-${i}`,
namespace: 'default',
},
spec: {
type: 'service',
lifecycle: 'production',
},
},
}),
);
let start = Date.now();
await sut.replaceUnprocessedEntities(knex, {
type: 'delta',
sourceKey,
added: entities,
removed: [],
});
let perSecond = Math.round(batchSize / ((Date.now() - start) / 1000));
traceLog(
`${databaseId} inserted ${perSecond} entities per second at a batch size of ${batchSize}`,
);
start = Date.now();
const rounds = 20;
for (let i = 0; i < rounds; i++) {
await sut.replaceUnprocessedEntities(knex, {
type: 'delta',
sourceKey,
added: entities,
removed: [],
});
}
perSecond = Math.round(
(batchSize * rounds) / ((Date.now() - start) / 1000),
);
traceLog(
`${databaseId} updated ${perSecond} entities per second at a batch size of ${batchSize}`,
);
expect(true).toBe(true);
},
);
});
});
// #endregion