Performance improvements for executions count on Postgres (#1888)

* Performance improvements for executions count on Postgres

As reported by a community member https://community.n8n.io/t/stress-load-testing/4846/5
and https://github.com/n8n-io/n8n/issues/1578, when using postgres with
a big volume of executions, the executions list's performance degrades.

This PR is aimed at Postgres specifically by querying postgres' stats
collector instead of running a full table scan, providing a good
estimate.

More can be read here: https://www.citusdata.com/blog/2016/10/12/count-performance/

* Removed order of magnitude so we display closer numbers

* Making count based on statistics only when not applying filters

*  Minor styling improvements

Co-authored-by: Jan Oberhauser <jan.oberhauser@gmail.com>
This commit is contained in:
Omar Ajoue
2021-07-06 23:25:25 +02:00
committed by GitHub
parent 297d9fe77d
commit 8d235e94cb
4 changed files with 50 additions and 9 deletions

View File

@@ -325,6 +325,7 @@ export interface IExecutionShortResponse {
export interface IExecutionsListResponse {
count: number;
results: IExecutionsSummary[];
estimated: boolean;
}
export interface IExecutionsCurrentSummaryExtended {

View File

@@ -1,6 +1,6 @@
<template>
<span>
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`Workflow Executions (${combinedExecutions.length}/${combinedExecutionsCount})`" :before-close="closeDialog">
<el-dialog :visible="dialogVisible" append-to-body width="80%" :title="`Workflow Executions ${combinedExecutions.length}/${finishedExecutionsCountEstimated === true ? '~' : ''}${combinedExecutionsCount}`" :before-close="closeDialog">
<div class="filters">
<el-row>
<el-col :span="4" class="filter-headline">
@@ -38,7 +38,7 @@
<div class="selection-options">
<span v-if="checkAll === true || isIndeterminate === true">
Selected: {{numSelected}}/{{finishedExecutionsCount}}
Selected: {{numSelected}} / <span v-if="finishedExecutionsCountEstimated === true">~</span>{{finishedExecutionsCount}}
<el-button type="danger" title="Delete Selected" icon="el-icon-delete" size="mini" @click="handleDeleteSelected" circle></el-button>
</span>
</div>
@@ -142,7 +142,7 @@
</el-table-column>
</el-table>
<div class="load-more" v-if="finishedExecutionsCount > finishedExecutions.length">
<div class="load-more" v-if="finishedExecutionsCount > finishedExecutions.length || finishedExecutionsCountEstimated === true">
<el-button title="Load More" @click="loadMore()" size="small" :disabled="isDataLoading">
<font-awesome-icon icon="sync" /> Load More
</el-button>
@@ -200,6 +200,7 @@ export default mixins(
return {
finishedExecutions: [] as IExecutionsSummary[],
finishedExecutionsCount: 0,
finishedExecutionsCountEstimated: false,
checkAll: false,
autoRefresh: true,
@@ -256,7 +257,7 @@ export default mixins(
return returnData;
},
combinedExecutionsCount (): number {
return this.activeExecutions.length + this.finishedExecutionsCount;
return 0 + this.activeExecutions.length + this.finishedExecutionsCount;
},
numSelected (): number {
if (this.checkAll === true) {
@@ -489,16 +490,19 @@ export default mixins(
}
this.finishedExecutions = this.finishedExecutions.filter(execution => !gaps.includes(parseInt(execution.id, 10)) && lastId >= parseInt(execution.id, 10));
this.finishedExecutionsCount = results[0].count;
this.finishedExecutionsCountEstimated = results[0].estimated;
},
async loadFinishedExecutions (): Promise<void> {
if (this.filter.status === 'running') {
this.finishedExecutions = [];
this.finishedExecutionsCount = 0;
this.finishedExecutionsCountEstimated = false;
return;
}
const data = await this.restApi().getPastExecutions(this.workflowFilterPast, this.requestItemsPerRequest);
this.finishedExecutions = data.results;
this.finishedExecutionsCount = data.count;
this.finishedExecutionsCountEstimated = data.estimated;
},
async loadMore () {
if (this.filter.status === 'running') {
@@ -526,6 +530,7 @@ export default mixins(
this.finishedExecutions.push.apply(this.finishedExecutions, data.results);
this.finishedExecutionsCount = data.count;
this.finishedExecutionsCountEstimated = data.estimated;
this.isDataLoading = false;
},