mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
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 ```
109 lines
2.7 KiB
TypeScript
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;
|
|
}>;
|
|
}
|