Files
n8n-enterprise-unlocked/packages/cli/src/databases/entities/WorkflowEntity.ts
Iván Ovejero c857e42677 feat(core): Coordinate workflow activation in multiple main scenario in internal API (#7566)
Story: https://linear.app/n8n/issue/PAY-926

This PR coordinates workflow activation on instance startup and on
leadership change in multiple main scenario in the internal API. Part 3
on manual workflow activation and deactivation will be a separate PR.

### Part 1: Instance startup

In multi-main scenario, on starting an instance...
- [x] If the instance is the leader, it should add webhooks, triggers
and pollers.
- [x] If the instance is the follower, it should not add webhooks,
triggers or pollers.
- [x] Unit tests.

### Part 2: Leadership change 

In multi-main scenario, if the main instance leader dies…

- [x] The new main instance leader must activate all trigger- and
poller-based workflows, excluding webhook-based workflows.
- [x] The old main instance leader must deactivate all trigger- and
poller-based workflows, excluding webhook-based workflows.
- [x] Unit tests.

To test, start two instances and check behavior on startup and
leadership change:

```
EXECUTIONS_MODE=queue N8N_LEADER_SELECTION_ENABLED=true N8N_LICENSE_TENANT_ID=... N8N_LICENSE_ACTIVATION_KEY=... N8N_LOG_LEVEL=debug npm run start

EXECUTIONS_MODE=queue N8N_LEADER_SELECTION_ENABLED=true N8N_LICENSE_TENANT_ID=... N8N_LICENSE_ACTIVATION_KEY=... N8N_LOG_LEVEL=debug N8N_PORT=5679 npm run start
```
2023-11-07 13:48:48 +01:00

109 lines
2.7 KiB
TypeScript

import { Length } from 'class-validator';
import { IConnections, IDataObject, IWorkflowSettings, WorkflowFEMeta } from 'n8n-workflow';
import type { IBinaryKeyData, INode, IPairedItemData } from 'n8n-workflow';
import { Column, Entity, Index, JoinColumn, JoinTable, ManyToMany, OneToMany } from 'typeorm';
import config from '@/config';
import type { TagEntity } from './TagEntity';
import type { SharedWorkflow } from './SharedWorkflow';
import type { WorkflowStatistics } from './WorkflowStatistics';
import type { WorkflowTagMapping } from './WorkflowTagMapping';
import { objectRetriever, sqlite } from '../utils/transformers';
import { WithTimestampsAndStringId, jsonColumnType } from './AbstractEntity';
import type { IWorkflowDb } from '@/Interfaces';
@Entity()
export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkflowDb {
// TODO: Add XSS check
@Index({ unique: true })
@Length(1, 128, {
message: 'Workflow name must be $constraint1 to $constraint2 characters long.',
})
@Column({ length: 128 })
name: string;
@Column()
active: boolean;
@Column(jsonColumnType)
nodes: INode[];
@Column(jsonColumnType)
connections: IConnections;
@Column({
type: jsonColumnType,
nullable: true,
})
settings?: IWorkflowSettings;
@Column({
type: jsonColumnType,
nullable: true,
transformer: objectRetriever,
})
staticData?: IDataObject;
@Column({
type: jsonColumnType,
nullable: true,
transformer: objectRetriever,
})
meta?: WorkflowFEMeta;
@ManyToMany('TagEntity', 'workflows')
@JoinTable({
name: 'workflows_tags', // table name for the junction table of this relation
joinColumn: {
name: 'workflowId',
referencedColumnName: 'id',
},
inverseJoinColumn: {
name: 'tagId',
referencedColumnName: 'id',
},
})
tags?: TagEntity[];
@OneToMany('WorkflowTagMapping', 'workflows')
tagMappings: WorkflowTagMapping[];
@OneToMany('SharedWorkflow', 'workflow')
shared: SharedWorkflow[];
@OneToMany('WorkflowStatistics', 'workflow')
@JoinColumn({ referencedColumnName: 'workflow' })
statistics: WorkflowStatistics[];
@Column({
type: config.getEnv('database.type') === 'sqlite' ? 'text' : 'json',
nullable: true,
transformer: sqlite.jsonColumn,
})
pinData: ISimplifiedPinData;
@Column({ length: 36 })
versionId: string;
@Column({ default: 0 })
triggerCount: number;
display() {
return `"${this.name}" (ID: ${this.id})`;
}
}
/**
* Simplified to prevent excessively deep type instantiation error from
* `INodeExecutionData` in `IPinData` in a TypeORM entity field.
*/
export interface ISimplifiedPinData {
[nodeName: string]: Array<{
json: IDataObject;
binary?: IBinaryKeyData;
pairedItem?: IPairedItemData | IPairedItemData[] | number;
}>;
}