Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf(core): Make execution queries faster #9817

Merged
merged 20 commits into from
Aug 22, 2024
Merged

perf(core): Make execution queries faster #9817

merged 20 commits into from
Aug 22, 2024

Conversation

ivov
Copy link
Contributor

@ivov ivov commented Jun 20, 2024

@ivov ivov changed the title perf(core): Speed up common execution queries perf(core): Accelerate common execution queries Jun 20, 2024
@n8n-assistant n8n-assistant bot added core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team labels Jun 20, 2024
@ivov ivov marked this pull request as ready for review June 20, 2024 13:23
@ivov ivov requested a review from a team as a code owner June 20, 2024 13:23
Copy link
Contributor

@krynble krynble left a comment

Choose a reason for hiding this comment

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

The way I see it is that we just need 4 indices, and the image below outlines the changes I'm proposing:

Screenshot 2024-06-24 at 10 22 50

The reason for each index has been described in the ticket, but in the end we want all 4 db systems to have only those 4 indices, wdyt?

@ivov
Copy link
Contributor Author

ivov commented Jun 24, 2024

Thanks, from the story I was under the impression we only wanted to touch these, rather than all of them.

@netroy netroy self-requested a review July 3, 2024 08:14
Copy link
Contributor

@tomi tomi left a comment

Choose a reason for hiding this comment

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

Couple comments, mainly about the structure of the indexes

@ivov ivov changed the title perf(core): Accelerate common execution queries perf(core): Make execution queries faster Aug 16, 2024
@ivov
Copy link
Contributor Author

ivov commented Aug 16, 2024

Find multiple executions

Method: ExecutionRepository.findManyByRangeQuery

Sample TypeORM log:

SELECT COUNT(DISTINCT("execution"."id")) AS "cnt" FROM "execution_entity" "execution" INNER JOIN "workflow_entity" "workflow" ON "workflow"."id"="execution"."workflowId" WHERE ( "execution"."workflowId" IN (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) AND "execution"."status" IN (?, ?) ) AND ( "execution"."deletedAt" IS NULL ) -- PARAMETERS: ["04OYwcQzYJdnjsdd","2N1V33FP2dKlmTz7","3SmuvtkVARLuQW6y","4Zo1ABVbAXz8pdhh","6fVKKFKQBGAnvZ4a","76hvMdsQBVBdnYNM","8dlw8vap8tPkg50i","A2v6XntNlA19XTBu","BDp700hAdBGEcoaI","CTfQjEFQcW5G3fMr","CfLvzSSWLAZVKyFV","Dp4reGx2lxez7Vni","EZ1fmiFhp6lby6Jg","Hm90qPVAq25VzrdD","LECmidOfr7VfQ8I7","LLRzyiCFeC9vmDki","Ov4lOrsuNJqQQvgm","Penre9AUYUc0oCr7","QIJEUiWfX8fe1qXO","QR2GgBoUbcM3JuuX","QnrkjpyTEfofdqTM","Rtm65p8UI6lmhqKX","UQtas8whCecK4p2c","UU6i2lrPR5Aipx0N","VT6WW8eWyqitfy16","X59nkhZx5Q5QxUVa","XNfBTQYWfrXaKtIE","XYKSUGOlf4jPJJ43","YTxYdrPMwsxQHj93","ZprM1PsYwZO3IBWA","fxc5dTtFiI1dlzzA","gCjcQNUkC5kepysS","h9zGiCxSeIPZtUW2","hDpmfTPWQMttZrGc","heRTgosoRcMOYZfB","j9TAi7mwIOhP3dJd","jYBbm6ZOWX1DNMRP","lexlovNxCRXANfqP","lqJVl2vUAs4hHIiU","nJKtmhRIZ3lwBekg","nJKtmhRIZ3lwBekg","onPzjgFhmLPqkWXU","rVJSrrVsFVInO1Mf","reVGqrHPH1xjdC3q","w71QwtdB2bThj4ap","zr50K32bPqIzW0jA","new","running"]

