mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-19 16:55:02 +02:00
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. |
||
|---|---|---|
| .. | ||
| src | ||
| test | ||
| eslint.config.mjs | ||
| package.json | ||
| README.md | ||
| stryker.config.mjs | ||
| tsconfig.build.cjs.json | ||
| tsconfig.build.esm.json | ||
| tsconfig.json | ||
| vitest.config.ts | ||
| vitest.stryker.config.ts | ||
