@@ -5,8 +5,10 @@ import type { WorkflowEntity } from '@n8n/db';
5
5
import type { IWorkflowDb } from '@n8n/db' ;
6
6
import type { WorkflowExecuteAfterContext } from '@n8n/decorators' ;
7
7
import { Container } from '@n8n/di' ;
8
+ import type { MockProxy } from 'jest-mock-extended' ;
8
9
import { mock } from 'jest-mock-extended' ;
9
10
import { DateTime } from 'luxon' ;
11
+ import type { InstanceSettings , InstanceType } from 'n8n-core' ;
10
12
import type { IRun } from 'n8n-workflow' ;
11
13
12
14
import { mockLogger } from '@test/mocking' ;
@@ -52,17 +54,30 @@ describe('startTimers', () => {
52
54
let compactionService : InsightsCompactionService ;
53
55
let collectionService : InsightsCollectionService ;
54
56
let pruningService : InsightsPruningService ;
57
+ let instanceSettings : MockProxy < InstanceSettings > ;
58
+ let isLeader = jest . fn ( ( ) => true ) ;
59
+ let isPruningEnabled = jest . fn ( ( ) => false ) ;
55
60
56
61
beforeAll ( ( ) => {
57
62
compactionService = mock < InsightsCompactionService > ( ) ;
58
63
collectionService = mock < InsightsCollectionService > ( ) ;
59
- pruningService = mock < InsightsPruningService > ( { isPruningEnabled : true } ) ;
64
+ pruningService = mock < InsightsPruningService > ( ) ;
65
+ Object . defineProperty ( pruningService , 'isPruningEnabled' , {
66
+ get : isPruningEnabled ,
67
+ } ) ;
68
+ instanceSettings = mock < InstanceSettings > ( {
69
+ instanceType : 'main' ,
70
+ } ) ;
71
+ Object . defineProperty ( instanceSettings , 'isLeader' , {
72
+ get : isLeader ,
73
+ } ) ;
60
74
insightsService = new InsightsService (
61
75
mock < InsightsByPeriodRepository > ( ) ,
62
76
compactionService ,
63
77
collectionService ,
64
78
pruningService ,
65
79
mock < LicenseState > ( ) ,
80
+ instanceSettings ,
66
81
mockLogger ( ) ,
67
82
) ;
68
83
} ) ;
@@ -71,21 +86,48 @@ describe('startTimers', () => {
71
86
jest . clearAllMocks ( ) ;
72
87
} ) ;
73
88
74
- test ( 'starts compaction, flushing and pruning timers' , ( ) => {
89
+ test ( 'starts compaction, flushing timers for main leader instance' , ( ) => {
90
+ isLeader . mockReturnValueOnce ( true ) ;
91
+ isPruningEnabled . mockReturnValueOnce ( false ) ;
92
+ insightsService . startTimers ( ) ;
93
+
94
+ expect ( collectionService . startFlushingTimer ) . toHaveBeenCalled ( ) ;
95
+ expect ( compactionService . startCompactionTimer ) . toHaveBeenCalled ( ) ;
96
+ expect ( pruningService . startPruningTimer ) . not . toHaveBeenCalled ( ) ;
97
+ } ) ;
98
+
99
+ test ( 'starts compaction, flushing and pruning timers for main leader instance with pruning enabled' , ( ) => {
100
+ isLeader . mockReturnValueOnce ( true ) ;
101
+ isPruningEnabled . mockReturnValueOnce ( true ) ;
75
102
insightsService . startTimers ( ) ;
76
103
77
104
expect ( collectionService . startFlushingTimer ) . toHaveBeenCalled ( ) ;
78
105
expect ( compactionService . startCompactionTimer ) . toHaveBeenCalled ( ) ;
79
106
expect ( pruningService . startPruningTimer ) . toHaveBeenCalled ( ) ;
80
107
} ) ;
81
108
82
- test ( 'starts only collection flushing timer when onlyCollection is true' , ( ) => {
83
- insightsService . startTimers ( { onlyCollection : true } ) ;
109
+ test ( 'starts only collection flushing timer for webhook instance' , ( ) => {
110
+ ( instanceSettings as any ) . instanceType = 'webhook' ;
111
+ insightsService . startTimers ( ) ;
84
112
85
113
expect ( collectionService . startFlushingTimer ) . toHaveBeenCalled ( ) ;
86
114
expect ( compactionService . startCompactionTimer ) . not . toHaveBeenCalled ( ) ;
87
115
expect ( pruningService . startPruningTimer ) . not . toHaveBeenCalled ( ) ;
88
116
} ) ;
117
+
118
+ test . each < InstanceType > ( [ 'worker' , 'main' ] ) (
119
+ 'do not start any timers for instance of type %s when not leader' ,
120
+ ( instanceType : InstanceType ) => {
121
+ ( instanceSettings as any ) . instanceType = instanceType ;
122
+ isLeader . mockReturnValue ( false ) ;
123
+
124
+ insightsService . startTimers ( ) ;
125
+
126
+ expect ( collectionService . startFlushingTimer ) . not . toHaveBeenCalled ( ) ;
127
+ expect ( compactionService . startCompactionTimer ) . not . toHaveBeenCalled ( ) ;
128
+ expect ( pruningService . startPruningTimer ) . not . toHaveBeenCalled ( ) ;
129
+ } ,
130
+ ) ;
89
131
} ) ;
90
132
91
133
describe ( 'getInsightsSummary' , ( ) => {
@@ -553,6 +595,7 @@ describe('getAvailableDateRanges', () => {
553
595
mock < InsightsCollectionService > ( ) ,
554
596
mock < InsightsPruningService > ( ) ,
555
597
licenseMock ,
598
+ mock < InstanceSettings > ( ) ,
556
599
mockLogger ( ) ,
557
600
) ;
558
601
} ) ;
@@ -655,6 +698,7 @@ describe('getMaxAgeInDaysAndGranularity', () => {
655
698
mock < InsightsCollectionService > ( ) ,
656
699
mock < InsightsPruningService > ( ) ,
657
700
licenseMock ,
701
+ mock < InstanceSettings > ( ) ,
658
702
mockLogger ( ) ,
659
703
) ;
660
704
} ) ;
@@ -743,6 +787,7 @@ describe('shutdown', () => {
743
787
mockCollectionService ,
744
788
mockPruningService ,
745
789
mock < LicenseState > ( ) ,
790
+ mock < InstanceSettings > ( ) ,
746
791
mockLogger ( ) ,
747
792
) ;
748
793
} ) ;
@@ -758,74 +803,6 @@ describe('shutdown', () => {
758
803
} ) ;
759
804
} ) ;
760
805
761
- describe ( 'timers' , ( ) => {
762
- let insightsService : InsightsService ;
763
-
764
- const mockCollectionService = mock < InsightsCollectionService > ( {
765
- startFlushingTimer : jest . fn ( ) ,
766
- stopFlushingTimer : jest . fn ( ) ,
767
- } ) ;
768
-
769
- const mockCompactionService = mock < InsightsCompactionService > ( {
770
- startCompactionTimer : jest . fn ( ) ,
771
- stopCompactionTimer : jest . fn ( ) ,
772
- } ) ;
773
-
774
- const mockPruningService = mock < InsightsPruningService > ( {
775
- startPruningTimer : jest . fn ( ) ,
776
- stopPruningTimer : jest . fn ( ) ,
777
- isPruningEnabled : false ,
778
- } ) ;
779
-
780
- const mockedLogger = mockLogger ( ) ;
781
- const mockedConfig = mock < InsightsConfig > ( {
782
- maxAgeDays : - 1 ,
783
- } ) ;
784
-
785
- beforeAll ( ( ) => {
786
- insightsService = new InsightsService (
787
- mock < InsightsByPeriodRepository > ( ) ,
788
- mockCompactionService ,
789
- mockCollectionService ,
790
- mockPruningService ,
791
- mock < LicenseState > ( ) ,
792
- mockedLogger ,
793
- ) ;
794
- } ) ;
795
-
796
- test ( 'startTimers starts timers except pruning' , ( ) => {
797
- // ACT
798
- insightsService . startTimers ( ) ;
799
-
800
- // ASSERT
801
- expect ( mockCompactionService . startCompactionTimer ) . toHaveBeenCalled ( ) ;
802
- expect ( mockCollectionService . startFlushingTimer ) . toHaveBeenCalled ( ) ;
803
- expect ( mockPruningService . startPruningTimer ) . not . toHaveBeenCalled ( ) ;
804
- } ) ;
805
-
806
- test ( 'startTimers starts pruning timer' , ( ) => {
807
- // ARRANGE
808
- mockedConfig . maxAgeDays = 30 ;
809
- Object . defineProperty ( mockPruningService , 'isPruningEnabled' , { value : true } ) ;
810
-
811
- // ACT
812
- insightsService . startTimers ( ) ;
813
-
814
- // ASSERT
815
- expect ( mockPruningService . startPruningTimer ) . toHaveBeenCalled ( ) ;
816
- } ) ;
817
-
818
- test ( 'stopTimers stops timers' , ( ) => {
819
- // ACT
820
- insightsService . stopTimers ( ) ;
821
-
822
- // ASSERT
823
- expect ( mockCompactionService . stopCompactionTimer ) . toHaveBeenCalled ( ) ;
824
- expect ( mockCollectionService . stopFlushingTimer ) . toHaveBeenCalled ( ) ;
825
- expect ( mockPruningService . stopPruningTimer ) . toHaveBeenCalled ( ) ;
826
- } ) ;
827
- } ) ;
828
-
829
806
describe ( 'legacy sqlite (without pooling) handles concurrent insights db process without throwing' , ( ) => {
830
807
let initialFlushBatchSize : number ;
831
808
let insightsConfig : InsightsConfig ;
0 commit comments