Equivalent SQL query:

SELECT COUNT(DISTINCT execution.id) AS cnt
FROM execution_entity execution
INNER JOIN workflow_entity workflow ON workflow.id = execution."workflowId"
WHERE execution."workflowId" IN (
    '04OYwcQzYJdnjsdd', '2N1V33FP2dKlmTz7', '3SmuvtkVARLuQW6y', '4Zo1ABVbAXz8pdhh',
    '6fVKKFKQBGAnvZ4a', '76hvMdsQBVBdnYNM', '8dlw8vap8tPkg50i', 'A2v6XntNlA19XTBu',
    'BDp700hAdBGEcoaI', 'CTfQjEFQcW5G3fMr', 'CfLvzSSWLAZVKyFV', 'Dp4reGx2lxez7Vni',
    'EZ1fmiFhp6lby6Jg', 'Hm90qPVAq25VzrdD', 'LECmidOfr7VfQ8I7', 'LLRzyiCFeC9vmDki',
    'Ov4lOrsuNJqQQvgm', 'Penre9AUYUc0oCr7', 'QIJEUiWfX8fe1qXO', 'QR2GgBoUbcM3JuuX',
    'QnrkjpyTEfofdqTM', 'Rtm65p8UI6lmhqKX', 'UQtas8whCecK4p2c', 'UU6i2lrPR5Aipx0N',
    'VT6WW8eWyqitfy16', 'X59nkhZx5Q5QxUVa', 'XNfBTQYWfrXaKtIE', 'XYKSUGOlf4jPJJ43',
    'YTxYdrPMwsxQHj93', 'ZprM1PsYwZO3IBWA', 'fxc5dTtFiI1dlzzA', 'gCjcQNUkC5kepysS',
    'h9zGiCxSeIPZtUW2', 'hDpmfTPWQMttZrGc', 'heRTgosoRcMOYZfB', 'j9TAi7mwIOhP3dJd',
    'jYBbm6ZOWX1DNMRP', 'lexlovNxCRXANfqP', 'lqJVl2vUAs4hHIiU', 'nJKtmhRIZ3lwBekg',
    'nJKtmhRIZ3lwBekg', 'onPzjgFhmLPqkWXU', 'rVJSrrVsFVInO1Mf', 'reVGqrHPH1xjdC3q',
    'w71QwtdB2bThj4ap', 'zr50K32bPqIzW0jA'
)
AND execution.status IN ('new', 'running')
AND execution."deletedAt" IS NULL;

EXPLAIN QUERY PLAN on SQLite 3.43 on master

SEARCH execution USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)
SEARCH workflow USING COVERING INDEX sqlite_autoindex_workflow_entity_1 (id=?)

EXPLAIN ANALYZE on Postgres 13.7 on master

Aggregate  (cost=16.55..16.56 rows=1 width=8) (actual time=0.027..0.030 rows=1 loops=1)
  ->  Nested Loop  (cost=0.29..16.54 rows=1 width=4) (actual time=0.008..0.010 rows=0 loops=1)
