n8n/packages/workflow
Garrit Franke d24fa54996
perf(editor): harden node search data layer with index, EXISTS sharing and keyset scan
Ports the measured DB-layer hardening from #30294 onto the node content
search, and closes two correctness gaps:

1. Sharing as EXISTS, not `workflow.id IN (subquery)`. The IN form makes
   SQLite drive from shared_workflow and sort every LIKE match in a temp
   B-tree, discarding the updatedAt index (measured on a 20k corpus in
   #30294; plan-asserted there in node-search-variants.perf.ts).

2. Index on workflow_entity.updatedAt (migration
   AddUpdatedAtIndexToWorkflowEntity1786525332822 + entity mirror). The
   query orders by updatedAt and stops at the limit; without the index
   every keystroke sorts the whole table (149ms -> 0.9ms at 20k). The
   migration was already registered in the generated migration index on
   this branch but the file itself was never committed.

3. Keyset scan instead of a fixed 500-candidate cap. The SQL pre-filter
   matches the raw JSON blob - node ids, webhook ids, positions - which
   the in-memory matcher deliberately skips, so a fixed cap let blob-only
   false positives displace real hits and silently return nothing. The
   service now batches 100 candidates on an (updatedAt, id) cursor (id
   tiebreak: updatedAt is not unique) until the result list fills,
   candidates run out, or a 2000-workflow safety rail stops pathological
   queries. The cursor is a row-value comparison, NOT the equivalent
   'a < x OR (a = x AND b < y)': the OR form makes SQLite pick a
   MULTI-INDEX OR plan that re-materialises per batch (measured 3.5s vs
   96ms per search at 20k in #30294).

   Credential names are matched too (new 'credentials' field, ranked
   last): they appear in the serialized workflow, so the pre-filter
   matches them, and without an in-memory counterpart a credential-name
   query only ever produced false positives - the most realistic trigger
   for the empty-results bug.

Also ports the Postgres planner hint (SET LOCAL enable_seqscan = off)
from #30294: Postgres cannot estimate '%...%' selectivity and otherwise
seq-scans + top-N sorts instead of walking the index (75ms -> 1ms).

Deliberately not ported from #30294 (yet): the pLimit(1) concurrency
gate + per-user rate limiter, and the performance benchmark suite.

Verified: matcher 40/40, service unit 19/19, integration 22/22 on both
SQLite and Postgres 16; typecheck and lint clean across db, cli,
workflow, api-types and editor-ui; editor-ui composable tests 24/24.
2026-08-12 16:24:01 +02:00
..
src perf(editor): harden node search data layer with index, EXISTS sharing and keyset scan 2026-08-12 16:24:01 +02:00
test perf(editor): harden node search data layer with index, EXISTS sharing and keyset scan 2026-08-12 16:24:01 +02:00
eslint.config.mjs chore: Bundle/2.x (#35563) 2026-08-05 08:29:40 +03:00
package.json 🚀 Release 2.34.0 (#35487) 2026-08-04 07:14:55 +00:00
README.md docs: Fix links to license files in readme (no-changelog) (#10257) 2024-08-06 15:21:57 +01:00
stryker.config.mjs feat(core): Make the vm expression engine the default (#35738) 2026-08-07 09:55:26 +00:00
tsconfig.build.cjs.json chore: Migrate all base packages to TypeScript 7 (#34338) 2026-07-17 12:56:29 +03:00
tsconfig.build.esm.json feat: Modernize build and testing for workflow package (no-changelog) (#16771) 2025-06-30 20:02:16 +03:00
tsconfig.json chore(core): Lock the @n8n/typeorm import ratchet and document the boundary (no-changelog) (#34370) 2026-07-21 08:26:21 +00:00
vitest.config.ts feat(core): Make the vm expression engine the default (#35738) 2026-08-07 09:55:26 +00:00
vitest.stryker.config.ts feat(core): Make the vm expression engine the default (#35738) 2026-08-07 09:55:26 +00:00

n8n.io - Workflow Automation

n8n-workflow

Workflow base code for n8n

npm install n8n-workflow

License

You can find the license information here