25
25
package scanner
26
26
27
27
import (
28
+ "context"
29
+ "sync"
28
30
"testing"
29
31
30
32
"github.com/golang/mock/gomock"
31
33
"github.com/stretchr/testify/suite"
34
+ "go.temporal.io/sdk/client"
32
35
33
36
"go.temporal.io/server/api/adminservicemock/v1"
34
37
"go.temporal.io/server/api/historyservicemock/v1"
35
38
"go.temporal.io/server/common/config"
39
+ "go.temporal.io/server/common/dynamicconfig"
36
40
"go.temporal.io/server/common/log"
37
41
"go.temporal.io/server/common/metrics"
38
42
"go.temporal.io/server/common/namespace"
@@ -161,27 +165,13 @@ func (s *scannerTestSuite) TestScannerEnabled() {
161
165
scanner := New (
162
166
log .NewNoopLogger (),
163
167
& Config {
164
- MaxConcurrentActivityExecutionSize : func () int {
165
- return 1
166
- },
167
- MaxConcurrentWorkflowTaskExecutionSize : func () int {
168
- return 1
169
- },
170
- MaxConcurrentActivityTaskPollers : func () int {
171
- return 1
172
- },
173
- MaxConcurrentWorkflowTaskPollers : func () int {
174
- return 1
175
- },
176
- ExecutionsScannerEnabled : func () bool {
177
- return c .ExecutionsScannerEnabled
178
- },
179
- HistoryScannerEnabled : func () bool {
180
- return c .HistoryScannerEnabled
181
- },
182
- TaskQueueScannerEnabled : func () bool {
183
- return c .TaskQueueScannerEnabled
184
- },
168
+ MaxConcurrentActivityExecutionSize : dynamicconfig .GetIntPropertyFn (1 ),
169
+ MaxConcurrentWorkflowTaskExecutionSize : dynamicconfig .GetIntPropertyFn (1 ),
170
+ MaxConcurrentActivityTaskPollers : dynamicconfig .GetIntPropertyFn (1 ),
171
+ MaxConcurrentWorkflowTaskPollers : dynamicconfig .GetIntPropertyFn (1 ),
172
+ HistoryScannerEnabled : dynamicconfig .GetBoolPropertyFn (c .HistoryScannerEnabled ),
173
+ ExecutionsScannerEnabled : dynamicconfig .GetBoolPropertyFn (c .ExecutionsScannerEnabled ),
174
+ TaskQueueScannerEnabled : dynamicconfig .GetBoolPropertyFn (c .TaskQueueScannerEnabled ),
185
175
Persistence : & config.Persistence {
186
176
DefaultStore : c .DefaultStore ,
187
177
DataStores : map [string ]config.DataStore {
@@ -201,18 +191,96 @@ func (s *scannerTestSuite) TestScannerEnabled() {
201
191
mockNamespaceRegistry ,
202
192
mockWorkerFactory ,
203
193
)
194
+ var wg sync.WaitGroup
204
195
for _ , sc := range c .ExpectedScanners {
196
+ wg .Add (1 )
205
197
worker := mocksdk .NewMockWorker (ctrl )
206
198
worker .EXPECT ().RegisterActivityWithOptions (gomock .Any (), gomock .Any ()).AnyTimes ()
207
199
worker .EXPECT ().RegisterWorkflowWithOptions (gomock .Any (), gomock .Any ()).AnyTimes ()
208
200
worker .EXPECT ().Start ()
209
201
mockWorkerFactory .EXPECT ().New (gomock .Any (), sc .TaskQueueName , gomock .Any ()).Return (worker )
210
202
mockSdkClientFactory .EXPECT ().GetSystemClient ().Return (mockSdkClient ).AnyTimes ()
211
- mockSdkClient .EXPECT ().ExecuteWorkflow (gomock .Any (), gomock .Any (), sc .WFTypeName , gomock .Any ())
203
+ mockSdkClient .EXPECT ().ExecuteWorkflow (gomock .Any (), gomock .Any (), sc .WFTypeName ,
204
+ gomock .Any ()).Do (func (
205
+ _ context.Context ,
206
+ _ client.StartWorkflowOptions ,
207
+ _ string ,
208
+ _ ... interface {},
209
+ ) {
210
+ wg .Done ()
211
+ })
212
212
}
213
213
err := scanner .Start ()
214
214
s .NoError (err )
215
+ wg .Wait ()
215
216
scanner .Stop ()
216
217
})
217
218
}
218
219
}
220
+
221
+ // TestScannerWorkflow tests that the scanner can be shut down even when it hasn't finished starting.
222
+ // This fixes a rare issue that can occur when Stop() is called quickly after Start(). When Start() is called, the
223
+ // scanner starts a new goroutine for each scanner type. In that goroutine, an sdk client is created which dials the
224
+ // frontend service. If the test driver calls Stop() on the server, then the server stops the frontend service and the
225
+ // history service. In some cases, the frontend services stops before the sdk client has finished connecting to it.
226
+ // This causes the startWorkflow() call to fail with an error. However, startWorkflowWithRetry retries the call for
227
+ // a whole minute, which causes the test to take a long time to fail. So, instead we immediately cancel all async
228
+ // requests when Stop() is called.
229
+ func (s * scannerTestSuite ) TestScannerShutdown () {
230
+ ctrl := gomock .NewController (s .T ())
231
+
232
+ logger := log .NewTestLogger ()
233
+ mockSdkClientFactory := sdk .NewMockClientFactory (ctrl )
234
+ mockSdkClient := mocksdk .NewMockClient (ctrl )
235
+ mockNamespaceRegistry := namespace .NewMockRegistry (ctrl )
236
+ mockAdminClient := adminservicemock .NewMockAdminServiceClient (ctrl )
237
+ mockWorkerFactory := sdk .NewMockWorkerFactory (ctrl )
238
+ worker := mocksdk .NewMockWorker (ctrl )
239
+ scanner := New (
240
+ logger ,
241
+ & Config {
242
+ MaxConcurrentActivityExecutionSize : dynamicconfig .GetIntPropertyFn (1 ),
243
+ MaxConcurrentWorkflowTaskExecutionSize : dynamicconfig .GetIntPropertyFn (1 ),
244
+ MaxConcurrentActivityTaskPollers : dynamicconfig .GetIntPropertyFn (1 ),
245
+ MaxConcurrentWorkflowTaskPollers : dynamicconfig .GetIntPropertyFn (1 ),
246
+ HistoryScannerEnabled : dynamicconfig .GetBoolPropertyFn (true ),
247
+ ExecutionsScannerEnabled : dynamicconfig .GetBoolPropertyFn (false ),
248
+ TaskQueueScannerEnabled : dynamicconfig .GetBoolPropertyFn (false ),
249
+ Persistence : & config.Persistence {
250
+ DefaultStore : config .StoreTypeNoSQL ,
251
+ DataStores : map [string ]config.DataStore {
252
+ config .StoreTypeNoSQL : {},
253
+ },
254
+ },
255
+ },
256
+ mockSdkClientFactory ,
257
+ metrics .NoopMetricsHandler ,
258
+ p .NewMockExecutionManager (ctrl ),
259
+ p .NewMockTaskManager (ctrl ),
260
+ historyservicemock .NewMockHistoryServiceClient (ctrl ),
261
+ mockAdminClient ,
262
+ mockNamespaceRegistry ,
263
+ mockWorkerFactory ,
264
+ )
265
+ mockSdkClientFactory .EXPECT ().GetSystemClient ().Return (mockSdkClient ).AnyTimes ()
266
+ worker .EXPECT ().RegisterActivityWithOptions (gomock .Any (), gomock .Any ()).AnyTimes ()
267
+ worker .EXPECT ().RegisterWorkflowWithOptions (gomock .Any (), gomock .Any ()).AnyTimes ()
268
+ worker .EXPECT ().Start ()
269
+ mockWorkerFactory .EXPECT ().New (gomock .Any (), gomock .Any (), gomock .Any ()).Return (worker )
270
+ var wg sync.WaitGroup
271
+ wg .Add (1 )
272
+ mockSdkClient .EXPECT ().ExecuteWorkflow (gomock .Any (), gomock .Any (), gomock .Any ()).DoAndReturn (func (
273
+ ctx context.Context ,
274
+ _ client.StartWorkflowOptions ,
275
+ _ string ,
276
+ _ ... interface {},
277
+ ) (client.WorkflowRun , error ) {
278
+ wg .Done ()
279
+ <- ctx .Done ()
280
+ return nil , ctx .Err ()
281
+ })
282
+ err := scanner .Start ()
283
+ s .NoError (err )
284
+ wg .Wait ()
285
+ scanner .Stop ()
286
+ }
0 commit comments