"        ->  Index Scan using ""IDX_execution_entity_deletedAt"" on execution_entity execution  (cost=0.15..8.23 rows=1 width=94) (actual time=0.007..0.008 rows=0 loops=1)"
"              Index Cond: (""deletedAt"" IS NULL)"
"              Filter: (((status)::text = ANY ('{new,running}'::text[])) AND ((""workflowId"")::text = ANY ('{04OYwcQzYJdnjsdd,2N1V33FP2dKlmTz7,3SmuvtkVARLuQW6y,4Zo1ABVbAXz8pdhh,6fVKKFKQBGAnvZ4a,76hvMdsQBVBdnYNM,8dlw8vap8tPkg50i,A2v6XntNlA19XTBu,BDp700hAdBGEcoaI,CTfQjEFQcW5G3fMr,CfLvzSSWLAZVKyFV,Dp4reGx2lxez7Vni,EZ1fmiFhp6lby6Jg,Hm90qPVAq25VzrdD,LECmidOfr7VfQ8I7,LLRzyiCFeC9vmDki,Ov4lOrsuNJqQQvgm,Penre9AUYUc0oCr7,QIJEUiWfX8fe1qXO,QR2GgBoUbcM3JuuX,QnrkjpyTEfofdqTM,Rtm65p8UI6lmhqKX,UQtas8whCecK4p2c,UU6i2lrPR5Aipx0N,VT6WW8eWyqitfy16,X59nkhZx5Q5QxUVa,XNfBTQYWfrXaKtIE,XYKSUGOlf4jPJJ43,YTxYdrPMwsxQHj93,ZprM1PsYwZO3IBWA,fxc5dTtFiI1dlzzA,gCjcQNUkC5kepysS,h9zGiCxSeIPZtUW2,hDpmfTPWQMttZrGc,heRTgosoRcMOYZfB,j9TAi7mwIOhP3dJd,jYBbm6ZOWX1DNMRP,lexlovNxCRXANfqP,lqJVl2vUAs4hHIiU,nJKtmhRIZ3lwBekg,nJKtmhRIZ3lwBekg,onPzjgFhmLPqkWXU,rVJSrrVsFVInO1Mf,reVGqrHPH1xjdC3q,w71QwtdB2bThj4ap,zr50K32bPqIzW0jA}'::text[])))"
        ->  Index Only Scan using workflow_entity_pkey on workflow_entity workflow  (cost=0.14..8.16 rows=1 width=90) (never executed)
"              Index Cond: (id = (execution.""workflowId"")::text)"
              Heap Fetches: 0
Planning Time: 1.705 ms
Execution Time: 0.133 ms

EXPLAIN QUERY PLAN on SQLite 3.43 on pay-1609

SEARCH execution USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)
SEARCH workflow USING COVERING INDEX sqlite_autoindex_workflow_entity_1 (id=?)

EXPLAIN ANALYZE on Postgres 13.7 on pay-1609

Aggregate  (cost=16.55..16.56 rows=1 width=8) (actual time=0.149..0.153 rows=1 loops=1)
  ->  Nested Loop  (cost=0.29..16.54 rows=1 width=4) (actual time=0.016..0.019 rows=0 loops=1)
"        ->  Index Scan using ""IDX_execution_entity_deletedAt"" on execution_entity execution  (cost=0.15..8.23 rows=1 width=94) (actual time=0.013..0.015 rows=0 loops=1)"
"              Index Cond: (""deletedAt"" IS NULL)"
"              Filter: (((status)::text = ANY ('{new,running}'::text[])) AND ((""workflowId"")::text = ANY ('{04OYwcQzYJdnjsdd,2N1V33FP2dKlmTz7,3SmuvtkVARLuQW6y,4Zo1ABVbAXz8pdhh,6fVKKFKQBGAnvZ4a,76hvMdsQBVBdnYNM,8dlw8vap8tPkg50i,A2v6XntNlA19XTBu,BDp700hAdBGEcoaI,CTfQjEFQcW5G3fMr,CfLvzSSWLAZVKyFV,Dp4reGx2lxez7Vni,EZ1fmiFhp6lby6Jg,Hm90qPVAq25VzrdD,LECmidOfr7VfQ8I7,LLRzyiCFeC9vmDki,Ov4lOrsuNJqQQvgm,Penre9AUYUc0oCr7,QIJEUiWfX8fe1qXO,QR2GgBoUbcM3JuuX,QnrkjpyTEfofdqTM,Rtm65p8UI6lmhqKX,UQtas8whCecK4p2c,UU6i2lrPR5Aipx0N,VT6WW8eWyqitfy16,X59nkhZx5Q5QxUVa,XNfBTQYWfrXaKtIE,XYKSUGOlf4jPJJ43,YTxYdrPMwsxQHj93,ZprM1PsYwZO3IBWA,fxc5dTtFiI1dlzzA,gCjcQNUkC5kepysS,h9zGiCxSeIPZtUW2,hDpmfTPWQMttZrGc,heRTgosoRcMOYZfB,j9TAi7mwIOhP3dJd,jYBbm6ZOWX1DNMRP,lexlovNxCRXANfqP,lqJVl2vUAs4hHIiU,nJKtmhRIZ3lwBekg,nJKtmhRIZ3lwBekg,onPzjgFhmLPqkWXU,rVJSrrVsFVInO1Mf,reVGqrHPH1xjdC3q,w71QwtdB2bThj4ap,zr50K32bPqIzW0jA}'::text[])))"
        ->  Index Only Scan using workflow_entity_pkey on workflow_entity workflow  (cost=0.14..8.16 rows=1 width=90) (never executed)
