|
| 1 | +import { statSync } from 'fs'; |
| 2 | +import path from 'path'; |
| 3 | +import { UserSettings } from 'n8n-core'; |
1 | 4 | import type { MigrationContext, IrreversibleMigration } from '@db/types'; |
| 5 | +import config from '@/config'; |
| 6 | +import { copyTable } from '@/databases/utils/migrationHelpers'; |
2 | 7 |
|
3 | 8 | export class MigrateIntegerKeysToString1690000000002 implements IrreversibleMigration { |
4 | 9 | transaction = false as const; |
5 | 10 |
|
6 | | - async up({ queryRunner, tablePrefix }: MigrationContext) { |
| 11 | + async up(context: MigrationContext) { |
| 12 | + // eslint-disable-next-line @typescript-eslint/no-use-before-define |
| 13 | + await pruneExecutionsData(context); |
| 14 | + |
| 15 | + const { queryRunner, tablePrefix } = context; |
| 16 | + |
7 | 17 | await queryRunner.query(` |
8 | 18 | CREATE TABLE "${tablePrefix}TMP_workflow_entity" ("id" varchar(36) PRIMARY KEY NOT NULL, "name" varchar(128) NOT NULL, "active" boolean NOT NULL, "nodes" text, "connections" text NOT NULL, "createdAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "updatedAt" datetime(3) NOT NULL DEFAULT (STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW')), "settings" text, "staticData" text, "pinData" text, "versionId" varchar(36), "triggerCount" integer NOT NULL DEFAULT 0);`); |
9 | 19 | await queryRunner.query( |
@@ -108,9 +118,7 @@ export class MigrateIntegerKeysToString1690000000002 implements IrreversibleMigr |
108 | 118 | "data" text NOT NULL, "status" varchar, |
109 | 119 | FOREIGN KEY("workflowId") REFERENCES "${tablePrefix}workflow_entity" ("id") ON DELETE CASCADE |
110 | 120 | );`); |
111 | | - await queryRunner.query( |
112 | | - `INSERT INTO "${tablePrefix}TMP_execution_entity" SELECT * FROM "${tablePrefix}execution_entity";`, |
113 | | - ); |
| 121 | + await copyTable({ tablePrefix, queryRunner }, 'execution_entity', 'TMP_execution_entity'); |
114 | 122 | await queryRunner.query(`DROP TABLE "${tablePrefix}execution_entity";`); |
115 | 123 | await queryRunner.query( |
116 | 124 | `ALTER TABLE "${tablePrefix}TMP_execution_entity" RENAME TO "${tablePrefix}execution_entity";`, |
@@ -178,3 +186,44 @@ export class MigrateIntegerKeysToString1690000000002 implements IrreversibleMigr |
178 | 186 | ); |
179 | 187 | } |
180 | 188 | } |
| 189 | + |
| 190 | +const DESIRED_DATABASE_FILE_SIZE = 1 * 1024 * 1024 * 1024; // 1 GB |
| 191 | +const migrationsPruningEnabled = process.env.MIGRATIONS_PRUNING_ENABLED === 'true'; |
| 192 | + |
| 193 | +function getSqliteDbFileSize(): number { |
| 194 | + const filename = path.resolve( |
| 195 | + UserSettings.getUserN8nFolderPath(), |
| 196 | + config.getEnv('database.sqlite.database'), |
| 197 | + ); |
| 198 | + const { size } = statSync(filename); |
| 199 | + return size; |
| 200 | +} |
| 201 | + |
| 202 | +const pruneExecutionsData = async ({ queryRunner, tablePrefix }: MigrationContext) => { |
| 203 | + if (migrationsPruningEnabled) { |
| 204 | + const dbFileSize = getSqliteDbFileSize(); |
| 205 | + if (dbFileSize < DESIRED_DATABASE_FILE_SIZE) { |
| 206 | + console.log(`DB Size not large enough to prune: ${dbFileSize}`); |
| 207 | + return; |
| 208 | + } |
| 209 | + |
| 210 | + console.time('pruningData'); |
| 211 | + const counting = (await queryRunner.query( |
| 212 | + `select count(id) as rows from "${tablePrefix}execution_entity";`, |
| 213 | + )) as Array<{ rows: number }>; |
| 214 | + |
| 215 | + const averageExecutionSize = dbFileSize / counting[0].rows; |
| 216 | + const numberOfExecutionsToKeep = Math.floor(DESIRED_DATABASE_FILE_SIZE / averageExecutionSize); |
| 217 | + |
| 218 | + const query = `SELECT id FROM "${tablePrefix}execution_entity" ORDER BY id DESC limit ${numberOfExecutionsToKeep}, 1`; |
| 219 | + const idToKeep = await queryRunner |
| 220 | + .query(query) |
| 221 | + .then((rows: Array<{ id: number }>) => rows[0].id); |
| 222 | + |
| 223 | + const removalQuery = `DELETE FROM "${tablePrefix}execution_entity" WHERE id < ${idToKeep} and status IN ('success')`; |
| 224 | + await queryRunner.query(removalQuery); |
| 225 | + console.timeEnd('pruningData'); |
| 226 | + } else { |
| 227 | + console.log('Pruning was requested, but was not enabled'); |
| 228 | + } |
| 229 | +}; |
0 commit comments