@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/lib/service-worker.d.ts
CHANGED
@@ -1,11 +1,10 @@
|
|
1
|
-
import {
|
1
|
+
import { ProcessStreamRequest } from '@sentio/protos';
|
2
2
|
import { MessagePort } from 'worker_threads';
|
3
3
|
|
4
|
-
declare function export_default({
|
5
|
-
request: DataBinding;
|
4
|
+
declare function export_default({ processId, request: firstRequest, workerPort }: {
|
6
5
|
processId: number;
|
7
|
-
|
8
|
-
|
9
|
-
}): Promise<
|
6
|
+
request: ProcessStreamRequest;
|
7
|
+
workerPort: MessagePort;
|
8
|
+
}): Promise<void>;
|
10
9
|
|
11
10
|
export { export_default as default };
|
package/lib/service-worker.js
CHANGED
@@ -1,27 +1,26 @@
|
|
1
1
|
import { createRequire as createRequireShim } from 'module'; const require = createRequireShim(import.meta.url);
|
2
2
|
import {
|
3
3
|
setupLogger
|
4
|
-
} from "./chunk-
|
4
|
+
} from "./chunk-FDZMT76B.js";
|
5
5
|
import {
|
6
|
-
|
6
|
+
ProcessorServiceImpl,
|
7
7
|
configureEndpoints,
|
8
8
|
errorString,
|
9
9
|
freezeGlobalConfig,
|
10
|
-
|
10
|
+
require_cjs,
|
11
11
|
require_lib3 as require_lib,
|
12
12
|
require_lib4 as require_lib2
|
13
|
-
} from "./chunk-
|
13
|
+
} from "./chunk-ELSE7PSY.js";
|
14
14
|
import {
|
15
|
-
PluginManager,
|
16
|
-
ProcessConfigResponse,
|
17
15
|
__toESM
|
18
|
-
} from "./chunk-
|
16
|
+
} from "./chunk-3SLKMZUX.js";
|
19
17
|
|
20
18
|
// src/service-worker.ts
|
21
19
|
var import_nice_grpc = __toESM(require_lib(), 1);
|
22
20
|
var import_nice_grpc_error_details = __toESM(require_lib2(), 1);
|
23
21
|
import { BroadcastChannel, threadId } from "worker_threads";
|
24
22
|
import { Piscina } from "piscina";
|
23
|
+
var import_rxjs = __toESM(require_cjs(), 1);
|
25
24
|
var started = false;
|
26
25
|
var unhandled;
|
27
26
|
process.on("uncaughtException", (err) => {
|
@@ -36,14 +35,7 @@ process.on("uncaughtException", (err) => {
|
|
36
35
|
}).on("exit", () => {
|
37
36
|
console.info("Worker thread exiting, threadId:", threadId);
|
38
37
|
});
|
39
|
-
|
40
|
-
if (!started) {
|
41
|
-
throw new import_nice_grpc.ServerError(import_nice_grpc.Status.UNAVAILABLE, "Service Not started.");
|
42
|
-
}
|
43
|
-
const newConfig = ProcessConfigResponse.fromPartial({});
|
44
|
-
await PluginManager.INSTANCE.configure(newConfig);
|
45
|
-
return newConfig;
|
46
|
-
}
|
38
|
+
var service;
|
47
39
|
var loader = async (options) => {
|
48
40
|
if (options.target) {
|
49
41
|
const m = await import(options.target);
|
@@ -53,27 +45,27 @@ var loader = async (options) => {
|
|
53
45
|
};
|
54
46
|
var configureChannel = new BroadcastChannel("configure_channel");
|
55
47
|
configureChannel.onmessage = (request) => {
|
56
|
-
getConfig(request);
|
48
|
+
service?.getConfig(request, emptyCallContext);
|
57
49
|
};
|
50
|
+
var emptyCallContext = {};
|
58
51
|
async function start(request, options) {
|
59
52
|
if (started) {
|
60
53
|
return {};
|
61
54
|
}
|
62
55
|
freezeGlobalConfig();
|
63
56
|
try {
|
64
|
-
|
57
|
+
service = new ProcessorServiceImpl(() => loader(options), options);
|
65
58
|
} catch (e) {
|
66
59
|
throw new import_nice_grpc.ServerError(import_nice_grpc.Status.INVALID_ARGUMENT, "Failed to load processor: " + errorString(e));
|
67
60
|
}
|
68
|
-
await
|
61
|
+
await service.start(request, emptyCallContext);
|
69
62
|
started = true;
|
70
63
|
return {};
|
71
64
|
}
|
72
65
|
async function service_worker_default({
|
73
|
-
request,
|
74
66
|
processId,
|
75
|
-
|
76
|
-
|
67
|
+
request: firstRequest,
|
68
|
+
workerPort
|
77
69
|
}) {
|
78
70
|
const { startRequest, configRequest, options } = Piscina.workerData;
|
79
71
|
if (!started) {
|
@@ -85,7 +77,7 @@ async function service_worker_default({
|
|
85
77
|
console.debug("worker", threadId, " started, template instance:", startRequest.templateInstances?.length);
|
86
78
|
}
|
87
79
|
if (configRequest) {
|
88
|
-
await getConfig(configRequest);
|
80
|
+
await service?.getConfig(configRequest, emptyCallContext);
|
89
81
|
console.debug("worker", threadId, " configured");
|
90
82
|
}
|
91
83
|
}
|
@@ -104,78 +96,22 @@ async function service_worker_default({
|
|
104
96
|
]
|
105
97
|
);
|
106
98
|
}
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
()
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
`Request ${processId} timed out after ${options["request-timeout"]} ms`
|
116
|
-
)
|
117
|
-
);
|
118
|
-
},
|
119
|
-
(options["process-timeout"] ?? 60) * 1e3
|
120
|
-
);
|
121
|
-
});
|
122
|
-
let resultPromise;
|
123
|
-
if (partition) {
|
124
|
-
resultPromise = PluginManager.INSTANCE.partition(request);
|
125
|
-
} else {
|
126
|
-
resultPromise = PluginManager.INSTANCE.processBinding(
|
127
|
-
request,
|
128
|
-
void 0,
|
129
|
-
workerPort ? new WorkerStoreContext(workerPort, processId) : void 0
|
130
|
-
).then((result) => {
|
131
|
-
recordRuntimeInfo(result, request.handlerType);
|
132
|
-
return result;
|
133
|
-
});
|
134
|
-
}
|
135
|
-
try {
|
136
|
-
const result = await Promise.race([resultPromise, timeoutPromise]);
|
137
|
-
console.debug(
|
138
|
-
"worker",
|
139
|
-
threadId,
|
140
|
-
`finished ${partition ? "partition" : "process"} request`,
|
141
|
-
processId,
|
142
|
-
"took",
|
143
|
-
Date.now() - now
|
144
|
-
);
|
145
|
-
return result;
|
146
|
-
} catch (e) {
|
147
|
-
console.error("worker", threadId, `failed ${partition ? "partition" : "process"} request`, processId, "error:", e);
|
148
|
-
if (e instanceof import_nice_grpc_error_details.RichServerError) {
|
149
|
-
throw e;
|
150
|
-
} else {
|
151
|
-
throw new import_nice_grpc_error_details.RichServerError(
|
152
|
-
import_nice_grpc.Status.INTERNAL,
|
153
|
-
`Worker ${threadId} failed to process request ${processId}: ` + errorString(e),
|
154
|
-
[
|
155
|
-
import_nice_grpc_error_details.DebugInfo.fromPartial({
|
156
|
-
detail: errorString(e),
|
157
|
-
stackEntries: e.stack?.split("\n")
|
158
|
-
})
|
159
|
-
]
|
160
|
-
);
|
161
|
-
}
|
162
|
-
}
|
163
|
-
}
|
164
|
-
var WorkerStoreContext = class extends AbstractStoreContext {
|
165
|
-
constructor(port, processId) {
|
166
|
-
super(processId);
|
167
|
-
this.port = port;
|
168
|
-
this.port.on("message", (resp) => {
|
169
|
-
this.result(resp);
|
99
|
+
await new Promise((resolve) => {
|
100
|
+
const subject = new import_rxjs.Subject();
|
101
|
+
subject.subscribe((resp) => {
|
102
|
+
workerPort.postMessage(resp);
|
103
|
+
if (resp.result) {
|
104
|
+
resolve();
|
105
|
+
workerPort.close();
|
106
|
+
}
|
170
107
|
});
|
171
|
-
|
172
|
-
|
108
|
+
workerPort.on("message", (msg) => {
|
109
|
+
const request = msg;
|
110
|
+
service?.handleRequest(request, firstRequest.binding, subject);
|
173
111
|
});
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
}
|
178
|
-
};
|
112
|
+
service?.handleRequest(firstRequest, firstRequest.binding, subject);
|
113
|
+
});
|
114
|
+
}
|
179
115
|
import("node:process").then((p) => p.stdout.write(""));
|
180
116
|
export {
|
181
117
|
service_worker_default as default
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/service-worker.ts"],"sourcesContent":["import {\n
|
1
|
+
{"version":3,"sources":["../src/service-worker.ts"],"sourcesContent":["import {\n DeepPartial,\n Empty,\n ProcessConfigRequest,\n ProcessStreamRequest,\n ProcessStreamResponse,\n StartRequest\n} from '@sentio/protos'\nimport { CallContext, ServerError, Status } from 'nice-grpc'\nimport { errorString } from './utils.js'\nimport { freezeGlobalConfig } from './global-config.js'\nimport { DebugInfo, RichServerError } from 'nice-grpc-error-details'\nimport { ProcessorServiceImpl } from './service.js'\nimport { BroadcastChannel, MessagePort, threadId } from 'worker_threads'\nimport { Piscina } from 'piscina'\nimport { configureEndpoints } from './endpoints.js'\nimport { setupLogger } from './logger.js'\nimport { Subject } from 'rxjs'\n\nlet started = false\n\nlet unhandled: Error | undefined\n\nprocess\n .on('uncaughtException', (err) => {\n console.error('Uncaught Exception, please checking if await is properly used', err)\n unhandled = err\n })\n .on('unhandledRejection', (reason, p) => {\n // @ts-ignore ignore invalid ens error\n if (reason?.message.startsWith('invalid ENS name (disallowed character: \"*\"')) {\n return\n }\n console.error('Unhandled Rejection, please checking if await is properly', reason)\n unhandled = reason as Error\n // shutdownServers(1)\n })\n .on('exit', () => {\n console.info('Worker thread exiting, threadId:', threadId)\n })\n\nlet service: ProcessorServiceImpl | undefined\n\nconst loader = async (options: any) => {\n if (options.target) {\n const m = await import(options.target)\n console.debug('Module loaded, path:', options.target, 'module:', m)\n return m\n }\n}\n\nconst configureChannel = new BroadcastChannel('configure_channel')\nconfigureChannel.onmessage = (request: ProcessConfigRequest) => {\n service?.getConfig(request, emptyCallContext)\n}\n\nconst emptyCallContext = <CallContext>{}\n\nasync function start(request: StartRequest, options: any): Promise<Empty> {\n if (started) {\n return {}\n }\n freezeGlobalConfig()\n\n try {\n service = new ProcessorServiceImpl(() => loader(options), options)\n } catch (e) {\n throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))\n }\n\n await service.start(request, emptyCallContext)\n started = true\n return {}\n}\n\nexport default async function ({\n processId,\n request: firstRequest,\n workerPort\n}: {\n processId: number\n request: ProcessStreamRequest\n workerPort: MessagePort\n}) {\n const { startRequest, configRequest, options } = Piscina.workerData\n if (!started) {\n const logLevel = process.env['LOG_LEVEL']?.toUpperCase()\n setupLogger(options['log-format'] === 'json', logLevel === 'debug' ? true : options.debug, threadId)\n\n configureEndpoints(options)\n\n if (startRequest) {\n await start(startRequest, options)\n console.debug('worker', threadId, ' started, template instance:', startRequest.templateInstances?.length)\n }\n\n if (configRequest) {\n await service?.getConfig(configRequest, emptyCallContext)\n console.debug('worker', threadId, ' configured')\n }\n }\n\n if (unhandled) {\n const err = unhandled\n unhandled = undefined\n console.error('Unhandled exception/rejection in previous request:', err)\n throw new RichServerError(\n Status.UNAVAILABLE,\n 'Unhandled exception/rejection in previous request: ' + errorString(err),\n [\n DebugInfo.fromPartial({\n detail: err.message,\n stackEntries: err.stack?.split('\\n')\n })\n ]\n )\n }\n\n await new Promise<void>((resolve) => {\n const subject = new Subject<DeepPartial<ProcessStreamResponse>>()\n subject.subscribe((resp: ProcessStreamResponse) => {\n workerPort.postMessage(resp)\n // receive the response from the processor , close and resolve the promise\n if (resp.result) {\n resolve()\n workerPort.close()\n }\n })\n workerPort.on('message', (msg: ProcessStreamRequest) => {\n const request = msg as ProcessStreamRequest\n service?.handleRequest(request, firstRequest.binding, subject)\n })\n service?.handleRequest(firstRequest, firstRequest.binding, subject)\n })\n}\n;import(\"node:process\").then((p) => p.stdout.write(\"\"));"],"mappings":"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","names":[]}
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../src/test-processor.test.ts"],"sourcesContent":["import { Plugin, PluginManager } from './plugin.js'\nimport { DataBinding, HandlerType, ProcessResult } from './gen/processor/protos/processor.js'\n\nclass TestPlugin extends Plugin {\n async processBinding(request: DataBinding): Promise<ProcessResult> {\n const dbContext = PluginManager.INSTANCE.dbContextLocalStorage.getStore()\n if (dbContext) {\n await dbContext.sendRequest({\n get: {\n entity: 'Test',\n id: '1'\n }\n })\n }\n\n return ProcessResult.fromPartial({\n states: {\n configUpdated: true\n }\n })\n }\n supportedHandlers = [HandlerType.UNKNOWN, HandlerType.ETH_LOG]\n}\n\nPluginManager.INSTANCE.plugins = []\nPluginManager.INSTANCE.register(new TestPlugin())\n;import(\"node:process\").then((p) => p.stdout.write(\"\"));"],"mappings":";;;;;;;;
|
1
|
+
{"version":3,"sources":["../src/test-processor.test.ts"],"sourcesContent":["import { Plugin, PluginManager } from './plugin.js'\nimport { DataBinding, HandlerType, ProcessResult } from './gen/processor/protos/processor.js'\nimport { ProcessStreamResponse_Partitions } from '@sentio/protos'\n\nclass TestPlugin extends Plugin {\n async processBinding(request: DataBinding): Promise<ProcessResult> {\n const dbContext = PluginManager.INSTANCE.dbContextLocalStorage.getStore()\n if (dbContext) {\n await dbContext.sendRequest({\n get: {\n entity: 'Test',\n id: '1'\n }\n })\n }\n\n return ProcessResult.fromPartial({\n states: {\n configUpdated: true\n }\n })\n }\n supportedHandlers = [HandlerType.UNKNOWN, HandlerType.ETH_LOG]\n\n async partition(request: DataBinding): Promise<ProcessStreamResponse_Partitions> {\n return {\n partitions: request.handlerIds.reduce(\n (acc, id) => ({\n ...acc,\n [id]: {\n userValue: 'test'\n }\n }),\n {}\n )\n }\n }\n}\n\nPluginManager.INSTANCE.plugins = []\nPluginManager.INSTANCE.register(new TestPlugin())\n;import(\"node:process\").then((p) => p.stdout.write(\"\"));"],"mappings":";;;;;;;;AAIA,IAAM,aAAN,cAAyB,OAAO;AAAA,EAC9B,MAAM,eAAe,SAA8C;AACjE,UAAM,YAAY,cAAc,SAAS,sBAAsB,SAAS;AACxE,QAAI,WAAW;AACb,YAAM,UAAU,YAAY;AAAA,QAC1B,KAAK;AAAA,UACH,QAAQ;AAAA,UACR,IAAI;AAAA,QACN;AAAA,MACF,CAAC;AAAA,IACH;AAEA,WAAO,cAAc,YAAY;AAAA,MAC/B,QAAQ;AAAA,QACN,eAAe;AAAA,MACjB;AAAA,IACF,CAAC;AAAA,EACH;AAAA,EACA,oBAAoB,iCAAyC;AAAA,EAE7D,MAAM,UAAU,SAAiE;AAC/E,WAAO;AAAA,MACL,YAAY,QAAQ,WAAW;AAAA,QAC7B,CAAC,KAAK,QAAQ;AAAA,UACZ,GAAG;AAAA,UACH,CAAC,EAAE,GAAG;AAAA,YACJ,WAAW;AAAA,UACb;AAAA,QACF;AAAA,QACA,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEA,cAAc,SAAS,UAAU,CAAC;AAClC,cAAc,SAAS,SAAS,IAAI,WAAW,CAAC;AAC/C,OAAO,cAAc,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,CAAC;","names":[]}
|
package/package.json
CHANGED
package/src/processor-runner.ts
CHANGED
@@ -50,7 +50,13 @@ export const optionDefinitions = [
|
|
50
50
|
{ name: 'otlp-debug', type: Boolean, defaultValue: false },
|
51
51
|
{ name: 'start-action-server', type: Boolean, defaultValue: false },
|
52
52
|
{ name: 'worker', type: Number, defaultValue: workerNum },
|
53
|
-
{ name: 'process-timeout', type: Number, defaultValue: 60 }
|
53
|
+
{ name: 'process-timeout', type: Number, defaultValue: 60 },
|
54
|
+
{ name: 'worker-timeout', type: Number, defaultValue: 60 },
|
55
|
+
{
|
56
|
+
name: 'enable-partition',
|
57
|
+
type: Boolean,
|
58
|
+
defaultValue: process.env['SENTIO_ENABLE_BINDING_DATA_PARTITION'] === 'true'
|
59
|
+
}
|
54
60
|
]
|
55
61
|
|
56
62
|
const options = commandLineArgs(optionDefinitions, { partial: true })
|
@@ -89,9 +95,9 @@ if (options['start-action-server']) {
|
|
89
95
|
.use(errorDetailsServerMiddleware)
|
90
96
|
|
91
97
|
if (options.worker > 1) {
|
92
|
-
baseService = new ServiceManager(
|
98
|
+
baseService = new ServiceManager(loader, options, server.shutdown)
|
93
99
|
} else {
|
94
|
-
baseService = new ProcessorServiceImpl(loader, server.shutdown)
|
100
|
+
baseService = new ProcessorServiceImpl(loader, options, server.shutdown)
|
95
101
|
}
|
96
102
|
|
97
103
|
const service = new FullProcessorServiceImpl(baseService)
|
package/src/service-manager.ts
CHANGED
@@ -1,12 +1,8 @@
|
|
1
|
-
import { CallContext
|
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,
|
@@ -15,17 +11,11 @@ import {
|
|
15
11
|
ProcessStreamResponse_Partitions,
|
16
12
|
StartRequest
|
17
13
|
} from '@sentio/protos'
|
18
|
-
|
19
|
-
import { IStoreContext } from './db-context.js'
|
20
14
|
import { Subject } from 'rxjs'
|
21
15
|
|
22
|
-
import { processMetrics } from './metrics.js'
|
23
16
|
import { MessageChannel } from 'node:worker_threads'
|
24
17
|
import { ProcessorServiceImpl } from './service.js'
|
25
18
|
import { TemplateInstanceState } from './state.js'
|
26
|
-
|
27
|
-
const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
|
28
|
-
|
29
19
|
;(BigInt.prototype as any).toJSON = function () {
|
30
20
|
return this.toString()
|
31
21
|
}
|
@@ -35,11 +25,11 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
35
25
|
private workerData: any = {}
|
36
26
|
|
37
27
|
constructor(
|
38
|
-
readonly options: any,
|
39
28
|
loader: () => Promise<any>,
|
29
|
+
readonly options: any,
|
40
30
|
shutdownHandler?: () => void
|
41
31
|
) {
|
42
|
-
super(loader, shutdownHandler)
|
32
|
+
super(loader, options, shutdownHandler)
|
43
33
|
this.workerData.options = options
|
44
34
|
}
|
45
35
|
|
@@ -77,110 +67,47 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
77
67
|
requests: AsyncIterable<ProcessStreamRequest>,
|
78
68
|
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
79
69
|
) {
|
80
|
-
|
70
|
+
if (!this.pool) {
|
71
|
+
await this.initPool()
|
72
|
+
}
|
81
73
|
for await (const request of requests) {
|
82
|
-
|
83
|
-
// console.debug('received request:', request)
|
84
|
-
if (request.binding) {
|
85
|
-
lastBinding = request.binding
|
86
|
-
process_binding_count.add(1)
|
87
|
-
// Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
|
88
|
-
// for older SDK version, so we just return empty result for them here
|
89
|
-
if (request.binding.handlerType === HandlerType.UNKNOWN) {
|
90
|
-
subject.next({
|
91
|
-
processId: request.processId,
|
92
|
-
result: ProcessResult.create()
|
93
|
-
})
|
94
|
-
continue
|
95
|
-
}
|
96
|
-
if (this.enablePartition) {
|
97
|
-
this.doPartition(request.processId, request.binding, subject)
|
98
|
-
} else {
|
99
|
-
this.doProcess(request.processId, request.binding, subject)
|
100
|
-
}
|
101
|
-
}
|
102
|
-
if (request.start) {
|
103
|
-
if (!lastBinding) {
|
104
|
-
throw new ServerError(Status.INVALID_ARGUMENT, 'start request received without binding')
|
105
|
-
}
|
106
|
-
this.doProcess(request.processId, lastBinding, subject)
|
107
|
-
}
|
108
|
-
if (request.dbResult) {
|
109
|
-
const dbContext = this.contexts.get(request.processId)
|
110
|
-
try {
|
111
|
-
dbContext?.result(request.dbResult)
|
112
|
-
} catch (e) {
|
113
|
-
subject.error(new Error('db result error, process should stop'))
|
114
|
-
}
|
115
|
-
}
|
116
|
-
} catch (e) {
|
117
|
-
// should not happen
|
118
|
-
console.error('unexpect error during handle loop', e)
|
119
|
-
}
|
74
|
+
this.handleSingleRequest(request, subject)
|
120
75
|
}
|
121
76
|
}
|
122
77
|
|
123
|
-
|
124
|
-
const
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
})
|
134
|
-
})
|
135
|
-
.catch((e) => {
|
136
|
-
dbContext.error(processId, e)
|
137
|
-
process_binding_error.add(1)
|
138
|
-
console.error('partition', processId, 'failed, took:', Date.now() - start)
|
139
|
-
})
|
140
|
-
.finally(() => {
|
141
|
-
const cost = Date.now() - start
|
142
|
-
process_binding_time.add(cost)
|
143
|
-
this.contexts.delete(processId)
|
144
|
-
})
|
145
|
-
}
|
146
|
-
|
147
|
-
private doProcess(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
|
148
|
-
const dbContext = this.contexts.new(processId, subject)
|
149
|
-
|
150
|
-
const start = Date.now()
|
151
|
-
this.process(binding, processId, dbContext, false)
|
152
|
-
.then(async (result) => {
|
153
|
-
console.debug('process', processId, 'finished, took:', Date.now() - start)
|
154
|
-
subject.next({
|
155
|
-
result: result as ProcessResult,
|
156
|
-
processId: processId
|
157
|
-
})
|
158
|
-
})
|
159
|
-
.catch((e) => {
|
160
|
-
dbContext.error(processId, e)
|
161
|
-
process_binding_error.add(1)
|
162
|
-
console.error('process', processId, 'failed, took:', Date.now() - start)
|
163
|
-
})
|
164
|
-
.finally(() => {
|
165
|
-
const cost = Date.now() - start
|
166
|
-
process_binding_time.add(cost)
|
167
|
-
this.contexts.delete(processId)
|
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)
|
87
|
+
}
|
168
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}`)
|
98
|
+
}
|
99
|
+
context.sendRequest(request)
|
100
|
+
}
|
169
101
|
}
|
170
102
|
|
171
|
-
async process(
|
172
|
-
request: DataBinding,
|
173
|
-
processId: number,
|
174
|
-
dbContext?: ChannelStoreContext,
|
175
|
-
partition: boolean = false
|
176
|
-
): Promise<ProcessResult | ProcessStreamResponse_Partitions> {
|
103
|
+
async process(processId: number, context: ChannelContext): Promise<ProcessResult | ProcessStreamResponse_Partitions> {
|
177
104
|
if (!this.pool) {
|
178
105
|
await this.initPool()
|
179
106
|
}
|
180
107
|
|
181
108
|
return this.pool.run(
|
182
|
-
{
|
183
|
-
{ transferList:
|
109
|
+
{ workerPort: context?.workerPort, processId },
|
110
|
+
{ transferList: context?.workerPort ? [context?.workerPort] : [] }
|
184
111
|
)
|
185
112
|
}
|
186
113
|
|
@@ -206,18 +133,18 @@ export class ServiceManager extends ProcessorServiceImpl {
|
|
206
133
|
}
|
207
134
|
|
208
135
|
class Contexts {
|
209
|
-
private contexts: Map<number,
|
136
|
+
private contexts: Map<number, ChannelContext> = new Map()
|
210
137
|
|
211
138
|
get(processId: number) {
|
212
139
|
return this.contexts.get(processId)
|
213
140
|
}
|
214
141
|
|
215
|
-
new(processId: number
|
142
|
+
new(processId: number) {
|
216
143
|
let context = this.get(processId)
|
217
144
|
if (context) {
|
218
145
|
return context
|
219
146
|
}
|
220
|
-
context = new
|
147
|
+
context = new ChannelContext(processId)
|
221
148
|
this.contexts.set(processId, context)
|
222
149
|
return context
|
223
150
|
}
|
@@ -227,25 +154,19 @@ class Contexts {
|
|
227
154
|
context?.close()
|
228
155
|
this.contexts.delete(processId)
|
229
156
|
}
|
157
|
+
|
158
|
+
has(processId: number) {
|
159
|
+
return this.contexts.has(processId)
|
160
|
+
}
|
230
161
|
}
|
231
162
|
|
232
|
-
export class
|
163
|
+
export class ChannelContext {
|
233
164
|
channel = new MessageChannel()
|
234
165
|
|
235
|
-
constructor(
|
236
|
-
readonly subject: Subject<DeepPartial<ProcessStreamResponse>>,
|
237
|
-
readonly processId: number
|
238
|
-
) {
|
239
|
-
this.mainPort.on('message', (req: ProcessStreamRequest) => {
|
240
|
-
subject.next({
|
241
|
-
...req,
|
242
|
-
processId: processId
|
243
|
-
})
|
244
|
-
})
|
245
|
-
}
|
166
|
+
constructor(readonly processId: number) {}
|
246
167
|
|
247
|
-
sendRequest(request:
|
248
|
-
|
168
|
+
sendRequest(request: ProcessStreamRequest) {
|
169
|
+
this.mainPort.postMessage(request)
|
249
170
|
}
|
250
171
|
|
251
172
|
get workerPort() {
|
@@ -256,25 +177,7 @@ export class ChannelStoreContext implements IStoreContext {
|
|
256
177
|
return this.channel.port1
|
257
178
|
}
|
258
179
|
|
259
|
-
result(dbResult: DBResponse) {
|
260
|
-
this.mainPort.postMessage(dbResult)
|
261
|
-
}
|
262
|
-
|
263
180
|
close(): void {
|
264
181
|
this.mainPort.close()
|
265
182
|
}
|
266
|
-
|
267
|
-
error(processId: number, e: any): void {
|
268
|
-
const stack = new Error().stack
|
269
|
-
console.error('process error', processId, e, stack)
|
270
|
-
const errorResult = ProcessResult.create({
|
271
|
-
states: {
|
272
|
-
error: e?.toString()
|
273
|
-
}
|
274
|
-
})
|
275
|
-
this.subject.next({
|
276
|
-
result: errorResult,
|
277
|
-
processId
|
278
|
-
})
|
279
|
-
}
|
280
183
|
}
|