diff --git a/packages/cli/src/events/WorkflowStatistics.ts b/packages/cli/src/events/WorkflowStatistics.ts index 8089e57398ca1..6a500399c5026 100644 --- a/packages/cli/src/events/WorkflowStatistics.ts +++ b/packages/cli/src/events/WorkflowStatistics.ts @@ -135,23 +135,35 @@ export async function nodeFetchedData( node: INode, ): Promise { if (!workflowId) return; - // Try to insert the data loaded statistic - try { - await Db.collections.WorkflowStatistics.insert({ + + const hasLoadedDataPreviously = await Db.collections.WorkflowStatistics.findOne({ + select: ['count'], + where: { workflowId, name: StatisticsNames.dataLoaded, - count: 1, - latestEvent: new Date(), - }); - } catch (error) { - // if it's a duplicate key error then that's fine, otherwise throw the error - if (!(error instanceof QueryFailedError)) { - throw error; - } - // If it is a query failed error, we return + }, + }); + + if (hasLoadedDataPreviously) { return; } + // Try to insert the data loaded statistic + try { + await Db.collections.WorkflowStatistics.createQueryBuilder('workflowStatistics') + .insert() + .values({ + workflowId, + name: StatisticsNames.dataLoaded, + count: 1, + latestEvent: new Date(), + }) + .orIgnore() + .execute(); + } catch (error) { + LoggerProxy.warn('Failed saving loaded data statistics'); + } + // Compile the metrics since this was a new data loaded event const owner = await getWorkflowOwner(workflowId); let metrics = { diff --git a/packages/cli/test/unit/Events.test.ts b/packages/cli/test/unit/Events.test.ts index ce9e3e58bd8f1..cb7f499d67d14 100644 --- a/packages/cli/test/unit/Events.test.ts +++ b/packages/cli/test/unit/Events.test.ts @@ -1,11 +1,11 @@ import type { IRun, WorkflowExecuteMode } from 'n8n-workflow'; import { LoggerProxy } from 'n8n-workflow'; -import { QueryFailedError } from 'typeorm'; import { mock } from 'jest-mock-extended'; import config from '@/config'; import * as Db from '@/Db'; import { User } from '@db/entities/User'; +import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import type { WorkflowStatistics } from '@db/entities/WorkflowStatistics'; import type { WorkflowStatisticsRepository } from '@db/repositories'; import { nodeFetchedData, workflowExecutionCompleted } from '@/events/WorkflowStatistics'; @@ -15,6 +15,7 @@ import { InternalHooks } from '@/InternalHooks'; import { mockInstance } from '../integration/shared/utils'; import { UserService } from '@/user/user.service'; +import { WorkflowEntity } from '@/databases/entities/WorkflowEntity'; jest.mock('@/Db', () => { return { @@ -202,8 +203,14 @@ describe('Events', () => { test('should not send metrics for entries that already have the flag set', async () => { // Fetch data for workflow 2 which is set up to not be altered in the mocks - workflowStatisticsRepository.insert.mockImplementationOnce(() => { - throw new QueryFailedError('invalid insert', [], ''); + workflowStatisticsRepository.findOne.mockImplementationOnce(async () => { + return { + count: 1, + name: StatisticsNames.dataLoaded, + latestEvent: new Date(), + workflowId: '2', + workflow: new WorkflowEntity(), + }; }); const workflowId = '1'; const node = {