"              Index Cond: (id = (execution.""workflowId"")::text)"
              Heap Fetches: 0
Planning Time: 4.385 ms
Execution Time: 0.361 ms

Find waiting executions

Method: ExecutionRepository.getWaitingExecutions

Sample TypeORM log:

SELECT "ExecutionEntity"."id" AS "ExecutionEntity_id", "ExecutionEntity"."waitTill" AS "ExecutionEntity_waitTill" FROM "execution_entity" "ExecutionEntity" WHERE ( (("ExecutionEntity"."waitTill" <= ?) AND ("ExecutionEntity"."status" != ?)) ) AND ( "ExecutionEntity"."deletedAt" IS NULL ) ORDER BY "ExecutionEntity_waitTill" ASC -- PARAMETERS: ["2024-08-16 11:37:40.997","crashed"]

SQL query:

SELECT id AS "ExecutionEntity_id", "waitTill" AS "ExecutionEntity_waitTill" 
FROM execution_entity 
WHERE ("waitTill" <= '2024-08-16 11:37:40.997' AND status != 'crashed')
  AND "deletedAt" IS NULL 
ORDER BY "waitTill" ASC;

EXPLAIN QUERY PLAN on SQLite 3.43 on master

SEARCH execution_entity USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)
USE TEMP B-TREE FOR ORDER BY

EXPLAIN ANALYZE on Postgres 13.7 on master

Sort  (cost=8.18..8.19 rows=1 width=12) (actual time=0.118..0.121 rows=0 loops=1)
"  Sort Key: ""waitTill"""
  Sort Method: quicksort  Memory: 25kB
"  ->  Index Scan using ""IDX_execution_entity_deletedAt"" on execution_entity  (cost=0.15..8.17 rows=1 width=12) (actual time=0.016..0.018 rows=0 loops=1)"
"        Index Cond: (""deletedAt"" IS NULL)"
"        Filter: ((""waitTill"" <= '2024-08-16 11:37:40.997+02'::timestamp with time zone) AND ((status)::text <> 'crashed'::text))"
Planning Time: 2.057 ms
Execution Time: 0.427 ms

EXPLAIN QUERY PLAN on SQLite 3.43 on pay-1609

SEARCH execution_entity USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)
USE TEMP B-TREE FOR ORDER BY

EXPLAIN ANALYZE on Postgres 13.7 on pay-1609

Index Scan using idx_execution_entity_wait_till_status_deleted_at on execution_entity  (cost=0.12..8.14 rows=1 width=12) (actual time=0.007..0.008 rows=0 loops=1)
"  Index Cond: (""waitTill"" <= '2024-08-16 11:37:40.997+02'::timestamp with time zone)"
"  Filter: ((status)::text <> 'crashed'::text)"
Planning Time: 0.383 ms
Execution Time: 0.046 ms

Query to soft-delete executions

Method: ExecutionRepository.softDeletePrunableExecutions

Sample TypeORM log:

UPDATE "execution_entity" SET "deletedAt" = ? WHERE ("deletedAt" IS NULL AND NOT("status" IN (?, ?, ?))) AND ("stoppedAt" <= ?) -- PARAMETERS: ["2024-08-16 11:39:13.455","new","running","waiting","2024-08-02 11:39:13.442"]

