Skip to content

feat(core): Add cluster check interface and decorator (no-changelog)#28724

Merged
afitzek merged 3 commits into
masterfrom
afitzek/iam-332-cluster-check
Apr 22, 2026
Merged

feat(core): Add cluster check interface and decorator (no-changelog)#28724
afitzek merged 3 commits into
masterfrom
afitzek/iam-332-cluster-check

Conversation

@afitzek
Copy link
Copy Markdown
Contributor

@afitzek afitzek commented Apr 20, 2026

Summary

Introduces the @ClusterCheck() decorator, the IClusterCheck contract, and the
ClusterCheckMetadata registry in @n8n/decorators. Lays the foundation for a
pluggable cluster health-check system using n8n's established auto-discovery
pattern (@ContextEstablishmentHook, @AuthHandler, @Module, …). No checks are
implemented 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
ClusterCheckMetadata registry.

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 with checkDescription property + async run().
  • ClusterCheckContext{ currentState, previousState, diff }, all read-only.
    State maps are keyed by instanceKey, matching the existing
    InstanceStorage.getLastKnownState() contract.
  • ClusterStateDiff — pre-computed { added, removed, changed } so every check
    reuses the same diff rather than recomputing it.
  • ClusterCheckResult — three loosely-typed output channels:
    warnings[], auditEvents[], pushNotifications[].
  • ClusterCheckMetadata@Service() registry with register(),
    getEntries(), getClasses().
  • @ClusterCheck() — class decorator that registers the class and applies
    @Service() for DI.
  • Exports wired through cluster-check/index.ts and @n8n/decorators/src/index.ts.
  • Unit tests covering registration, ordering, DI resolution, and constructor
    dependency injection (8 tests).

Key implementation decisions

  • Output channels kept loose inside @n8n/decorators. Warnings, audit
    events, and push notifications are plain structured shapes ({ code, message, severity?, context? }, { eventName, payload }, { type, data }) rather
    than concrete EventMessageAudit classes or PushMessage discriminated
    unions. This keeps the decorator package free of packages/cli audit enums
    and @n8n/api-types/push churn. A thin translator in packages/cli (future
    ticket, under the Leader Service) maps each shape to the concrete type at
    emit time. Mirrors the precedent set by auth-handler,
    context-establishment, and pubsub.
  • InstanceRegistration imported from @n8n/api-types. Verified no
    circular dep (@n8n/api-types does 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/decorators
    dependencies and the matching TypeScript project reference.
  • No init() lifecycle hook. The ACs don't require it and checks are
    stateless over state snapshots. Can be added non-breakingly later if a
    concrete check needs it.
  • Pattern match with @ContextEstablishmentHook. Same two-file split
    (interface + metadata/decorator), same Set<Entry> storage, same decorator
    body (Container.get(Metadata).register({ class: target }); return Service()(target);), same register / getEntries / getClasses trio.

Related Links

closes https://linear.app/n8n/issue/IAM-332/cluster-check-interface-and-decorator

Review / Merge checklist

  • I have seen this code, I have run this code, and I take responsibility for this code.
  • PR title and summary are descriptive. (conventions)
  • Docs updated or follow-up ticket created.
  • Tests included.
  • PR Labeled with Backport to Beta, Backport to Stable, or Backport to v1 (if the PR is an urgent fix that needs to be backported)

@afitzek afitzek force-pushed the afitzek/iam-332-cluster-check branch from d8802d1 to f60cff1 Compare April 20, 2026 15:39
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 20, 2026

Performance Comparison

Comparing currentlatest master14-day baseline

Idle baseline with Instance AI module loaded

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
instance-ai-rss-baseline 342.64 MB 386.52 MB 364.63 MB (σ 22.57) -11.4% -6.0%
instance-ai-heap-used-baseline 186.73 MB 186.88 MB 186.42 MB (σ 0.27) -0.1% +0.2% ⚠️

Memory consumption baseline with starter plan resources

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
memory-rss-baseline 282.50 MB 288.44 MB 290.51 MB (σ 41.09) -2.1% -2.8%
memory-heap-used-baseline 114.37 MB 114.39 MB 114.37 MB (σ 0.40) -0.0% +0.0%

docker-stats

Metric Current Latest Master Baseline (avg) vs Master vs Baseline Status
docker-image-size-runners 388.00 MB 412.00 MB 392.93 MB (σ 11.30) -5.8% -1.3%
docker-image-size-n8n 1269.76 MB 1300.48 MB 1273.86 MB (σ 10.81) -2.4% -0.3%
How to read this table
  • Current: This PR's value (or latest master if PR perf tests haven't run)
  • Latest Master: Most recent nightly master measurement
  • Baseline: Rolling 14-day average from master
  • vs Master: PR impact (current vs latest master)
  • vs Baseline: Drift from baseline (current vs rolling avg)
  • Status: ✅ within 1σ | ⚠️ 1-2σ | 🔴 >2σ regression

@n8n-assistant n8n-assistant Bot added the n8n team Authored by the n8n team label Apr 20, 2026
@afitzek afitzek marked this pull request as ready for review April 20, 2026 17:27
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
Loading

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread packages/@n8n/decorators/src/cluster-check/cluster-check-metadata.ts Outdated
@afitzek afitzek requested review from a team, BGZStephen, cstuncsik, guillaumejacquart and phyllis-noester and removed request for a team April 20, 2026 19:36
Copy link
Copy Markdown
Contributor

@guillaumejacquart guillaumejacquart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

very clear PR 🚀

@afitzek afitzek added this pull request to the merge queue Apr 22, 2026
Merged via the queue into master with commit 29f0677 Apr 22, 2026
56 checks passed
@afitzek afitzek deleted the afitzek/iam-332-cluster-check branch April 22, 2026 06:47
@n8n-assistant
Copy link
Copy Markdown
Contributor

n8n-assistant Bot commented Apr 28, 2026

Got released with n8n@2.19.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

n8n team Authored by the n8n team Released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants