feat(core): Add cluster check interface and decorator (no-changelog)#28724
Merged
Conversation
d8802d1 to
f60cff1
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Contributor
Performance ComparisonComparing current → latest master → 14-day baseline Idle baseline with Instance AI module loaded
Memory consumption baseline with starter plan resources
docker-stats
How to read this table
|
Contributor
There was a problem hiding this comment.
2 issues found across 8 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="packages/@n8n/decorators/src/cluster-check/cluster-check-metadata.ts">
<violation number="1" location="packages/@n8n/decorators/src/cluster-check/cluster-check-metadata.ts:69">
P2: Registration is not actually deduplicated by class; repeated registration of the same check class creates duplicate entries and can cause duplicate execution downstream.</violation>
<violation number="2" location="packages/@n8n/decorators/src/cluster-check/cluster-check-metadata.ts:88">
P2: `getEntries()` returns incorrect tuple shape: `Set.entries()` yields `[entry, entry]`, not `[index, entry]` as documented.</violation>
</file>
Architecture diagram
sequenceDiagram
participant Loader as Module Loader
participant Decorator as @ClusterCheck()
participant Registry as ClusterCheckMetadata
participant DI as DI Container (Container)
participant Leader as Leader Service (Future Consumer)
Note over Loader,Registry: PHASE 1: Auto-Discovery (Eager Module Load)
Loader->>Decorator: Execute class decorator
Decorator->>Registry: NEW: register({ class: target })
Registry->>Registry: Add to internal Set<ClusterCheckEntry>
Decorator->>DI: NEW: Apply @Service() to target
Note right of DI: Makes check class resolvable and injectable
Note over Registry,Leader: PHASE 2: Orchestration Initialization
Leader->>Registry: NEW: getClasses()
Registry-->>Leader: Readonly array of check constructors
loop for each Class
Leader->>DI: Container.get(CheckClass)
DI-->>Leader: Instance (with injected dependencies)
end
Note over Leader,Decorator: PHASE 3: Execution (Reconciliation Tick)
Leader->>Leader: Compute ClusterStateDiff (added/removed/changed)
loop for each Registered Check
Leader->>Decorator: NEW: run(ClusterCheckContext)
Note right of Decorator: Context includes currentState, previousState, & diff
alt Check identifies issues
Decorator-->>Leader: NEW: ClusterCheckResult (warnings, auditEvents, push)
else Happy Path
Decorator-->>Leader: Return empty object {}
end
end
Note over Leader,Leader: Downstream: Map results to concrete CLI types & emit
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.
guillaumejacquart
approved these changes
Apr 21, 2026
Contributor
guillaumejacquart
left a comment
There was a problem hiding this comment.
very clear PR 🚀
Contributor
|
Got released with |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Introduces the
@ClusterCheck()decorator, theIClusterCheckcontract, and theClusterCheckMetadataregistry in@n8n/decorators. Lays the foundation for apluggable cluster health-check system using n8n's established auto-discovery
pattern (
@ContextEstablishmentHook,@AuthHandler,@Module, …). No checks areimplemented here — this is the interface ticket only.
Why
Part of the Cluster Instance Registry initiative, milestone Phase 3: Health
Check Registry & Leader Service. The Leader Service needs to run a set of
pluggable checks over every cluster state reconciliation tick and emit
warnings, audit events, and push notifications. Rather than require manual
registration of every check, checks self-register at module load time via a
class decorator, and the (future) Leader Service discovers them through the
ClusterCheckMetadataregistry.This is the smallest sensible slice: the contract and registry only.
Unblocks IAM-333 Built-in Health Checks
which will provide the first concrete checks (version mismatch, leader
missing, etc.) on top of this interface.
What's in scope
IClusterCheck— interface withcheckDescriptionproperty + asyncrun().ClusterCheckContext—{ currentState, previousState, diff }, all read-only.State maps are keyed by
instanceKey, matching the existingInstanceStorage.getLastKnownState()contract.ClusterStateDiff— pre-computed{ added, removed, changed }so every checkreuses the same diff rather than recomputing it.
ClusterCheckResult— three loosely-typed output channels:warnings[],auditEvents[],pushNotifications[].ClusterCheckMetadata—@Service()registry withregister(),getEntries(),getClasses().@ClusterCheck()— class decorator that registers the class and applies@Service()for DI.cluster-check/index.tsand@n8n/decorators/src/index.ts.dependency injection (8 tests).
Key implementation decisions
@n8n/decorators. Warnings, auditevents, and push notifications are plain structured shapes (
{ code, message, severity?, context? },{ eventName, payload },{ type, data }) ratherthan concrete
EventMessageAuditclasses orPushMessagediscriminatedunions. This keeps the decorator package free of
packages/cliaudit enumsand
@n8n/api-types/pushchurn. A thin translator inpackages/cli(futureticket, under the Leader Service) maps each shape to the concrete type at
emit time. Mirrors the precedent set by
auth-handler,context-establishment, andpubsub.InstanceRegistrationimported from@n8n/api-types. Verified nocircular dep (
@n8n/api-typesdoes not import from@n8n/decorators).Avoids a local mirror type that would drift from the canonical cluster
state entity. Added
@n8n/api-types: workspace:*to@n8n/decoratorsdependencies and the matching TypeScript project reference.
init()lifecycle hook. The ACs don't require it and checks arestateless over state snapshots. Can be added non-breakingly later if a
concrete check needs it.
@ContextEstablishmentHook. Same two-file split(interface + metadata/decorator), same
Set<Entry>storage, same decoratorbody (
Container.get(Metadata).register({ class: target }); return Service()(target);), sameregister/getEntries/getClassestrio.Related Links
closes https://linear.app/n8n/issue/IAM-332/cluster-check-interface-and-decorator
Review / Merge checklist
Backport to Beta,Backport to Stable, orBackport to v1(if the PR is an urgent fix that needs to be backported)