@sentio/runtime 2.59.0-rc.4 → 2.59.0-rc.41
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/{chunk-ZUTD563J.js → chunk-3SLKMZUX.js} +1020 -34
- package/lib/chunk-3SLKMZUX.js.map +1 -0
- package/lib/{chunk-BPGFX5S5.js → chunk-FDZMT76B.js} +2 -2
- package/lib/{chunk-QELD44EL.js → chunk-OGXSRUVD.js} +4385 -4320
- package/lib/{chunk-QELD44EL.js.map → chunk-OGXSRUVD.js.map} +1 -1
- package/lib/index.d.ts +631 -17
- package/lib/index.js +4 -2
- package/lib/index.js.map +1 -1
- package/lib/processor-runner.js +102 -94
- package/lib/processor-runner.js.map +1 -1
- package/lib/service-worker.d.ts +5 -6
- package/lib/service-worker.js +32 -43
- package/lib/service-worker.js.map +1 -1
- package/lib/test-processor.test.js.map +1 -1
- package/package.json +1 -1
- package/src/db-context.ts +2 -4
- package/src/full-service.ts +59 -13
- package/src/gen/processor/protos/processor.ts +1099 -125
- package/src/gen/service/common/protos/common.ts +304 -1
- package/src/index.ts +7 -0
- package/src/metrics.ts +8 -4
- package/src/plugin.ts +24 -0
- package/src/processor-runner.ts +10 -3
- package/src/service-manager.ts +56 -106
- package/src/service-worker.ts +33 -48
- package/src/service.ts +89 -46
- package/src/utils.ts +20 -4
- package/lib/chunk-ZUTD563J.js.map +0 -1
- /package/lib/{chunk-BPGFX5S5.js.map → chunk-FDZMT76B.js.map} +0 -0
package/src/service-manager.ts
CHANGED
@@ -1,30 +1,21 @@
|
|
1
1
|
import { CallContext } from 'nice-grpc'
|
2
2
|
import { Piscina } from 'piscina'
|
3
3
|
import {
|
4
|
-
DataBinding,
|
5
|
-
DBRequest,
|
6
|
-
DBResponse,
|
7
4
|
DeepPartial,
|
8
5
|
Empty,
|
9
|
-
HandlerType,
|
10
6
|
ProcessConfigRequest,
|
11
7
|
ProcessConfigResponse,
|
12
8
|
ProcessResult,
|
13
9
|
ProcessStreamRequest,
|
14
10
|
ProcessStreamResponse,
|
11
|
+
ProcessStreamResponse_Partitions,
|
15
12
|
StartRequest
|
16
13
|
} from '@sentio/protos'
|
17
|
-
|
18
|
-
import { IStoreContext } from './db-context.js'
|
19
14
|
import { Subject } from 'rxjs'
|
20
15
|
|
21
|
-
import { processMetrics } from './metrics.js'
|
22
16
|
import { MessageChannel } from 'node:worker_threads'
|
23
17
|
import { ProcessorServiceImpl } from './service.js'
|
24
18
|
import { TemplateInstanceState } from './state.js'
|
25
|
-
|
26
|
-
const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
|
27
|
-
|
28
19
|
;(BigInt.prototype as any).toJSON = function () {
|
29
20
|
return this.toString()
|
30
21
|
}
|
@@ -34,11 +25,11 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
34
25
|
private workerData: any = {}
|
35
26
|
|
36
27
|
constructor(
|
37
|
-
readonly options: any,
|
38
28
|
loader: () => Promise<any>,
|
29
|
+
readonly options: any,
|
39
30
|
shutdownHandler?: () => void
|
40
31
|
) {
|
41
|
-
super(loader, shutdownHandler)
|
32
|
+
super(loader, options, shutdownHandler)
|
42
33
|
this.workerData.options = options
|
43
34
|
}
|
44
35
|
|
@@ -70,76 +61,56 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
70
61
|
return await super.stop(request, context)
|
71
62
|
}
|
72
63
|
|
73
|
-
async process(request: DataBinding, dbContext?: ChannelStoreContext): Promise<ProcessResult> {
|
74
|
-
if (!this.pool) {
|
75
|
-
await this.initPool()
|
76
|
-
}
|
77
|
-
|
78
|
-
return this.pool.run(
|
79
|
-
{ request, workerPort: dbContext?.workerPort },
|
80
|
-
{ transferList: dbContext?.workerPort ? [dbContext?.workerPort] : [] }
|
81
|
-
)
|
82
|
-
}
|
83
|
-
|
84
64
|
private readonly contexts = new Contexts()
|
85
65
|
|
86
66
|
protected async handleRequests(
|
87
67
|
requests: AsyncIterable<ProcessStreamRequest>,
|
88
68
|
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
89
69
|
) {
|
70
|
+
if (!this.pool) {
|
71
|
+
await this.initPool()
|
72
|
+
}
|
90
73
|
for await (const request of requests) {
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
}
|
105
|
-
|
106
|
-
const binding = request.binding
|
107
|
-
|
108
|
-
const dbContext = this.contexts.new(request.processId, subject)
|
109
|
-
|
110
|
-
const start = Date.now()
|
111
|
-
this.process(binding, dbContext)
|
112
|
-
.then(async (result) => {
|
113
|
-
subject.next({
|
114
|
-
result,
|
115
|
-
processId: request.processId
|
116
|
-
})
|
117
|
-
})
|
118
|
-
.catch((e) => {
|
119
|
-
dbContext.error(request.processId, e)
|
120
|
-
process_binding_error.add(1)
|
121
|
-
})
|
122
|
-
.finally(() => {
|
123
|
-
const cost = Date.now() - start
|
124
|
-
process_binding_time.add(cost)
|
125
|
-
this.contexts.delete(request.processId)
|
126
|
-
})
|
127
|
-
}
|
128
|
-
if (request.dbResult) {
|
129
|
-
const dbContext = this.contexts.get(request.processId)
|
130
|
-
try {
|
131
|
-
dbContext?.result(request.dbResult)
|
132
|
-
} catch (e) {
|
133
|
-
subject.error(new Error('db result error, process should stop'))
|
134
|
-
}
|
74
|
+
this.handleSingleRequest(request, subject)
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
async handleSingleRequest(request: ProcessStreamRequest, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
|
79
|
+
const processId = request.processId
|
80
|
+
if (request.binding) {
|
81
|
+
const context = this.contexts.new(processId)
|
82
|
+
context.mainPort.on('message', (resp: ProcessStreamResponse) => {
|
83
|
+
subject.next(resp)
|
84
|
+
if (resp.result) {
|
85
|
+
// last response
|
86
|
+
this.contexts.delete(processId)
|
135
87
|
}
|
136
|
-
}
|
137
|
-
|
138
|
-
|
88
|
+
})
|
89
|
+
await this.pool.run(
|
90
|
+
{ request, workerPort: context.workerPort, processId },
|
91
|
+
{ transferList: [context.workerPort] }
|
92
|
+
)
|
93
|
+
} else {
|
94
|
+
const context = this.contexts.get(processId)
|
95
|
+
if (!context) {
|
96
|
+
console.error('No context found for processId:', processId)
|
97
|
+
throw new Error(`No context found for processId: ${processId}`)
|
139
98
|
}
|
99
|
+
context.sendRequest(request)
|
140
100
|
}
|
141
101
|
}
|
142
102
|
|
103
|
+
async process(processId: number, context: ChannelContext): Promise<ProcessResult | ProcessStreamResponse_Partitions> {
|
104
|
+
if (!this.pool) {
|
105
|
+
await this.initPool()
|
106
|
+
}
|
107
|
+
|
108
|
+
return this.pool.run(
|
109
|
+
{ workerPort: context?.workerPort, processId },
|
110
|
+
{ transferList: context?.workerPort ? [context?.workerPort] : [] }
|
111
|
+
)
|
112
|
+
}
|
113
|
+
|
143
114
|
private async initPool() {
|
144
115
|
if (this.pool) {
|
145
116
|
await this.pool.close()
|
@@ -161,17 +132,19 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
161
132
|
}
|
162
133
|
}
|
163
134
|
|
164
|
-
export type WorkerMessage = DBRequest & { processId: number }
|
165
|
-
|
166
135
|
class Contexts {
|
167
|
-
private contexts: Map<number,
|
136
|
+
private contexts: Map<number, ChannelContext> = new Map()
|
168
137
|
|
169
138
|
get(processId: number) {
|
170
139
|
return this.contexts.get(processId)
|
171
140
|
}
|
172
141
|
|
173
|
-
new(processId: number
|
174
|
-
|
142
|
+
new(processId: number) {
|
143
|
+
let context = this.get(processId)
|
144
|
+
if (context) {
|
145
|
+
return context
|
146
|
+
}
|
147
|
+
context = new ChannelContext(processId)
|
175
148
|
this.contexts.set(processId, context)
|
176
149
|
return context
|
177
150
|
}
|
@@ -181,25 +154,19 @@ class Contexts {
|
|
181
154
|
context?.close()
|
182
155
|
this.contexts.delete(processId)
|
183
156
|
}
|
157
|
+
|
158
|
+
has(processId: number) {
|
159
|
+
return this.contexts.has(processId)
|
160
|
+
}
|
184
161
|
}
|
185
162
|
|
186
|
-
export class
|
163
|
+
export class ChannelContext {
|
187
164
|
channel = new MessageChannel()
|
188
165
|
|
189
|
-
constructor(
|
190
|
-
readonly subject: Subject<DeepPartial<ProcessStreamResponse>>,
|
191
|
-
readonly processId: number
|
192
|
-
) {
|
193
|
-
this.mainPort.on('message', (req: ProcessStreamRequest) => {
|
194
|
-
subject.next({
|
195
|
-
...req,
|
196
|
-
processId: processId
|
197
|
-
})
|
198
|
-
})
|
199
|
-
}
|
166
|
+
constructor(readonly processId: number) {}
|
200
167
|
|
201
|
-
sendRequest(request:
|
202
|
-
|
168
|
+
sendRequest(request: ProcessStreamRequest) {
|
169
|
+
this.mainPort.postMessage(request)
|
203
170
|
}
|
204
171
|
|
205
172
|
get workerPort() {
|
@@ -210,24 +177,7 @@ export class ChannelStoreContext implements IStoreContext {
|
|
210
177
|
return this.channel.port1
|
211
178
|
}
|
212
179
|
|
213
|
-
result(dbResult: DBResponse) {
|
214
|
-
this.mainPort.postMessage(dbResult)
|
215
|
-
}
|
216
|
-
|
217
180
|
close(): void {
|
218
181
|
this.mainPort.close()
|
219
182
|
}
|
220
|
-
|
221
|
-
error(processId: number, e: any): void {
|
222
|
-
console.error('process error', processId, e)
|
223
|
-
const errorResult = ProcessResult.create({
|
224
|
-
states: {
|
225
|
-
error: e?.toString()
|
226
|
-
}
|
227
|
-
})
|
228
|
-
this.subject.next({
|
229
|
-
result: errorResult,
|
230
|
-
processId
|
231
|
-
})
|
232
|
-
}
|
233
183
|
}
|
package/src/service-worker.ts
CHANGED
@@ -1,24 +1,21 @@
|
|
1
1
|
import {
|
2
|
-
DataBinding,
|
3
|
-
DBResponse,
|
4
2
|
DeepPartial,
|
5
3
|
Empty,
|
6
4
|
ProcessConfigRequest,
|
7
|
-
|
5
|
+
ProcessStreamRequest,
|
8
6
|
ProcessStreamResponse,
|
9
7
|
StartRequest
|
10
8
|
} from '@sentio/protos'
|
11
9
|
import { CallContext, ServerError, Status } from 'nice-grpc'
|
12
|
-
import { PluginManager } from './plugin.js'
|
13
10
|
import { errorString } from './utils.js'
|
14
11
|
import { freezeGlobalConfig } from './global-config.js'
|
15
12
|
import { DebugInfo, RichServerError } from 'nice-grpc-error-details'
|
16
|
-
import {
|
13
|
+
import { ProcessorServiceImpl } from './service.js'
|
17
14
|
import { BroadcastChannel, MessagePort, threadId } from 'worker_threads'
|
18
15
|
import { Piscina } from 'piscina'
|
19
16
|
import { configureEndpoints } from './endpoints.js'
|
20
17
|
import { setupLogger } from './logger.js'
|
21
|
-
import {
|
18
|
+
import { Subject } from 'rxjs'
|
22
19
|
|
23
20
|
let started = false
|
24
21
|
|
@@ -38,16 +35,11 @@ process
|
|
38
35
|
unhandled = reason as Error
|
39
36
|
// shutdownServers(1)
|
40
37
|
})
|
38
|
+
.on('exit', () => {
|
39
|
+
console.info('Worker thread exiting, threadId:', threadId)
|
40
|
+
})
|
41
41
|
|
42
|
-
|
43
|
-
if (!started) {
|
44
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
45
|
-
}
|
46
|
-
|
47
|
-
const newConfig = ProcessConfigResponse.fromPartial({})
|
48
|
-
await PluginManager.INSTANCE.configure(newConfig)
|
49
|
-
return newConfig
|
50
|
-
}
|
42
|
+
let service: ProcessorServiceImpl | undefined
|
51
43
|
|
52
44
|
const loader = async (options: any) => {
|
53
45
|
if (options.target) {
|
@@ -59,9 +51,11 @@ const loader = async (options: any) => {
|
|
59
51
|
|
60
52
|
const configureChannel = new BroadcastChannel('configure_channel')
|
61
53
|
configureChannel.onmessage = (request: ProcessConfigRequest) => {
|
62
|
-
getConfig(request)
|
54
|
+
service?.getConfig(request, emptyCallContext)
|
63
55
|
}
|
64
56
|
|
57
|
+
const emptyCallContext = <CallContext>{}
|
58
|
+
|
65
59
|
async function start(request: StartRequest, options: any): Promise<Empty> {
|
66
60
|
if (started) {
|
67
61
|
return {}
|
@@ -69,24 +63,24 @@ async function start(request: StartRequest, options: any): Promise<Empty> {
|
|
69
63
|
freezeGlobalConfig()
|
70
64
|
|
71
65
|
try {
|
72
|
-
|
66
|
+
service = new ProcessorServiceImpl(() => loader(options), options)
|
73
67
|
} catch (e) {
|
74
68
|
throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))
|
75
69
|
}
|
76
70
|
|
77
|
-
await
|
71
|
+
await service.start(request, emptyCallContext)
|
78
72
|
started = true
|
79
73
|
return {}
|
80
74
|
}
|
81
75
|
|
82
76
|
export default async function ({
|
83
|
-
request,
|
84
77
|
processId,
|
78
|
+
request: firstRequest,
|
85
79
|
workerPort
|
86
80
|
}: {
|
87
|
-
request: DataBinding
|
88
81
|
processId: number
|
89
|
-
|
82
|
+
request: ProcessStreamRequest
|
83
|
+
workerPort: MessagePort
|
90
84
|
}) {
|
91
85
|
const { startRequest, configRequest, options } = Piscina.workerData
|
92
86
|
if (!started) {
|
@@ -97,18 +91,19 @@ export default async function ({
|
|
97
91
|
|
98
92
|
if (startRequest) {
|
99
93
|
await start(startRequest, options)
|
100
|
-
console.debug('worker started, template instance:', startRequest.templateInstances?.length)
|
94
|
+
console.debug('worker', threadId, ' started, template instance:', startRequest.templateInstances?.length)
|
101
95
|
}
|
102
96
|
|
103
97
|
if (configRequest) {
|
104
|
-
await getConfig(configRequest)
|
105
|
-
console.debug('worker configured')
|
98
|
+
await service?.getConfig(configRequest, emptyCallContext)
|
99
|
+
console.debug('worker', threadId, ' configured')
|
106
100
|
}
|
107
101
|
}
|
108
102
|
|
109
103
|
if (unhandled) {
|
110
104
|
const err = unhandled
|
111
105
|
unhandled = undefined
|
106
|
+
console.error('Unhandled exception/rejection in previous request:', err)
|
112
107
|
throw new RichServerError(
|
113
108
|
Status.UNAVAILABLE,
|
114
109
|
'Unhandled exception/rejection in previous request: ' + errorString(err),
|
@@ -121,30 +116,20 @@ export default async function ({
|
|
121
116
|
)
|
122
117
|
}
|
123
118
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
class WorkerStoreContext extends AbstractStoreContext {
|
134
|
-
constructor(
|
135
|
-
readonly port: MessagePort,
|
136
|
-
processId: number
|
137
|
-
) {
|
138
|
-
super(processId)
|
139
|
-
this.port.on('message', (resp: DBResponse) => {
|
140
|
-
this.result(resp)
|
119
|
+
await new Promise<void>((resolve) => {
|
120
|
+
const subject = new Subject<DeepPartial<ProcessStreamResponse>>()
|
121
|
+
subject.subscribe((resp: ProcessStreamResponse) => {
|
122
|
+
workerPort.postMessage(resp)
|
123
|
+
// receive the response from the processor , close and resolve the promise
|
124
|
+
if (resp.result) {
|
125
|
+
resolve()
|
126
|
+
workerPort.close()
|
127
|
+
}
|
141
128
|
})
|
142
|
-
|
143
|
-
|
129
|
+
workerPort.on('message', (msg: ProcessStreamRequest) => {
|
130
|
+
const request = msg as ProcessStreamRequest
|
131
|
+
service?.handleRequest(request, firstRequest.binding, subject)
|
144
132
|
})
|
145
|
-
|
146
|
-
|
147
|
-
doSend(req: DeepPartial<ProcessStreamResponse>): void {
|
148
|
-
this.port.postMessage(req)
|
149
|
-
}
|
133
|
+
service?.handleRequest(firstRequest, firstRequest.binding, subject)
|
134
|
+
})
|
150
135
|
}
|
package/src/service.ts
CHANGED
@@ -56,14 +56,17 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
56
56
|
private readonly enablePreprocess: boolean
|
57
57
|
|
58
58
|
private preparedData: PreparedData | undefined
|
59
|
+
readonly enablePartition: boolean
|
59
60
|
|
60
|
-
constructor(loader: () => Promise<any>, shutdownHandler?: () => void) {
|
61
|
+
constructor(loader: () => Promise<any>, options?: any, shutdownHandler?: () => void) {
|
61
62
|
this.loader = loader
|
62
63
|
this.shutdownHandler = shutdownHandler
|
63
64
|
|
64
65
|
this.enablePreprocess = process.env['ENABLE_PREPROCESS']
|
65
66
|
? process.env['ENABLE_PREPROCESS'].toLowerCase() == 'true'
|
66
67
|
: false
|
68
|
+
|
69
|
+
this.enablePartition = options?.['enable-partition'] == true
|
67
70
|
}
|
68
71
|
|
69
72
|
async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {
|
@@ -413,65 +416,105 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
413
416
|
yield* from(subject).pipe(withAbort(context.signal))
|
414
417
|
}
|
415
418
|
|
419
|
+
private dbContexts = new Contexts()
|
420
|
+
|
416
421
|
protected async handleRequests(
|
417
422
|
requests: AsyncIterable<ProcessStreamRequest>,
|
418
423
|
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
419
424
|
) {
|
420
|
-
|
425
|
+
let lastBinding: DataBinding | undefined = undefined
|
421
426
|
for await (const request of requests) {
|
422
427
|
try {
|
423
|
-
// console.
|
428
|
+
// console.log('received request:', request, 'lastBinding:', lastBinding)
|
424
429
|
if (request.binding) {
|
425
|
-
|
426
|
-
|
427
|
-
// Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
|
428
|
-
// for older SDK version, so we just return empty result for them here
|
429
|
-
if (request.binding.handlerType === HandlerType.UNKNOWN) {
|
430
|
-
subject.next({
|
431
|
-
processId: request.processId,
|
432
|
-
result: ProcessResult.create()
|
433
|
-
})
|
434
|
-
continue
|
435
|
-
}
|
436
|
-
|
437
|
-
const binding = request.binding
|
438
|
-
const dbContext = contexts.new(request.processId, subject)
|
439
|
-
const start = Date.now()
|
440
|
-
PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
|
441
|
-
.then(async (result) => {
|
442
|
-
// await all pending db requests
|
443
|
-
await dbContext.awaitPendings()
|
444
|
-
subject.next({
|
445
|
-
result,
|
446
|
-
processId: request.processId
|
447
|
-
})
|
448
|
-
recordRuntimeInfo(result, binding.handlerType)
|
449
|
-
})
|
450
|
-
.catch((e) => {
|
451
|
-
console.debug(e)
|
452
|
-
dbContext.error(request.processId, e)
|
453
|
-
process_binding_error.add(1)
|
454
|
-
})
|
455
|
-
.finally(() => {
|
456
|
-
const cost = Date.now() - start
|
457
|
-
process_binding_time.add(cost)
|
458
|
-
contexts.delete(request.processId)
|
459
|
-
})
|
460
|
-
}
|
461
|
-
if (request.dbResult) {
|
462
|
-
const dbContext = contexts.get(request.processId)
|
463
|
-
try {
|
464
|
-
dbContext?.result(request.dbResult)
|
465
|
-
} catch (e) {
|
466
|
-
subject.error(new Error('db result error, process should stop'))
|
467
|
-
}
|
430
|
+
lastBinding = request.binding
|
468
431
|
}
|
432
|
+
this.handleRequest(request, lastBinding, subject)
|
469
433
|
} catch (e) {
|
470
434
|
// should not happen
|
471
435
|
console.error('unexpect error during handle loop', e)
|
472
436
|
}
|
473
437
|
}
|
474
438
|
}
|
439
|
+
|
440
|
+
async handleRequest(
|
441
|
+
request: ProcessStreamRequest,
|
442
|
+
lastBinding: DataBinding | undefined,
|
443
|
+
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
444
|
+
) {
|
445
|
+
if (request.binding) {
|
446
|
+
process_binding_count.add(1)
|
447
|
+
|
448
|
+
// Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
|
449
|
+
// for older SDK version, so we just return empty result for them here
|
450
|
+
if (request.binding.handlerType === HandlerType.UNKNOWN) {
|
451
|
+
subject.next({
|
452
|
+
processId: request.processId,
|
453
|
+
result: ProcessResult.create()
|
454
|
+
})
|
455
|
+
return
|
456
|
+
}
|
457
|
+
|
458
|
+
if (this.enablePartition) {
|
459
|
+
try {
|
460
|
+
const partitions = await PluginManager.INSTANCE.partition(request.binding)
|
461
|
+
subject.next({
|
462
|
+
processId: request.processId,
|
463
|
+
partitions
|
464
|
+
})
|
465
|
+
} catch (e) {
|
466
|
+
console.error('Partition error:', e)
|
467
|
+
subject.error(new Error('Partition error: ' + errorString(e)))
|
468
|
+
return
|
469
|
+
}
|
470
|
+
} else {
|
471
|
+
this.startProcess(request.processId, request.binding, subject)
|
472
|
+
}
|
473
|
+
}
|
474
|
+
|
475
|
+
if (request.start) {
|
476
|
+
if (!lastBinding) {
|
477
|
+
console.error('start request received without binding')
|
478
|
+
subject.error(new Error('start request received without binding'))
|
479
|
+
return
|
480
|
+
}
|
481
|
+
this.startProcess(request.processId, lastBinding, subject)
|
482
|
+
}
|
483
|
+
|
484
|
+
if (request.dbResult) {
|
485
|
+
const dbContext = this.dbContexts.get(request.processId)
|
486
|
+
try {
|
487
|
+
dbContext?.result(request.dbResult)
|
488
|
+
} catch (e) {
|
489
|
+
subject.error(new Error('db result error, process should stop'))
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
|
494
|
+
private startProcess(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
|
495
|
+
const dbContext = this.dbContexts.new(processId, subject)
|
496
|
+
const start = Date.now()
|
497
|
+
PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
|
498
|
+
.then(async (result) => {
|
499
|
+
// await all pending db requests
|
500
|
+
await dbContext.awaitPendings()
|
501
|
+
subject.next({
|
502
|
+
result,
|
503
|
+
processId: processId
|
504
|
+
})
|
505
|
+
recordRuntimeInfo(result, binding.handlerType)
|
506
|
+
})
|
507
|
+
.catch((e) => {
|
508
|
+
console.error(e)
|
509
|
+
dbContext.error(processId, e)
|
510
|
+
process_binding_error.add(1)
|
511
|
+
})
|
512
|
+
.finally(() => {
|
513
|
+
const cost = Date.now() - start
|
514
|
+
process_binding_time.add(cost)
|
515
|
+
this.dbContexts.delete(processId)
|
516
|
+
})
|
517
|
+
}
|
475
518
|
}
|
476
519
|
|
477
520
|
export function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
|
package/src/utils.ts
CHANGED
@@ -10,12 +10,22 @@ export function mergeProcessResults(results: ProcessResult[]): Required<ProcessR
|
|
10
10
|
...ProcessResultFull.create(),
|
11
11
|
states: StateResult.create()
|
12
12
|
}
|
13
|
+
return mergeProcessResultsInPlace(res, results)
|
14
|
+
}
|
13
15
|
|
16
|
+
export function mergeProcessResultsInPlace(
|
17
|
+
res: ProcessResult,
|
18
|
+
results: ProcessResult[]
|
19
|
+
): Required<ProcessResult, 'states'> {
|
20
|
+
res.states = res.states || StateResult.create()
|
14
21
|
for (const r of results) {
|
15
|
-
|
16
|
-
|
17
|
-
res.
|
18
|
-
res.
|
22
|
+
// not using spread operator since it puts all element on the stack
|
23
|
+
// cause maximum call stack size exceeded error if it's a large array
|
24
|
+
mergeArrayInPlace(res.counters, r.counters)
|
25
|
+
mergeArrayInPlace(res.gauges, r.gauges)
|
26
|
+
mergeArrayInPlace(res.events, r.events)
|
27
|
+
mergeArrayInPlace(res.exports, r.exports)
|
28
|
+
mergeArrayInPlace(res.timeseriesResult, r.timeseriesResult)
|
19
29
|
res.states = {
|
20
30
|
configUpdated: res.states?.configUpdated || r.states?.configUpdated || false
|
21
31
|
}
|
@@ -23,6 +33,12 @@ export function mergeProcessResults(results: ProcessResult[]): Required<ProcessR
|
|
23
33
|
return res
|
24
34
|
}
|
25
35
|
|
36
|
+
function mergeArrayInPlace<T>(dst: T[], src: T[]) {
|
37
|
+
for (const r of src) {
|
38
|
+
dst.push(r)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
26
42
|
export function errorString(e: Error): string {
|
27
43
|
return e.message + '\n' + e.stack
|
28
44
|
}
|