@sentio/runtime 2.59.0-rc.39 → 2.59.0-rc.40
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-FO2V2K7T.js → chunk-3SLKMZUX.js} +1 -1
- package/lib/{chunk-C4IDZGJI.js → chunk-ELSE7PSY.js} +4318 -4307
- package/lib/{chunk-C4IDZGJI.js.map → chunk-ELSE7PSY.js.map} +1 -1
- package/lib/{chunk-CPLWSUD7.js → chunk-FDZMT76B.js} +2 -2
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -2
- package/lib/processor-runner.js +69 -135
- package/lib/processor-runner.js.map +1 -1
- package/lib/service-worker.d.ts +5 -6
- package/lib/service-worker.js +28 -92
- package/lib/service-worker.js.map +1 -1
- package/lib/test-processor.test.js.map +1 -1
- package/package.json +1 -1
- package/src/processor-runner.ts +9 -3
- package/src/service-manager.ts +43 -140
- package/src/service-worker.ts +28 -101
- package/src/service.ts +60 -49
- /package/lib/{chunk-FO2V2K7T.js.map → chunk-3SLKMZUX.js.map} +0 -0
- /package/lib/{chunk-CPLWSUD7.js.map → chunk-FDZMT76B.js.map} +0 -0
package/src/service-worker.ts
CHANGED
@@ -1,26 +1,21 @@
|
|
1
1
|
import {
|
2
|
-
DataBinding,
|
3
|
-
DBResponse,
|
4
2
|
DeepPartial,
|
5
3
|
Empty,
|
6
4
|
ProcessConfigRequest,
|
7
|
-
|
8
|
-
ProcessResult,
|
5
|
+
ProcessStreamRequest,
|
9
6
|
ProcessStreamResponse,
|
10
|
-
ProcessStreamResponse_Partitions,
|
11
7
|
StartRequest
|
12
8
|
} from '@sentio/protos'
|
13
9
|
import { CallContext, ServerError, Status } from 'nice-grpc'
|
14
|
-
import { PluginManager } from './plugin.js'
|
15
10
|
import { errorString } from './utils.js'
|
16
11
|
import { freezeGlobalConfig } from './global-config.js'
|
17
12
|
import { DebugInfo, RichServerError } from 'nice-grpc-error-details'
|
18
|
-
import {
|
13
|
+
import { ProcessorServiceImpl } from './service.js'
|
19
14
|
import { BroadcastChannel, MessagePort, threadId } from 'worker_threads'
|
20
15
|
import { Piscina } from 'piscina'
|
21
16
|
import { configureEndpoints } from './endpoints.js'
|
22
17
|
import { setupLogger } from './logger.js'
|
23
|
-
import {
|
18
|
+
import { Subject } from 'rxjs'
|
24
19
|
|
25
20
|
let started = false
|
26
21
|
|
@@ -44,15 +39,7 @@ process
|
|
44
39
|
console.info('Worker thread exiting, threadId:', threadId)
|
45
40
|
})
|
46
41
|
|
47
|
-
|
48
|
-
if (!started) {
|
49
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
50
|
-
}
|
51
|
-
|
52
|
-
const newConfig = ProcessConfigResponse.fromPartial({})
|
53
|
-
await PluginManager.INSTANCE.configure(newConfig)
|
54
|
-
return newConfig
|
55
|
-
}
|
42
|
+
let service: ProcessorServiceImpl | undefined
|
56
43
|
|
57
44
|
const loader = async (options: any) => {
|
58
45
|
if (options.target) {
|
@@ -64,9 +51,11 @@ const loader = async (options: any) => {
|
|
64
51
|
|
65
52
|
const configureChannel = new BroadcastChannel('configure_channel')
|
66
53
|
configureChannel.onmessage = (request: ProcessConfigRequest) => {
|
67
|
-
getConfig(request)
|
54
|
+
service?.getConfig(request, emptyCallContext)
|
68
55
|
}
|
69
56
|
|
57
|
+
const emptyCallContext = <CallContext>{}
|
58
|
+
|
70
59
|
async function start(request: StartRequest, options: any): Promise<Empty> {
|
71
60
|
if (started) {
|
72
61
|
return {}
|
@@ -74,26 +63,24 @@ async function start(request: StartRequest, options: any): Promise<Empty> {
|
|
74
63
|
freezeGlobalConfig()
|
75
64
|
|
76
65
|
try {
|
77
|
-
|
66
|
+
service = new ProcessorServiceImpl(() => loader(options), options)
|
78
67
|
} catch (e) {
|
79
68
|
throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))
|
80
69
|
}
|
81
70
|
|
82
|
-
await
|
71
|
+
await service.start(request, emptyCallContext)
|
83
72
|
started = true
|
84
73
|
return {}
|
85
74
|
}
|
86
75
|
|
87
76
|
export default async function ({
|
88
|
-
request,
|
89
77
|
processId,
|
90
|
-
|
91
|
-
|
78
|
+
request: firstRequest,
|
79
|
+
workerPort
|
92
80
|
}: {
|
93
|
-
request: DataBinding
|
94
81
|
processId: number
|
95
|
-
|
96
|
-
|
82
|
+
request: ProcessStreamRequest
|
83
|
+
workerPort: MessagePort
|
97
84
|
}) {
|
98
85
|
const { startRequest, configRequest, options } = Piscina.workerData
|
99
86
|
if (!started) {
|
@@ -108,7 +95,7 @@ export default async function ({
|
|
108
95
|
}
|
109
96
|
|
110
97
|
if (configRequest) {
|
111
|
-
await getConfig(configRequest)
|
98
|
+
await service?.getConfig(configRequest, emptyCallContext)
|
112
99
|
console.debug('worker', threadId, ' configured')
|
113
100
|
}
|
114
101
|
}
|
@@ -129,80 +116,20 @@ export default async function ({
|
|
129
116
|
)
|
130
117
|
}
|
131
118
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
)
|
142
|
-
)
|
143
|
-
},
|
144
|
-
(options['process-timeout'] ?? 60) * 1000
|
145
|
-
)
|
146
|
-
})
|
147
|
-
let resultPromise: Promise<ProcessResult | ProcessStreamResponse_Partitions>
|
148
|
-
if (partition) {
|
149
|
-
resultPromise = PluginManager.INSTANCE.partition(request)
|
150
|
-
} else {
|
151
|
-
resultPromise = PluginManager.INSTANCE.processBinding(
|
152
|
-
request,
|
153
|
-
undefined,
|
154
|
-
workerPort ? new WorkerStoreContext(workerPort, processId) : undefined
|
155
|
-
).then((result) => {
|
156
|
-
recordRuntimeInfo(result, request.handlerType)
|
157
|
-
return result
|
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
|
+
}
|
158
128
|
})
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
const result = await Promise.race([resultPromise, timeoutPromise])
|
163
|
-
console.debug(
|
164
|
-
'worker',
|
165
|
-
threadId,
|
166
|
-
`finished ${partition ? 'partition' : 'process'} request`,
|
167
|
-
processId,
|
168
|
-
'took',
|
169
|
-
Date.now() - now
|
170
|
-
)
|
171
|
-
return result
|
172
|
-
} catch (e) {
|
173
|
-
console.error('worker', threadId, `failed ${partition ? 'partition' : 'process'} request`, processId, 'error:', e)
|
174
|
-
if (e instanceof RichServerError) {
|
175
|
-
throw e
|
176
|
-
} else {
|
177
|
-
throw new RichServerError(
|
178
|
-
Status.INTERNAL,
|
179
|
-
`Worker ${threadId} failed to process request ${processId}: ` + errorString(e),
|
180
|
-
[
|
181
|
-
DebugInfo.fromPartial({
|
182
|
-
detail: errorString(e),
|
183
|
-
stackEntries: e.stack?.split('\n')
|
184
|
-
})
|
185
|
-
]
|
186
|
-
)
|
187
|
-
}
|
188
|
-
}
|
189
|
-
}
|
190
|
-
|
191
|
-
class WorkerStoreContext extends AbstractStoreContext {
|
192
|
-
constructor(
|
193
|
-
readonly port: MessagePort,
|
194
|
-
processId: number
|
195
|
-
) {
|
196
|
-
super(processId)
|
197
|
-
this.port.on('message', (resp: DBResponse) => {
|
198
|
-
this.result(resp)
|
129
|
+
workerPort.on('message', (msg: ProcessStreamRequest) => {
|
130
|
+
const request = msg as ProcessStreamRequest
|
131
|
+
service?.handleRequest(request, firstRequest.binding, subject)
|
199
132
|
})
|
200
|
-
|
201
|
-
|
202
|
-
})
|
203
|
-
}
|
204
|
-
|
205
|
-
doSend(req: DeepPartial<ProcessStreamResponse>): void {
|
206
|
-
this.port.postMessage(req)
|
207
|
-
}
|
133
|
+
service?.handleRequest(firstRequest, firstRequest.binding, subject)
|
134
|
+
})
|
208
135
|
}
|
package/src/service.ts
CHANGED
@@ -58,7 +58,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
58
58
|
private preparedData: PreparedData | undefined
|
59
59
|
readonly enablePartition: boolean
|
60
60
|
|
61
|
-
constructor(loader: () => Promise<any>, shutdownHandler?: () => void) {
|
61
|
+
constructor(loader: () => Promise<any>, options?: any, shutdownHandler?: () => void) {
|
62
62
|
this.loader = loader
|
63
63
|
this.shutdownHandler = shutdownHandler
|
64
64
|
|
@@ -66,7 +66,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
66
66
|
? process.env['ENABLE_PREPROCESS'].toLowerCase() == 'true'
|
67
67
|
: false
|
68
68
|
|
69
|
-
this.enablePartition =
|
69
|
+
this.enablePartition = options?.['enable-partition'] == true
|
70
70
|
}
|
71
71
|
|
72
72
|
async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {
|
@@ -416,58 +416,20 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
416
416
|
yield* from(subject).pipe(withAbort(context.signal))
|
417
417
|
}
|
418
418
|
|
419
|
+
private dbContexts = new Contexts()
|
420
|
+
|
419
421
|
protected async handleRequests(
|
420
422
|
requests: AsyncIterable<ProcessStreamRequest>,
|
421
423
|
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
422
424
|
) {
|
423
|
-
const contexts = new Contexts()
|
424
425
|
let lastBinding: DataBinding | undefined = undefined
|
425
426
|
for await (const request of requests) {
|
426
427
|
try {
|
427
428
|
// console.log('received request:', request, 'lastBinding:', lastBinding)
|
428
429
|
if (request.binding) {
|
429
|
-
process_binding_count.add(1)
|
430
|
-
|
431
|
-
// Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
|
432
|
-
// for older SDK version, so we just return empty result for them here
|
433
|
-
if (request.binding.handlerType === HandlerType.UNKNOWN) {
|
434
|
-
subject.next({
|
435
|
-
processId: request.processId,
|
436
|
-
result: ProcessResult.create()
|
437
|
-
})
|
438
|
-
continue
|
439
|
-
}
|
440
430
|
lastBinding = request.binding
|
441
|
-
|
442
|
-
if (this.enablePartition) {
|
443
|
-
PluginManager.INSTANCE.partition(request.binding).then((partitions) => {
|
444
|
-
subject.next({
|
445
|
-
processId: request.processId,
|
446
|
-
partitions
|
447
|
-
})
|
448
|
-
})
|
449
|
-
} else {
|
450
|
-
this.startProcess(request.processId, request.binding, contexts, subject)
|
451
|
-
}
|
452
|
-
}
|
453
|
-
|
454
|
-
if (request.start) {
|
455
|
-
if (!lastBinding) {
|
456
|
-
console.error('start request received without binding')
|
457
|
-
subject.error(new Error('start request received without binding'))
|
458
|
-
continue
|
459
|
-
}
|
460
|
-
this.startProcess(request.processId, lastBinding, contexts, subject)
|
461
|
-
}
|
462
|
-
|
463
|
-
if (request.dbResult) {
|
464
|
-
const dbContext = contexts.get(request.processId)
|
465
|
-
try {
|
466
|
-
dbContext?.result(request.dbResult)
|
467
|
-
} catch (e) {
|
468
|
-
subject.error(new Error('db result error, process should stop'))
|
469
|
-
}
|
470
431
|
}
|
432
|
+
this.handleRequest(request, lastBinding, subject)
|
471
433
|
} catch (e) {
|
472
434
|
// should not happen
|
473
435
|
console.error('unexpect error during handle loop', e)
|
@@ -475,13 +437,62 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
475
437
|
}
|
476
438
|
}
|
477
439
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
contexts: Contexts,
|
440
|
+
async handleRequest(
|
441
|
+
request: ProcessStreamRequest,
|
442
|
+
lastBinding: DataBinding | undefined,
|
482
443
|
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
483
444
|
) {
|
484
|
-
|
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)
|
485
496
|
const start = Date.now()
|
486
497
|
PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
|
487
498
|
.then(async (result) => {
|
@@ -501,7 +512,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
501
512
|
.finally(() => {
|
502
513
|
const cost = Date.now() - start
|
503
514
|
process_binding_time.add(cost)
|
504
|
-
|
515
|
+
this.dbContexts.delete(processId)
|
505
516
|
})
|
506
517
|
}
|
507
518
|
}
|
File without changes
|
File without changes
|