UNPKG

n8n

Version:

n8n Workflow Automation Tool

191 lines • 8.59 kB
"use strict"; var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InsightsCollectionService = void 0; const di_1 = require("@n8n/di"); const typeorm_1 = require("@n8n/typeorm"); const luxon_1 = require("luxon"); const n8n_core_1 = require("n8n-core"); const n8n_workflow_1 = require("n8n-workflow"); const shared_workflow_1 = require("../../databases/entities/shared-workflow"); const shared_workflow_repository_1 = require("../../databases/repositories/shared-workflow.repository"); const insights_metadata_1 = require("../../modules/insights/database/entities/insights-metadata"); const insights_raw_1 = require("../../modules/insights/database/entities/insights-raw"); const insights_config_1 = require("./insights.config"); const shouldSkipStatus = { success: false, crashed: false, error: false, canceled: true, new: true, running: true, unknown: true, waiting: true, }; const shouldSkipMode = { cli: false, error: false, retry: false, trigger: false, webhook: false, evaluation: false, integrated: true, internal: true, manual: true, }; let InsightsCollectionService = class InsightsCollectionService { constructor(sharedWorkflowRepository, insightsConfig, logger) { this.sharedWorkflowRepository = sharedWorkflowRepository; this.insightsConfig = insightsConfig; this.logger = logger; this.cachedMetadata = new Map(); this.bufferedInsights = new Set(); this.isAsynchronouslySavingInsights = true; this.flushesInProgress = new Set(); this.logger = this.logger.scoped('insights'); } startFlushingTimer() { this.isAsynchronouslySavingInsights = true; this.stopFlushingTimer(); this.flushInsightsRawBufferTimer = setTimeout(async () => await this.flushEvents(), this.insightsConfig.flushIntervalSeconds * 1000); } stopFlushingTimer() { if (this.flushInsightsRawBufferTimer !== undefined) { clearTimeout(this.flushInsightsRawBufferTimer); this.flushInsightsRawBufferTimer = undefined; } } async shutdown() { this.stopFlushingTimer(); this.isAsynchronouslySavingInsights = false; await Promise.all([...this.flushesInProgress, this.flushEvents()]); } async workflowExecuteAfterHandler(ctx, fullRunData) { if (shouldSkipStatus[fullRunData.status] || shouldSkipMode[fullRunData.mode]) { return; } const status = fullRunData.status === 'success' ? 'success' : 'failure'; const commonWorkflowData = { workflowId: ctx.workflowData.id, workflowName: ctx.workflowData.name, timestamp: luxon_1.DateTime.utc().toJSDate(), }; this.bufferedInsights.add({ ...commonWorkflowData, type: status, value: 1, }); if (fullRunData.stoppedAt) { const value = fullRunData.stoppedAt.getTime() - fullRunData.startedAt.getTime(); this.bufferedInsights.add({ ...commonWorkflowData, type: 'runtime_ms', value, }); } if (status === 'success' && ctx.workflowData.settings?.timeSavedPerExecution) { this.bufferedInsights.add({ ...commonWorkflowData, type: 'time_saved_min', value: ctx.workflowData.settings.timeSavedPerExecution, }); } if (!this.isAsynchronouslySavingInsights) { await this.flushEvents(); } if (this.bufferedInsights.size >= this.insightsConfig.flushBatchSize) { void this.flushEvents(); } } async saveInsightsMetadataAndRaw(insightsRawToInsertBuffer) { const workflowIdNames = new Map(); for (const event of insightsRawToInsertBuffer) { workflowIdNames.set(event.workflowId, event.workflowName); } await this.sharedWorkflowRepository.manager.transaction(async (trx) => { const sharedWorkflows = await trx.find(shared_workflow_1.SharedWorkflow, { where: { workflowId: (0, typeorm_1.In)([...workflowIdNames.keys()]), role: 'workflow:owner' }, relations: { project: true }, }); const metadataToUpsert = sharedWorkflows.reduce((acc, workflow) => { const cachedMetadata = this.cachedMetadata.get(workflow.workflowId); if (!cachedMetadata || cachedMetadata.projectId !== workflow.projectId || cachedMetadata.projectName !== workflow.project.name || cachedMetadata.workflowName !== workflowIdNames.get(workflow.workflowId)) { const metadata = new insights_metadata_1.InsightsMetadata(); metadata.projectId = workflow.projectId; metadata.projectName = workflow.project.name; metadata.workflowId = workflow.workflowId; metadata.workflowName = workflowIdNames.get(workflow.workflowId); acc.push(metadata); } return acc; }, []); await trx.upsert(insights_metadata_1.InsightsMetadata, metadataToUpsert, ['workflowId']); const upsertMetadata = await trx.findBy(insights_metadata_1.InsightsMetadata, { workflowId: (0, typeorm_1.In)(metadataToUpsert.map((m) => m.workflowId)), }); for (const metadata of upsertMetadata) { this.cachedMetadata.set(metadata.workflowId, metadata); } const events = []; for (const event of insightsRawToInsertBuffer) { const insight = new insights_raw_1.InsightsRaw(); const metadata = this.cachedMetadata.get(event.workflowId); if (!metadata) { throw new n8n_workflow_1.UnexpectedError(`Could not find shared workflow for insight with workflowId ${event.workflowId}`); } insight.metaId = metadata.metaId; insight.type = event.type; insight.value = event.value; insight.timestamp = event.timestamp; events.push(insight); } await trx.insert(insights_raw_1.InsightsRaw, events); }); } async flushEvents() { if (this.bufferedInsights.size === 0) { this.startFlushingTimer(); return; } this.stopFlushingTimer(); const bufferedInsightsToFlush = new Set(this.bufferedInsights); this.bufferedInsights.clear(); let flushPromise = undefined; flushPromise = (async () => { try { await this.saveInsightsMetadataAndRaw(bufferedInsightsToFlush); } catch (e) { this.logger.error('Error while saving insights metadata and raw data', { error: e }); for (const event of bufferedInsightsToFlush) { this.bufferedInsights.add(event); } } finally { this.startFlushingTimer(); this.flushesInProgress.delete(flushPromise); } })(); this.flushesInProgress.add(flushPromise); await flushPromise; } }; exports.InsightsCollectionService = InsightsCollectionService; exports.InsightsCollectionService = InsightsCollectionService = __decorate([ (0, di_1.Service)(), __metadata("design:paramtypes", [shared_workflow_repository_1.SharedWorkflowRepository, insights_config_1.InsightsConfig, n8n_core_1.Logger]) ], InsightsCollectionService); //# sourceMappingURL=insights-collection.service.js.map