fix(core): Remove typeorm patches, but still enforce transactions on every migration (#6594)

* revert(core): Remove typeorm patches, but still enforce transactions on every migration

This reverts #6519

* always re-enable foreign keys, and explicitly rollback transaction
This commit is contained in:
कारतोफ्फेलस्क्रिप्ट™
2023-07-05 11:26:49 +02:00
committed by GitHub
parent 7495e31a5b
commit 9def7a729b
9 changed files with 69 additions and 66 deletions

View File

@@ -1,11 +1,10 @@
/* eslint-disable no-await-in-loop */
import { readFileSync, rmSync } from 'fs';
import { UserSettings } from 'n8n-core';
import type { QueryRunner } from 'typeorm/query-runner/QueryRunner';
import config from '@/config';
import { getLogger } from '@/Logger';
import { inTest } from '@/constants';
import type { Migration, MigrationContext } from '@db/types';
import type { BaseMigration, Migration, MigrationContext, MigrationFn } from '@db/types';
const logger = getLogger();
@@ -39,30 +38,47 @@ export function loadSurveyFromDisk(): string | null {
}
}
let logFinishTimeout: NodeJS.Timeout;
let runningMigrations = false;
export function logMigrationStart(migrationName: string, disableLogging = inTest): void {
if (disableLogging) return;
function logMigrationStart(migrationName: string): void {
if (inTest) return;
if (!logFinishTimeout) {
if (!runningMigrations) {
logger.warn('Migrations in progress, please do NOT stop the process.');
runningMigrations = true;
}
logger.debug(`Starting migration ${migrationName}`);
clearTimeout(logFinishTimeout);
}
export function logMigrationEnd(migrationName: string, disableLogging = inTest): void {
if (disableLogging) return;
function logMigrationEnd(migrationName: string): void {
if (inTest) return;
logger.debug(`Finished migration ${migrationName}`);
logFinishTimeout = setTimeout(() => {
logger.warn('Migrations finished.');
}, 100);
}
const runDisablingForeignKeys = async (
migration: BaseMigration,
context: MigrationContext,
fn: MigrationFn,
) => {
const { dbType, queryRunner } = context;
if (dbType !== 'sqlite') throw new Error('Disabling transactions only available in sqlite');
await queryRunner.query('PRAGMA foreign_keys=OFF');
await queryRunner.startTransaction();
try {
await fn.call(migration, context);
await queryRunner.commitTransaction();
} catch (e) {
try {
await queryRunner.rollbackTransaction();
} catch {}
throw e;
} finally {
await queryRunner.query('PRAGMA foreign_keys=ON');
}
};
export const wrapMigration = (migration: Migration) => {
const dbType = config.getEnv('database.type');
const dbName = config.getEnv(`database.${dbType === 'mariadb' ? 'mysqldb' : dbType}.database`);
@@ -78,13 +94,23 @@ export const wrapMigration = (migration: Migration) => {
const { up, down } = migration.prototype;
Object.assign(migration.prototype, {
async up(queryRunner: QueryRunner) {
async up(this: BaseMigration, queryRunner: QueryRunner) {
logMigrationStart(migrationName);
await up.call(this, { queryRunner, ...context });
if (!this.transaction) {
await runDisablingForeignKeys(this, { queryRunner, ...context }, up);
} else {
await up.call(this, { queryRunner, ...context });
}
logMigrationEnd(migrationName);
},
async down(queryRunner: QueryRunner) {
await down?.call(this, { queryRunner, ...context });
async down(this: BaseMigration, queryRunner: QueryRunner) {
if (down) {
if (!this.transaction) {
await runDisablingForeignKeys(this, { queryRunner, ...context }, up);
} else {
await down.call(this, { queryRunner, ...context });
}
}
},
});
};