SQL:

UPDATE execution_entity
SET "deletedAt" = '2024-08-16 11:39:13.455'
WHERE "deletedAt" IS NULL
  AND "status" NOT IN ('new', 'running', 'waiting')
  AND "stoppedAt" <= '2024-08-02 11:39:13.442';

EXPLAIN QUERY PLAN on SQLite 3.43 on master

SEARCH execution_entity USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)

EXPLAIN ANALYZE on Postgres 13.7 on master

Update on execution_entity  (cost=0.15..8.17 rows=1 width=273) (actual time=0.006..0.008 rows=0 loops=1)
"  ->  Index Scan using ""IDX_execution_entity_deletedAt"" on execution_entity  (cost=0.15..8.17 rows=1 width=273) (actual time=0.004..0.004 rows=0 loops=1)"
"        Index Cond: (""deletedAt"" IS NULL)"
"        Filter: ((""stoppedAt"" <= '2024-08-02 11:39:13.442+02'::timestamp with time zone) AND ((status)::text <> ALL ('{new,running,waiting}'::text[])))"
Planning Time: 0.407 ms
Execution Time: 0.085 ms

EXPLAIN QUERY PLAN on SQLite 3.43 on pay-1609

SEARCH execution_entity USING INDEX IDX_execution_entity_deletedAt (deletedAt=?)

EXPLAIN ANALYZE on Postgres 13.7 on pay-1609

Update on execution_entity  (cost=0.12..8.15 rows=1 width=273) (actual time=0.015..0.021 rows=0 loops=1)
  ->  Index Scan using idx_execution_entity_stopped_at_status_deleted_at on execution_entity  (cost=0.12..8.15 rows=1 width=273) (actual time=0.010..0.011 rows=0 loops=1)
"        Index Cond: ((""stoppedAt"" <= '2024-08-02 11:39:13.442+02'::timestamp with time zone) AND (""deletedAt"" IS NULL))"
"        Filter: ((status)::text <> ALL ('{new,running,waiting}'::text[]))"
Planning Time: 0.487 ms
Execution Time: 0.196 ms

Copy link
Contributor

@tomi tomi left a comment

Choose a reason for hiding this comment

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

Nice stuff 🎉 Just one very small comment. Feel free to ignore it

Comment on lines +63 to +65
await schemaBuilder.createIndex('execution_entity', ['workflowId', 'startedAt']);
await schemaBuilder.createIndex('execution_entity', ['waitTill', 'status', 'deletedAt']);
await schemaBuilder.createIndex('execution_entity', ['stoppedAt', 'status', 'deletedAt']);
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe these could be named explicitly as well, so the names would match for all different DBs

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, since we'll be dropping MySQL, I think this is okay for now.

Copy link

cypress bot commented Aug 22, 2024



Test summary

414 0 0 0Flakiness 1


Run details

Project n8n
Status Passed
Commit 4068175
Started Aug 22, 2024 10:42 AM
Ended Aug 22, 2024 10:47 AM
Duration 04:41 💡
OS Linux Debian -
Browser Electron 118

View run in Cypress Cloud ➡️


Flakiness

e2e/17-sharing.cy.ts Flakiness
1 Sharing > credentials should work between team and personal projects

This comment has been generated by cypress-bot as a result of this project's GitHub integration settings. You can manage this integration in this project's settings in the Cypress Cloud

Copy link
Contributor

✅ All Cypress E2E specs passed

@ivov ivov merged commit dc7dc99 into master Aug 22, 2024
41 checks passed
@ivov ivov deleted the pay-1609 branch August 22, 2024 11:27
This was referenced Aug 28, 2024
@janober
Copy link
Member

janober commented Aug 28, 2024

Got released with n8n@1.57.0

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Enhancement outside /nodes-base and /editor-ui n8n team Authored by the n8n team Released
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants