@live-change/framework 0.9.171 → 0.9.173
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/App.js +328 -239
- package/lib/processes/commandExecutor.js +126 -56
- package/lib/processes/eventListener.js +32 -7
- package/lib/processes/triggerExecutor.js +28 -1
- package/lib/runtime/Action.js +63 -34
- package/lib/runtime/TriggerHandler.js +130 -55
- package/lib/utils.js +55 -0
- package/package.json +4 -4
- package/tsconfig.json +3 -2
- package/LICENSE.md +0 -11
package/lib/App.js
CHANGED
|
@@ -37,9 +37,12 @@ import Debug from 'debug'
|
|
|
37
37
|
|
|
38
38
|
const debug = Debug('framework')
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
import { expandObjectAttributes } from './utils.js'
|
|
41
41
|
import * as utils from './utils.js'
|
|
42
42
|
import * as validation from './utils/validation.js'
|
|
43
|
+
import { trace, SpanKind, context, propagation } from '@opentelemetry/api'
|
|
44
|
+
|
|
45
|
+
|
|
43
46
|
|
|
44
47
|
class App {
|
|
45
48
|
|
|
@@ -89,6 +92,8 @@ class App {
|
|
|
89
92
|
this.triggerRoutes = {}
|
|
90
93
|
this.globalViews = {}
|
|
91
94
|
|
|
95
|
+
this.loggingHelpers = utils.loggingHelpers('live-change/app', '')
|
|
96
|
+
this.tracer = trace.getTracer('live-change/app')
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
static app() {
|
|
@@ -252,205 +257,264 @@ class App {
|
|
|
252
257
|
}
|
|
253
258
|
|
|
254
259
|
async trigger(trigger, data) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
260
|
+
return this.tracer.startActiveSpan('callTrigger:'+trigger.type, {
|
|
261
|
+
kind: SpanKind.INTERNAL,
|
|
262
|
+
attributes: {
|
|
263
|
+
...expandObjectAttributes(trigger, 'trigger'),
|
|
264
|
+
...expandObjectAttributes(data, 'data'),
|
|
265
|
+
}
|
|
266
|
+
}, async (triggerSpan) => {
|
|
267
|
+
try {
|
|
268
|
+
if(!trigger) throw new Error("trigger must have type")
|
|
269
|
+
if(typeof trigger !== 'object') throw new Error("trigger must be object")
|
|
270
|
+
if(typeof trigger.type !== 'string') throw new Error("trigger type must be string")
|
|
271
|
+
if(!data) throw new Error("trigger must have data")
|
|
272
|
+
if(typeof data !== 'object') throw new Error("trigger must be object")
|
|
273
|
+
if(trigger.service) return await this.triggerService(trigger, data, true)
|
|
274
|
+
if(this.shortTriggers) {
|
|
275
|
+
const triggers = this.triggerRoutes[trigger.type] /// TODO: check if it is right
|
|
276
|
+
return await Promise.all(triggers.map(t => t.execute(data)))
|
|
277
|
+
}
|
|
278
|
+
const profileOp = await this.profileLog.begin({
|
|
279
|
+
operation: "callTrigger", triggerType: trigger.type, id: data.id, by: data.by
|
|
280
|
+
})
|
|
281
|
+
const routes = await this.dao.get(['database', 'tableRange', this.databaseName, 'triggerRoutes',
|
|
282
|
+
{ gte: trigger.type+'=', lte: trigger.type+'=\xFF\xFF\xFF\xFF' }])
|
|
283
|
+
this.loggingHelpers.log("TRIGGER ROUTES", trigger.type, '=>', routes.map(r => r.service).join(', '))
|
|
284
|
+
let promises = []
|
|
285
|
+
for(const route of routes) {
|
|
286
|
+
promises.push(this.triggerService({ ...trigger, service: route.service }, { ...data }, true))
|
|
287
|
+
}
|
|
288
|
+
const promise = Promise.all(promises)
|
|
289
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
290
|
+
const result = (await promise).flat()
|
|
291
|
+
this.loggingHelpers.log("TRIGGER FINISHED!", result)
|
|
292
|
+
return result
|
|
293
|
+
} finally {
|
|
294
|
+
triggerSpan.end()
|
|
295
|
+
}
|
|
267
296
|
})
|
|
268
|
-
const routes = await this.dao.get(['database', 'tableRange', this.databaseName, 'triggerRoutes',
|
|
269
|
-
{ gte: trigger.type+'=', lte: trigger.type+'=\xFF\xFF\xFF\xFF' }])
|
|
270
|
-
console.log("TRIGGER ROUTES", trigger.type, '=>', routes.map(r => r.service).join(', '))
|
|
271
|
-
let promises = []
|
|
272
|
-
for(const route of routes) {
|
|
273
|
-
promises.push(this.triggerService({ ...trigger, service: route.service }, { ...data }, true))
|
|
274
|
-
}
|
|
275
|
-
const promise = Promise.all(promises)
|
|
276
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
277
|
-
const result = (await promise).flat()
|
|
278
|
-
console.log("TRIGGER FINISHED!", result)
|
|
279
|
-
return result
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
async triggerService(trigger, data, returnArray = false) {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
300
|
+
return this.tracer.startActiveSpan('callTriggerService:'+trigger.service+'.'+trigger.type, {
|
|
301
|
+
kind: SpanKind.INTERNAL,
|
|
302
|
+
attributes: {
|
|
303
|
+
...expandObjectAttributes(trigger, 'trigger'),
|
|
304
|
+
...expandObjectAttributes(data, 'data'),
|
|
305
|
+
service: trigger.service
|
|
306
|
+
}
|
|
307
|
+
}, async (triggerSpan) => {
|
|
308
|
+
try {
|
|
309
|
+
if(!trigger.service) throw new Error("trigger must have service")
|
|
310
|
+
if(typeof trigger !== 'object') throw new Error("trigger must be object")
|
|
311
|
+
if(typeof trigger.service !== 'string') throw new Error("trigger service must be string")
|
|
312
|
+
if(typeof trigger.type !== 'string') throw new Error("trigger type must be string")
|
|
313
|
+
trigger.data = data
|
|
314
|
+
if(!trigger.data) throw new Error("trigger must have data")
|
|
315
|
+
if(typeof trigger.data !== 'object') throw new Error("trigger must be object")
|
|
316
|
+
if(this.shortTriggers) {
|
|
317
|
+
const service = this.startedServices[trigger.service]
|
|
318
|
+
const triggers = service.triggers[trigger.type]
|
|
319
|
+
if(!triggers) return []
|
|
320
|
+
const result = await Promise.all(triggers.map(t => t.execute(data)))
|
|
321
|
+
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
322
|
+
return result
|
|
323
|
+
}
|
|
324
|
+
if(!trigger.id) trigger.id = this.generateUid()
|
|
325
|
+
trigger.state = 'new'
|
|
326
|
+
if(!trigger.timestamp) trigger.timestamp = (new Date()).toISOString()
|
|
301
327
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
328
|
+
const profileOp = await this.profileLog.begin({
|
|
329
|
+
operation: "callTriggerService", triggerType: trigger.type, service: trigger.service, triggerId: trigger.id, by: data.by
|
|
330
|
+
})
|
|
305
331
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
332
|
+
const triggersTable = this.splitCommands ? `${this.name}_triggers` : 'triggers'
|
|
333
|
+
const objectObservable = this.dao.observable(
|
|
334
|
+
['database', 'tableObject', this.databaseName, triggersTable, trigger.id],
|
|
335
|
+
ReactiveDao.ObservableValue
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
propagation.inject(context.active(), trigger._trace)
|
|
339
|
+
|
|
340
|
+
await this.dao.request(['database', 'update', this.databaseName, triggersTable, trigger.id, [{
|
|
341
|
+
op: 'conditional',
|
|
342
|
+
conditions: [{ test: 'notExist', property: 'type' }],
|
|
343
|
+
operations: [{ op: 'reverseMerge', value: trigger }],
|
|
344
|
+
}]])
|
|
345
|
+
let observer
|
|
346
|
+
const promise = new Promise((resolve, reject) => {
|
|
347
|
+
observer = (signal, value) => {
|
|
348
|
+
if(signal !== 'set') return reject('unknownSignal')
|
|
349
|
+
if(!value) return
|
|
350
|
+
if(value.state === 'done') return resolve(value.result)
|
|
351
|
+
if(value.state === 'failed') return reject(value.error)
|
|
352
|
+
}
|
|
353
|
+
objectObservable.observe(observer)
|
|
354
|
+
}).finally(() => {
|
|
355
|
+
objectObservable.unobserve(observer)
|
|
356
|
+
})
|
|
357
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
358
|
+
|
|
359
|
+
const result = await promise
|
|
360
|
+
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
361
|
+
return result
|
|
362
|
+
} finally {
|
|
363
|
+
triggerSpan.end()
|
|
323
364
|
}
|
|
324
|
-
objectObservable.observe(observer)
|
|
325
|
-
}).finally(() => {
|
|
326
|
-
objectObservable.unobserve(observer)
|
|
327
365
|
})
|
|
328
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
329
|
-
|
|
330
|
-
const result = await promise
|
|
331
|
-
if(!returnArray && Array.isArray(result) && result.length === 1) return result[0]
|
|
332
|
-
return result
|
|
333
366
|
}
|
|
334
|
-
|
|
335
367
|
async command(data, requestTimeout) {
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
})
|
|
375
|
-
|
|
368
|
+
return this.tracer.startActiveSpan('callCommand:'+data.service+'.'+data.type, {
|
|
369
|
+
kind: SpanKind.INTERNAL,
|
|
370
|
+
attributes: {
|
|
371
|
+
requestTimeout: requestTimeout,
|
|
372
|
+
...expandObjectAttributes(data, 'command'),
|
|
373
|
+
}
|
|
374
|
+
}, async (commandSpan) => {
|
|
375
|
+
const testTrace = {}
|
|
376
|
+
propagation.inject(context.active(), testTrace)
|
|
377
|
+
console.log("CALL COMMAND TRACE - INJECTED TRACE", testTrace)
|
|
378
|
+
try {
|
|
379
|
+
|
|
380
|
+
if(!data.id) data.id = this.generateUid()
|
|
381
|
+
if(!data.service) throw new Error("command must have service")
|
|
382
|
+
if(!data.type) throw new Error("command must have type")
|
|
383
|
+
if(!data.timestamp) data.timestamp = (new Date()).toISOString()
|
|
384
|
+
data.state = 'new'
|
|
385
|
+
|
|
386
|
+
if(this.shortCommands) {
|
|
387
|
+
const command = data
|
|
388
|
+
const service = this.startedServices[data.service]
|
|
389
|
+
const action = service.actions[data.type]
|
|
390
|
+
const queuedBy = action.definition.queuedBy
|
|
391
|
+
if(queuedBy) {
|
|
392
|
+
const profileOp = await service.profileLog.begin({
|
|
393
|
+
operation: 'queueCommand', commandType: actionName,
|
|
394
|
+
commandId: command.id, client: command.client
|
|
395
|
+
})
|
|
396
|
+
const keyFunction = typeof queuedBy == 'function' ? queuedBy : (
|
|
397
|
+
Array.isArray(queuedBy) ? (c) => JSON.stringify(queuedBy.map(k => c[k])) :
|
|
398
|
+
(c) => JSON.stringify(c[queuedBy]))
|
|
399
|
+
|
|
400
|
+
const _trace = {}
|
|
401
|
+
propagation.inject(context.active(), _trace)
|
|
402
|
+
|
|
403
|
+
const routine = () => service.profileLog.profile({
|
|
404
|
+
operation: 'runCommand', commandType: actionName,
|
|
405
|
+
commandId: command.id, client: command.client
|
|
406
|
+
}, async () => {
|
|
407
|
+
propagation.extract(context.active(), _trace)
|
|
408
|
+
const reportFinished = action.definition.waitForEvents ? command.id : undefined
|
|
409
|
+
propagation.inject(context.active(), _trace)
|
|
410
|
+
const flags = {commandId: command.id, reportFinished, _trace}
|
|
411
|
+
const emit = (!this.splitEvents || this.shortEvents)
|
|
412
|
+
? new SingleEmitQueue(service, flags)
|
|
413
|
+
: new SplitEmitQueue(service, flags)
|
|
414
|
+
|
|
415
|
+
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
416
|
+
action.definition.timeout || 10000,
|
|
417
|
+
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
418
|
+
if(this.shortEvents) {
|
|
419
|
+
const bucket = {}
|
|
420
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
421
|
+
const service = this.startedServices[event.service]
|
|
422
|
+
const handler = service.events[event.type]
|
|
423
|
+
service.exentQueue.queue(() => handler.execute(event, bucket))
|
|
424
|
+
}))
|
|
425
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
426
|
+
} else {
|
|
427
|
+
const events = await emit.commit()
|
|
428
|
+
if (action.definition.waitForEvents)
|
|
429
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
430
|
+
}
|
|
431
|
+
return result
|
|
432
|
+
})
|
|
433
|
+
routine.key = keyFunction(command)
|
|
434
|
+
const promise = service.keyBasedExecutionQueues.queue(routine)
|
|
435
|
+
await service.profileLog.endPromise(profileOp, promise)
|
|
436
|
+
return promise
|
|
376
437
|
} else {
|
|
377
|
-
const
|
|
378
|
-
|
|
379
|
-
|
|
438
|
+
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
439
|
+
action.definition.timeout || 10000,
|
|
440
|
+
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
441
|
+
if(this.shortEvents) {
|
|
442
|
+
const bucket = {}
|
|
443
|
+
console.log("emit", emit)
|
|
444
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
445
|
+
const service = this.startedServices[event.service]
|
|
446
|
+
const handler = service.events[event.type]
|
|
447
|
+
service.exentQueue.queue(() => handler.execute(event, bucket))
|
|
448
|
+
}))
|
|
449
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
450
|
+
} else {
|
|
451
|
+
const events = await emit.commit()
|
|
452
|
+
if (action.definition.waitForEvents)
|
|
453
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
454
|
+
}
|
|
455
|
+
return result
|
|
380
456
|
}
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
457
|
+
} else { // queue and observe command execution
|
|
458
|
+
const profileOp = await this.profileLog.begin({
|
|
459
|
+
operation: "callCommand", commandType: data.type, service: data.service,
|
|
460
|
+
commandId: data.id, by: data.by, client: data.client
|
|
461
|
+
})
|
|
462
|
+
|
|
463
|
+
const commandsTable = this.splitCommands ? `${data.service}_commands` : 'commands'
|
|
464
|
+
const objectObservable = this.dao.observable(
|
|
465
|
+
['database', 'tableObject', this.databaseName, commandsTable, data.id],
|
|
466
|
+
ReactiveDao.ObservableValue
|
|
467
|
+
)
|
|
468
|
+
|
|
469
|
+
const _trace = {}
|
|
470
|
+
propagation.inject(context.active(), _trace)
|
|
471
|
+
data._trace = _trace
|
|
472
|
+
console.log("CALL COMMAND TRACE - SAVING TRACE", _trace)
|
|
473
|
+
|
|
474
|
+
await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [{
|
|
475
|
+
op: 'conditional',
|
|
476
|
+
conditions: [{ test: 'notExist', property: 'type' }],
|
|
477
|
+
operations: [{ op: 'reverseMerge', value: data }],
|
|
478
|
+
}]])
|
|
479
|
+
let observer
|
|
480
|
+
const promise = new Promise((resolve, reject) => {
|
|
481
|
+
observer = (signal, value) => {
|
|
482
|
+
if (signal !== 'set') return reject('unknownSignal')
|
|
483
|
+
if (!value) return
|
|
484
|
+
if (value.state === 'done') return resolve(value.result)
|
|
485
|
+
if (value.state === 'failed') return reject(value.error)
|
|
486
|
+
}
|
|
487
|
+
objectObservable.observe(observer)
|
|
488
|
+
if (!requestTimeout) {
|
|
489
|
+
requestTimeout = this.requestTimeout
|
|
490
|
+
}
|
|
491
|
+
if (requestTimeout) {
|
|
492
|
+
const timeout = setTimeout(() => {
|
|
493
|
+
this.activeTimeouts.delete(timeout)
|
|
494
|
+
reject('timeout')
|
|
495
|
+
}, requestTimeout)
|
|
496
|
+
this.activeTimeouts.add(timeout)
|
|
497
|
+
}
|
|
498
|
+
}).finally(() => {
|
|
499
|
+
objectObservable.unobserve(observer)
|
|
500
|
+
})
|
|
412
501
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
['database', 'tableObject', this.databaseName, commandsTable, data.id],
|
|
416
|
-
ReactiveDao.ObservableValue
|
|
417
|
-
)
|
|
418
|
-
await this.dao.request(['database', 'update', this.databaseName, commandsTable, data.id, [{
|
|
419
|
-
op: 'conditional',
|
|
420
|
-
conditions: [{ test: 'notExist', property: 'type' }],
|
|
421
|
-
operations: [{ op: 'reverseMerge', value: data }],
|
|
422
|
-
}]])
|
|
423
|
-
let observer
|
|
424
|
-
const promise = new Promise((resolve, reject) => {
|
|
425
|
-
observer = (signal, value) => {
|
|
426
|
-
if (signal !== 'set') return reject('unknownSignal')
|
|
427
|
-
if (!value) return
|
|
428
|
-
if (value.state === 'done') return resolve(value.result)
|
|
429
|
-
if (value.state === 'failed') return reject(value.error)
|
|
430
|
-
}
|
|
431
|
-
objectObservable.observe(observer)
|
|
432
|
-
if (!requestTimeout) {
|
|
433
|
-
requestTimeout = this.requestTimeout
|
|
502
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
503
|
+
return promise
|
|
434
504
|
}
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
}, requestTimeout)
|
|
440
|
-
this.activeTimeouts.add(timeout)
|
|
441
|
-
}
|
|
442
|
-
}).finally(() => {
|
|
443
|
-
objectObservable.unobserve(observer)
|
|
444
|
-
})
|
|
445
|
-
|
|
446
|
-
await this.profileLog.endPromise(profileOp, promise)
|
|
447
|
-
return promise
|
|
448
|
-
}
|
|
505
|
+
} finally {
|
|
506
|
+
commandSpan.end()
|
|
507
|
+
}
|
|
508
|
+
})
|
|
449
509
|
}
|
|
450
510
|
|
|
451
511
|
async emitEvents(service, events, flags = {}) {
|
|
452
512
|
for(let event of events) {
|
|
453
513
|
if(!event.service) event.service = service
|
|
514
|
+
if(!event._trace) {
|
|
515
|
+
event._trace = {}
|
|
516
|
+
propagation.inject(context.active(), event._trace)
|
|
517
|
+
}
|
|
454
518
|
}
|
|
455
519
|
if(this.splitEvents) {
|
|
456
520
|
let promises = []
|
|
@@ -476,73 +540,97 @@ class App {
|
|
|
476
540
|
|
|
477
541
|
async waitForEvents(reportId, events, timeout) {
|
|
478
542
|
if(events.length === 0) {
|
|
479
|
-
|
|
543
|
+
this.loggingHelpers.log("no events, no need to wait", reportId)
|
|
480
544
|
return
|
|
481
545
|
}
|
|
482
546
|
const [action, id] = reportId.split('_')
|
|
483
547
|
const commandId = id
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const eventsNotDone = events.filter(event => finished.find(e => e.id === event.id))
|
|
493
|
-
if(eventsNotDone.length > 0) {
|
|
494
|
-
console.error(" pending events:")
|
|
495
|
-
for(const event of eventsNotDone) {
|
|
496
|
-
console.error(` ${event.id} - type: ${event.type}`)
|
|
497
|
-
}
|
|
498
|
-
}
|
|
499
|
-
reject(message)
|
|
500
|
-
done = true
|
|
548
|
+
|
|
549
|
+
return this.tracer.startActiveSpan('waitForEvents', {
|
|
550
|
+
kind: SpanKind.INTERNAL,
|
|
551
|
+
attributes: {
|
|
552
|
+
reportId: reportId,
|
|
553
|
+
eventTypes: events.map(e => e.type),
|
|
554
|
+
eventIds: events.map(e => e.id),
|
|
555
|
+
timeout: timeout
|
|
501
556
|
}
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
557
|
+
}, async (waitForEventsSpan) => {
|
|
558
|
+
const profileOp = await this.profileLog.begin({
|
|
559
|
+
operation: "waitForEvents", action: action, commandId, reportId, events, timeout
|
|
560
|
+
})
|
|
561
|
+
const _trace = {}
|
|
562
|
+
propagation.inject(context.active(), _trace)
|
|
563
|
+
const promise = new Promise((resolve, reject) => {
|
|
564
|
+
propagation.extract(context.active(), _trace)
|
|
565
|
+
let done = false
|
|
566
|
+
let finishedEvents = []
|
|
567
|
+
const handleError = (message) => {
|
|
568
|
+
let errorMessage = `waitForEvents error: ${message}`
|
|
569
|
+
const eventsNotDone = events.filter(event => finishedEvents.find(e => e.id === event.id))
|
|
570
|
+
if(eventsNotDone.length > 0) {
|
|
571
|
+
errorMessage += "\n pending events:"
|
|
572
|
+
for(const event of eventsNotDone) {
|
|
573
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
this.loggingHelpers.error(errorMessage)
|
|
577
|
+
reject(message)
|
|
578
|
+
done = true
|
|
508
579
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
580
|
+
const observable = this.dao.observable(
|
|
581
|
+
['database', 'tableObject', this.databaseName, 'eventReports', reportId]
|
|
582
|
+
)
|
|
583
|
+
const reportsObserver = (signal, data) => {
|
|
584
|
+
if(signal !== 'set') {
|
|
585
|
+
handleError(`unknown signal ${signal} with data: ${data}`)
|
|
586
|
+
}
|
|
587
|
+
if(data == null) return /// wait for real data
|
|
588
|
+
if(data.finished) {
|
|
589
|
+
finishedEvents = data.finished
|
|
590
|
+
if(finishedEvents.length >= events.length) {
|
|
591
|
+
const eventsNotDone = events.filter(event => data.finished.find(e => e.id === event.id))
|
|
592
|
+
if(eventsNotDone.length !== 0) {
|
|
593
|
+
const eventsDone = events.filter(event => !data.finished.find(e => e.id === event.id))
|
|
594
|
+
let errorMessage = "waitForEvents - finished events does not match!"
|
|
595
|
+
errorMessage += "\n finished events:"
|
|
596
|
+
for(const event of eventsDone) {
|
|
597
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
598
|
+
}
|
|
599
|
+
errorMessage += "\n pending events:"
|
|
600
|
+
for(const event of eventsNotDone) {
|
|
601
|
+
errorMessage += `\n ${event.id} - type: ${event.type}`
|
|
602
|
+
}
|
|
603
|
+
this.loggingHelpers.error(errorMessage)
|
|
604
|
+
} else {
|
|
605
|
+
this.loggingHelpers.log("waiting for events finished", reportId)
|
|
606
|
+
resolve('finished')
|
|
607
|
+
observable.unobserve(reportsObserver)
|
|
524
608
|
}
|
|
525
|
-
} else {
|
|
526
|
-
console.log("waiting for events finished", reportId)
|
|
527
|
-
resolve('finished')
|
|
528
|
-
observable.unobserve(reportsObserver)
|
|
529
609
|
}
|
|
530
610
|
}
|
|
531
611
|
}
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
}
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
612
|
+
this.loggingHelpers.log("waiting for events", reportId)
|
|
613
|
+
observable.observe(reportsObserver)
|
|
614
|
+
if(Number.isFinite(timeout)) {
|
|
615
|
+
setTimeout(() => {
|
|
616
|
+
if(done) return
|
|
617
|
+
observable.unobserve(reportsObserver)
|
|
618
|
+
this.loggingHelpers.error("events timeout", reportId)
|
|
619
|
+
handleError('timeout')
|
|
620
|
+
}, timeout)
|
|
621
|
+
}
|
|
622
|
+
})
|
|
623
|
+
await this.profileLog.endPromise(profileOp, promise)
|
|
624
|
+
promise.then(() => {
|
|
625
|
+
waitForEventsSpan.end()
|
|
626
|
+
})
|
|
627
|
+
promise.catch((error) => {
|
|
628
|
+
this.loggingHelpers.error("waitForEvents error", error)
|
|
629
|
+
waitForEventsSpan.end()
|
|
630
|
+
throw error
|
|
631
|
+
})
|
|
632
|
+
return promise
|
|
633
|
+
})
|
|
546
634
|
}
|
|
547
635
|
|
|
548
636
|
async emitEventsAndWait(service, events, flags = {}) {
|
|
@@ -554,7 +642,8 @@ class App {
|
|
|
554
642
|
async assertTime(taskName, duration, task, ...data) {
|
|
555
643
|
const profileOp = await this.profileLog.begin({ operation: 'assertTime', taskName })
|
|
556
644
|
const taskTimeout = setTimeout(() => {
|
|
557
|
-
|
|
645
|
+
this.loggingHelpers.error(`TASK ${taskName} TIMEOUT`, ...data)
|
|
646
|
+
//console.log(`TASK ${taskName} TIMEOUT`, ...data)
|
|
558
647
|
this.profileLog.end({ ...profileOp, result: "timeout" })
|
|
559
648
|
}, duration)
|
|
560
649
|
try {
|