@live-change/framework 0.9.171 → 0.9.174
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 +327 -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/EventHandler.js +7 -1
- package/lib/runtime/TriggerHandler.js +130 -55
- package/lib/utils.js +59 -0
- package/package.json +4 -4
- package/tsconfig.json +3 -2
|
@@ -1,7 +1,24 @@
|
|
|
1
|
-
import KeyBasedExecutionQueues from '../utils/KeyBasedExecutionQueues.js'
|
|
2
|
-
import CommandQueue from '../utils/CommandQueue.js'
|
|
3
|
-
import SingleEmitQueue from '../utils/SingleEmitQueue.js'
|
|
4
|
-
import SplitEmitQueue from '../utils/SplitEmitQueue.js'
|
|
1
|
+
import KeyBasedExecutionQueues from '../utils/KeyBasedExecutionQueues.js'
|
|
2
|
+
import CommandQueue from '../utils/CommandQueue.js'
|
|
3
|
+
import SingleEmitQueue from '../utils/SingleEmitQueue.js'
|
|
4
|
+
import SplitEmitQueue from '../utils/SplitEmitQueue.js'
|
|
5
|
+
|
|
6
|
+
import { context, propagation, trace } from '@opentelemetry/api'
|
|
7
|
+
import { SpanKind } from '@opentelemetry/api'
|
|
8
|
+
const tracer = trace.getTracer('live-change:commandExecutor')
|
|
9
|
+
|
|
10
|
+
import { expandObjectAttributes } from '../utils.js'
|
|
11
|
+
|
|
12
|
+
function spanAttributes(command, service, action) {
|
|
13
|
+
return {
|
|
14
|
+
service: service.name,
|
|
15
|
+
action_name: action.definition.name,
|
|
16
|
+
action_waitForEvents: action.definition.waitForEvents,
|
|
17
|
+
action_timeout: action.definition.timeout,
|
|
18
|
+
action_requestTimeout: action.definition.requestTimeout,
|
|
19
|
+
...expandObjectAttributes(command, 'command'),
|
|
20
|
+
}
|
|
21
|
+
}
|
|
5
22
|
|
|
6
23
|
async function startCommandExecutor(service, config) {
|
|
7
24
|
if(!config.runCommands) return
|
|
@@ -21,71 +38,124 @@ async function startCommandExecutor(service, config) {
|
|
|
21
38
|
Array.isArray(queuedBy) ? (c) => JSON.stringify(queuedBy.map(k => c[k])) :
|
|
22
39
|
(c) => JSON.stringify(c[queuedBy]))
|
|
23
40
|
service.commandQueue.addCommandHandler(actionName, async (command) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
41
|
+
if(command._trace) {
|
|
42
|
+
propagation.extract(context.active(), command._trace)
|
|
43
|
+
}
|
|
44
|
+
return tracer.startActiveSpan('queueCommand:'+command.service+'.'+command.type, {
|
|
45
|
+
root: !command._trace,
|
|
46
|
+
attributes: spanAttributes(command, service, action)
|
|
47
|
+
}, async (queueSpan) => {
|
|
48
|
+
console.log("COMMAND TRACE", command._trace)
|
|
49
|
+
|
|
50
|
+
const queueContext = context.active()
|
|
51
|
+
|
|
52
|
+
const profileOp = await service.profileLog.begin({
|
|
53
|
+
operation: 'queueCommand', commandType: actionName,
|
|
54
|
+
commandId: command.id, client: command.client
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
const routine = () => service.profileLog.profile({
|
|
58
|
+
operation: 'runCommand', commandType: actionName,
|
|
59
|
+
commandId: command.id, client: command.client
|
|
60
|
+
}, async () => {
|
|
61
|
+
return tracer.startActiveSpan('handleCommand:'+command.service+'.'+command.type, {
|
|
62
|
+
kind: SpanKind.INTERNAL,
|
|
63
|
+
attributes: spanAttributes(command, service, action)
|
|
64
|
+
}, queueContext, async (handleSpan) => {
|
|
65
|
+
|
|
66
|
+
const _trace = {}
|
|
67
|
+
propagation.inject(context.active(), _trace)
|
|
68
|
+
const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
|
|
69
|
+
const flags = { commandId: command.id, reportFinished, _trace }
|
|
70
|
+
const emit = (!service.app.splitEvents || this.shortEvents)
|
|
71
|
+
? new SingleEmitQueue(service, flags)
|
|
72
|
+
: new SplitEmitQueue(service, flags)
|
|
73
|
+
|
|
74
|
+
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
75
|
+
action.definition.timeout || 10000,
|
|
76
|
+
async () => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
77
|
+
|
|
78
|
+
return tracer.startActiveSpan('handleEvents', {
|
|
79
|
+
kind: SpanKind.INTERNAL,
|
|
80
|
+
attributes: spanAttributes(command, service, action)
|
|
81
|
+
}, queueContext, async (handleEventsSpan) => {
|
|
82
|
+
|
|
83
|
+
if(service.app.shortEvents) {
|
|
84
|
+
const bucket = {}
|
|
85
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
86
|
+
const handlerService = service.app.startedServices[event.service]
|
|
87
|
+
const handler = handlerService.events[event.type]
|
|
88
|
+
handlerService.exentQueue.queue(() => handler.execute(event, bucket))
|
|
89
|
+
}))
|
|
90
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
91
|
+
} else {
|
|
92
|
+
const events = await emit.commit()
|
|
93
|
+
if (action.definition.waitForEvents)
|
|
94
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
95
|
+
}
|
|
96
|
+
handleEventsSpan.end()
|
|
97
|
+
handleSpan.end()
|
|
98
|
+
queueSpan.end()
|
|
99
|
+
return result
|
|
100
|
+
})
|
|
101
|
+
})
|
|
102
|
+
})
|
|
103
|
+
routine.key = keyFunction(command)
|
|
104
|
+
const promise = service.keyBasedExecutionQueues.queue(routine)
|
|
105
|
+
await service.profileLog.endPromise(profileOp, promise)
|
|
106
|
+
return promise
|
|
27
107
|
})
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
: new SplitEmitQueue(service, flags)
|
|
33
|
-
const routine = () => service.profileLog.profile({
|
|
108
|
+
})
|
|
109
|
+
} else {
|
|
110
|
+
service.commandQueue.addCommandHandler(actionName,
|
|
111
|
+
(command) => service.profileLog.profile({
|
|
34
112
|
operation: 'runCommand', commandType: actionName,
|
|
35
113
|
commandId: command.id, client: command.client
|
|
36
114
|
}, async () => {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
40
|
-
if(service.app.shortEvents) {
|
|
41
|
-
const bucket = {}
|
|
42
|
-
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
43
|
-
const handlerService = service.app.startedServices[event.service]
|
|
44
|
-
const handler = handlerService.events[event.type]
|
|
45
|
-
handlerService.exentQueue.queue(() => handler.execute(event, bucket))
|
|
46
|
-
}))
|
|
47
|
-
if (action.definition.waitForEvents) await eventsPromise
|
|
48
|
-
} else {
|
|
49
|
-
const events = await emit.commit()
|
|
50
|
-
if (action.definition.waitForEvents)
|
|
51
|
-
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
115
|
+
if(command._trace) {
|
|
116
|
+
propagation.extract(context.active(), command._trace)
|
|
52
117
|
}
|
|
53
|
-
return
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
(command) => service.profileLog.profile({
|
|
63
|
-
operation: 'runCommand', commandType: actionName,
|
|
64
|
-
commandId: command.id, client: command.client
|
|
65
|
-
}, async () => {
|
|
118
|
+
return tracer.startActiveSpan('handleCommand:'+command.service+'.'+command.type, {
|
|
119
|
+
kind: SpanKind.INTERNAL,
|
|
120
|
+
attributes: spanAttributes(command, service, action)
|
|
121
|
+
}, async (handleSpan) => {
|
|
122
|
+
|
|
123
|
+
const handleContext = context.active()
|
|
124
|
+
|
|
125
|
+
const _trace = {}
|
|
126
|
+
propagation.inject(context.active(), _trace)
|
|
66
127
|
const reportFinished = action.definition.waitForEvents ? 'command_' + command.id : undefined
|
|
67
|
-
const flags = {commandId: command.id, reportFinished}
|
|
128
|
+
const flags = { commandId: command.id, reportFinished, _trace }
|
|
68
129
|
const emit = (!service.app.splitEvents || this.shortEvents)
|
|
69
130
|
? new SingleEmitQueue(service, flags)
|
|
70
131
|
: new SplitEmitQueue(service, flags)
|
|
71
132
|
const result = await service.app.assertTime('command ' + action.definition.name,
|
|
72
133
|
action.definition.timeout || 10000,
|
|
73
134
|
() => action.runCommand(command, (...args) => emit.emit(...args)), command)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
135
|
+
|
|
136
|
+
return tracer.startActiveSpan('emitEvents', {
|
|
137
|
+
kind: SpanKind.INTERNAL,
|
|
138
|
+
attributes: spanAttributes(command, service, action)
|
|
139
|
+
}, handleContext, async (handleEventsSpan) => {
|
|
140
|
+
if(service.app.shortEvents) {
|
|
141
|
+
const bucket = {}
|
|
142
|
+
const eventsPromise = Promise.all(emit.emittedEvents.map(event => {
|
|
143
|
+
const handlerService = service.app.startedServices[event.service]
|
|
144
|
+
const handler = handlerService.events[event.type]
|
|
145
|
+
handlerService.exentQueue.queue(() => handler.execute(event, bucket))
|
|
146
|
+
}))
|
|
147
|
+
if (action.definition.waitForEvents) await eventsPromise
|
|
148
|
+
} else {
|
|
149
|
+
const events = await emit.commit()
|
|
150
|
+
if (action.definition.waitForEvents)
|
|
151
|
+
await service.app.waitForEvents(reportFinished, events, action.definition.waitForEvents)
|
|
152
|
+
}
|
|
153
|
+
handleEventsSpan.end()
|
|
154
|
+
handleSpan.end()
|
|
155
|
+
return result
|
|
156
|
+
})
|
|
88
157
|
})
|
|
158
|
+
})
|
|
89
159
|
)
|
|
90
160
|
|
|
91
161
|
}
|
|
@@ -3,6 +3,19 @@ import EventSourcing from '../utils/EventSourcing.js'
|
|
|
3
3
|
import Debug from 'debug'
|
|
4
4
|
const debug = Debug("framework:eventListener")
|
|
5
5
|
|
|
6
|
+
import { context, propagation, trace } from '@opentelemetry/api'
|
|
7
|
+
import { SpanKind } from '@opentelemetry/api'
|
|
8
|
+
const tracer = trace.getTracer('live-change:eventListener')
|
|
9
|
+
|
|
10
|
+
import { expandObjectAttributes } from '../utils.js'
|
|
11
|
+
|
|
12
|
+
async function spanAttributes(ev, service) {
|
|
13
|
+
return {
|
|
14
|
+
...expandObjectAttributes(ev, 'event'),
|
|
15
|
+
service: service.name
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
async function startEventListener(service, config) {
|
|
7
20
|
if(!config.handleEvents) return
|
|
8
21
|
|
|
@@ -19,13 +32,25 @@ async function startEventListener(service, config) {
|
|
|
19
32
|
for (let eventName in service.events) {
|
|
20
33
|
const event = service.events[eventName]
|
|
21
34
|
service.eventSourcing.addEventHandler(eventName, async (ev, bucket) => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
)
|
|
35
|
+
if(ev._trace) {
|
|
36
|
+
propagation.extract(context.active(), ev._trace)
|
|
37
|
+
}
|
|
38
|
+
return tracer.startActiveSpan('handleEvent:'+service.name+'.'+eventName, {
|
|
39
|
+
kind: SpanKind.INTERNAL,
|
|
40
|
+
attributes: spanAttributes(ev, service)
|
|
41
|
+
}, async (handleSpan) => {
|
|
42
|
+
try {
|
|
43
|
+
return await service.profileLog.profile({ operation: "handleEvent", eventName, id: ev.id,
|
|
44
|
+
bucketId: bucket.id, triggerId: bucket.triggerId, commandId: bucket.commandId },
|
|
45
|
+
() => {
|
|
46
|
+
debug("EXECUTING EVENT", ev)
|
|
47
|
+
return event.execute(ev, bucket)
|
|
48
|
+
}
|
|
49
|
+
)
|
|
50
|
+
} finally {
|
|
51
|
+
handleSpan.end()
|
|
52
|
+
}
|
|
53
|
+
})
|
|
29
54
|
})
|
|
30
55
|
service.eventSourcing.onBucketEnd = async (bucket, handledEvents) => {
|
|
31
56
|
if(bucket.reportFinished && handledEvents.length > 0) {
|
|
@@ -3,6 +3,19 @@ import CommandQueue from '../utils/CommandQueue.js'
|
|
|
3
3
|
import SingleEmitQueue from '../utils/SingleEmitQueue.js'
|
|
4
4
|
import SplitEmitQueue from '../utils/SplitEmitQueue.js'
|
|
5
5
|
|
|
6
|
+
import { context, propagation, trace } from '@opentelemetry/api'
|
|
7
|
+
import { SpanKind } from '@opentelemetry/api'
|
|
8
|
+
const tracer = trace.getTracer('live-change:triggerExecutor')
|
|
9
|
+
|
|
10
|
+
import { expandObjectAttributes } from '../utils.js'
|
|
11
|
+
|
|
12
|
+
async function spanAttributes(trig, service) {
|
|
13
|
+
return {
|
|
14
|
+
...expandObjectAttributes(trig, 'trigger'),
|
|
15
|
+
service: service.name
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
async function startTriggerExecutor(service, config) {
|
|
7
20
|
if(!config.runCommands) return
|
|
8
21
|
|
|
@@ -27,7 +40,21 @@ async function startTriggerExecutor(service, config) {
|
|
|
27
40
|
await service.dao.request(['database', 'put'], service.databaseName, 'triggerRoutes',
|
|
28
41
|
{ id: triggerName + '=>' + service.name, trigger: triggerName, service: service.name })
|
|
29
42
|
service.triggerQueue.addCommandHandler(triggerName,
|
|
30
|
-
(trig) =>
|
|
43
|
+
async (trig) => {
|
|
44
|
+
if(trig._trace) {
|
|
45
|
+
propagation.extract(context.active(), trig._trace)
|
|
46
|
+
}
|
|
47
|
+
return tracer.startActiveSpan('handleTriggerCall:'+service.name+'.'+triggerName, {
|
|
48
|
+
kind: SpanKind.INTERNAL,
|
|
49
|
+
attributes: spanAttributes(trig, service)
|
|
50
|
+
}, async (triggerSpan) => {
|
|
51
|
+
try {
|
|
52
|
+
return await Promise.all(triggers.map( trigger => trigger.execute(trig, service) ))
|
|
53
|
+
} finally {
|
|
54
|
+
triggerSpan.end()
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
31
58
|
)
|
|
32
59
|
}
|
|
33
60
|
|
package/lib/runtime/Action.js
CHANGED
|
@@ -1,57 +1,86 @@
|
|
|
1
1
|
import { prepareParameters, processReturn, preFilterParameters } from "./params.js"
|
|
2
2
|
|
|
3
|
+
import { context, propagation, trace } from '@opentelemetry/api'
|
|
4
|
+
import { loggingHelpers } from '../utils.js'
|
|
5
|
+
import { SpanKind } from '@opentelemetry/api'
|
|
6
|
+
const tracer = trace.getTracer('live-change:action')
|
|
7
|
+
|
|
8
|
+
import { expandObjectAttributes } from '../utils.js'
|
|
9
|
+
|
|
3
10
|
class Action {
|
|
4
11
|
|
|
5
12
|
constructor(definition, service) {
|
|
6
13
|
this.definition = definition
|
|
7
14
|
this.service = service
|
|
15
|
+
this.loggingHelpers = loggingHelpers(this.service.name, this.service.app.config.clientConfig.version, {
|
|
16
|
+
action: this.definition.name,
|
|
17
|
+
})
|
|
8
18
|
}
|
|
9
19
|
|
|
10
|
-
async runCommand(command, emit) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
command
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
20
|
+
async runCommand(command, emit, traceContext) {
|
|
21
|
+
return tracer.startActiveSpan('runCommand:'+this.service.name+'.'+this.definition.name, {
|
|
22
|
+
kind: SpanKind.SERVER,
|
|
23
|
+
attributes: {
|
|
24
|
+
...expandObjectAttributes(command, 'command'),
|
|
25
|
+
service: this.service.name,
|
|
26
|
+
action_name: this.definition.name,
|
|
27
|
+
action_waitForEvents: this.definition.waitForEvents,
|
|
28
|
+
action_timeout: this.definition.timeout,
|
|
29
|
+
action_requestTimeout: this.definition.requestTimeout,
|
|
30
|
+
}
|
|
31
|
+
}, traceContext, async (runSpan) => {
|
|
32
|
+
|
|
33
|
+
let parameters = command.parameters
|
|
34
|
+
//console.log("PARAMETERS", JSON.stringify(parameters), "DEFN", this.definition.properties)
|
|
35
|
+
let preparedParams = await prepareParameters(parameters, this.definition.properties, this.service)
|
|
36
|
+
//console.log("PREP PARAMS", preparedParams)
|
|
37
|
+
|
|
38
|
+
let resultPromise = (async () => this.definition.execute({
|
|
39
|
+
...parameters,
|
|
40
|
+
...preparedParams
|
|
41
|
+
}, {
|
|
42
|
+
action: this,
|
|
43
|
+
service: this.service,
|
|
33
44
|
client: command.client,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
45
|
+
command,
|
|
46
|
+
trigger: (trigger, data) => this.service.trigger({
|
|
47
|
+
causeType: 'action',
|
|
48
|
+
cause: command.id,
|
|
49
|
+
client: command.client,
|
|
50
|
+
...trigger
|
|
51
|
+
}, data),
|
|
52
|
+
triggerService: (trigger, data, returnArray = false) => this.service.triggerService({
|
|
53
|
+
causeType: 'action',
|
|
54
|
+
cause: command.id,
|
|
55
|
+
client: command.client,
|
|
56
|
+
...trigger
|
|
57
|
+
}, data, returnArray),
|
|
58
|
+
...this.loggingHelpers
|
|
59
|
+
}, emit))()
|
|
37
60
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
61
|
+
resultPromise = resultPromise.then(async result => {
|
|
62
|
+
let processedResult = await processReturn(result, this.definition.returns, this.service)
|
|
63
|
+
runSpan.end()
|
|
64
|
+
return processedResult
|
|
65
|
+
})
|
|
66
|
+
resultPromise.catch(error => {
|
|
67
|
+
this.loggingHelpers.error(`Action ${this.definition.name} error `, error.stack || error)
|
|
68
|
+
runSpan.end()
|
|
69
|
+
})
|
|
70
|
+
return resultPromise
|
|
44
71
|
})
|
|
45
|
-
return resultPromise
|
|
46
72
|
}
|
|
47
73
|
|
|
48
74
|
async callCommand(parameters, clientData) {
|
|
49
75
|
// if(!clientData.roles) throw new Error("no roles") - roles are not required in frontend
|
|
76
|
+
const _trace = {}
|
|
77
|
+
propagation.inject(context.active(), _trace)
|
|
50
78
|
const command = {
|
|
51
79
|
type: this.definition.name,
|
|
52
80
|
service: this.service.name,
|
|
53
81
|
client: clientData,
|
|
54
|
-
parameters: await preFilterParameters(parameters, this.definition.properties)
|
|
82
|
+
parameters: await preFilterParameters(parameters, this.definition.properties),
|
|
83
|
+
_trace
|
|
55
84
|
}
|
|
56
85
|
if(parameters._commandId) command.id = parameters._commandId
|
|
57
86
|
//console.log("CALL COMMAND", JSON.stringify(command, null, " "))
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { prepareParameters, processReturn } from "./params.js"
|
|
2
|
+
import { loggingHelpers } from '../utils.js'
|
|
2
3
|
|
|
3
4
|
class EventHandler {
|
|
4
5
|
|
|
5
6
|
constructor(definition, service) {
|
|
6
7
|
this.definition = definition
|
|
7
8
|
this.service = service
|
|
9
|
+
|
|
10
|
+
this.loggingHelpers = loggingHelpers(service.name, service.app.config.clientConfig.version, {
|
|
11
|
+
eventType: definition.name,
|
|
12
|
+
})
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
async execute(parameters, bucket) {
|
|
@@ -17,7 +22,8 @@ class EventHandler {
|
|
|
17
22
|
}, {
|
|
18
23
|
action: this,
|
|
19
24
|
service: this.service,
|
|
20
|
-
bucket: bucket
|
|
25
|
+
bucket: bucket,
|
|
26
|
+
...this.loggingHelpers
|
|
21
27
|
})
|
|
22
28
|
|
|
23
29
|
resultPromise = resultPromise.then(async result => {
|