mirror of
https://github.com/n8n-io/n8n.git
synced 2026-06-19 07:36:52 +00:00
perf(core): Add partial index on execution_entity to fix executions list CPU spike (#32116)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,7 @@
|
||||
| idx_execution_entity_wait_till_status_deleted_at | CREATE INDEX idx_execution_entity_wait_till_status_deleted_at ON public.execution_entity USING btree ("waitTill", status, "deletedAt") WHERE (("waitTill" IS NOT NULL) AND ("deletedAt" IS NULL)) |
|
||||
| idx_execution_entity_stopped_at_status_deleted_at | CREATE INDEX idx_execution_entity_stopped_at_status_deleted_at ON public.execution_entity USING btree ("stoppedAt", status, "deletedAt") WHERE (("stoppedAt" IS NOT NULL) AND ("deletedAt" IS NULL)) |
|
||||
| IDX_execution_entity_deduplicationKey | CREATE UNIQUE INDEX "IDX_execution_entity_deduplicationKey" ON public.execution_entity USING btree ("deduplicationKey") WHERE ("deduplicationKey" IS NOT NULL) |
|
||||
| IDX_execution_entity_workflowId_status_id | CREATE INDEX "IDX_execution_entity_workflowId_status_id" ON public.execution_entity USING btree ("workflowId", status, id) WHERE ("deletedAt" IS NULL) |
|
||||
|
||||
## Relations
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ export type ExecutionDataStorageLocation = 'db' | 'fs' | 's3';
|
||||
@Index(['finished', 'id'])
|
||||
@Index(['workflowId', 'finished', 'id'])
|
||||
@Index(['workflowId', 'waitTill', 'id'])
|
||||
// Partial index (Postgres only) — supports paginated list queries filtered by
|
||||
// workflowId + status without full sequential scans. See migration 1784000000029.
|
||||
@Index(['workflowId', 'status', 'id'], { where: '"deletedAt" IS NULL' })
|
||||
export class ExecutionEntity {
|
||||
@Generated()
|
||||
@PrimaryColumn({ transformer: idStringifier })
|
||||
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import type { MigrationContext, ReversibleMigration } from '../migration-types';
|
||||
|
||||
/**
|
||||
* Adds a partial composite index on execution_entity(workflowId, status, id DESC)
|
||||
* filtered to non-deleted rows (deletedAt IS NULL).
|
||||
*
|
||||
* Without this index Postgres performs a parallel sequential scan of the full
|
||||
* execution_entity table for every paginated executions-list request, because
|
||||
* the CASE-based ORDER BY and the access-control IN/EXISTS subquery prevent the
|
||||
* planner from using any narrower path.
|
||||
*
|
||||
* With this index the planner can do per-workflowId range scans (one index seek
|
||||
* per accessible workflow) and merge them into the top-N result without touching
|
||||
* irrelevant rows, dropping the query from a full-table sequential scan to a
|
||||
* bounded index walk.
|
||||
*
|
||||
* Partial (WHERE deletedAt IS NULL) keeps the index small: pruned executions are
|
||||
* excluded and never appear in list queries.
|
||||
*
|
||||
* Postgres-only migration: partial functional indexes are a Postgres feature;
|
||||
* SQLite is not affected by this performance class of issue at n8n scale.
|
||||
*/
|
||||
export class AddExecutionEntityWorkflowStatusIndex1784000000031 implements ReversibleMigration {
|
||||
async up({ schemaBuilder: { createIndex }, escape }: MigrationContext) {
|
||||
const col = (c: string) => escape.columnName(c);
|
||||
|
||||
await createIndex(
|
||||
'execution_entity',
|
||||
['workflowId', 'status', 'id'],
|
||||
false,
|
||||
undefined,
|
||||
`${col('deletedAt')} IS NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
async down({ schemaBuilder: { dropIndex } }: MigrationContext) {
|
||||
await dropIndex('execution_entity', ['workflowId', 'status', 'id'], { skipIfMissing: true });
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ import { ChangeWorkflowStatisticsFKToNoAction1767018516000 } from './17670185160
|
||||
import { ExpandVariablesValueColumnToText1777420800000 } from './1777420800000-ExpandVariablesValueColumnToText';
|
||||
import { LimitWorkflowVersionTriggerToContent1784000000003 } from './1784000000003-LimitWorkflowVersionTriggerToContent';
|
||||
import { AddProjectIdToInstanceAiThread1784000000028 } from './1784000000028-AddProjectIdToInstanceAiThread';
|
||||
import { AddExecutionEntityWorkflowStatusIndex1784000000031 } from './1784000000031-AddExecutionEntityWorkflowStatusIndex';
|
||||
import { CreateLdapEntities1674509946020 } from '../common/1674509946020-CreateLdapEntities';
|
||||
import { PurgeInvalidWorkflowConnections1675940580449 } from '../common/1675940580449-PurgeInvalidWorkflowConnections';
|
||||
import { RemoveResetPasswordColumns1690000000030 } from '../common/1690000000030-RemoveResetPasswordColumns';
|
||||
@@ -415,4 +416,5 @@ export const postgresMigrations: Migration[] = [
|
||||
AddProjectIdToInstanceAiThread1784000000028,
|
||||
AddJsonSizeBytesAndWorkflowVersionIdToExecutionEntity1784000000029,
|
||||
CreateAgentChatSubscriptions1784000000030,
|
||||
AddExecutionEntityWorkflowStatusIndex1784000000031,
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user