@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.
@@ -1,11 +1,10 @@
1
- import { DataBinding } from '@sentio/protos';
1
+ import { ProcessStreamRequest } from '@sentio/protos';
2
2
  import { MessagePort } from 'worker_threads';
3
3
 
4
- declare function export_default({ request, processId, workerPort, partition }: {
5
- request: DataBinding;
4
+ declare function export_default({ processId, request: firstRequest, workerPort }: {
6
5
  processId: number;
7
- workerPort?: MessagePort;
8
- partition?: boolean;
9
- }): Promise<unknown>;
6
+ request: ProcessStreamRequest;
7
+ workerPort: MessagePort;
8
+ }): Promise<void>;
10
9
 
11
10
  export { export_default as default };
@@ -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-CPLWSUD7.js";
4
+ } from "./chunk-FDZMT76B.js";
5
5
  import {
6
- AbstractStoreContext,
6
+ ProcessorServiceImpl,
7
7
  configureEndpoints,
8
8
  errorString,
9
9
  freezeGlobalConfig,
10
- recordRuntimeInfo,
10
+ require_cjs,
11
11
  require_lib3 as require_lib,
12
12
  require_lib4 as require_lib2
13
- } from "./chunk-C4IDZGJI.js";
13
+ } from "./chunk-ELSE7PSY.js";
14
14
  import {
15
- PluginManager,
16
- ProcessConfigResponse,
17
15
  __toESM
18
- } from "./chunk-FO2V2K7T.js";
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
- async function getConfig(request, context) {
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
- await loader(options);
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 PluginManager.INSTANCE.start(request);
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
- workerPort,
76
- partition
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
- console.debug("Worker", threadId, "starting request ", processId);
108
- const now = Date.now();
109
- const timeoutPromise = new Promise((resolve, reject) => {
110
- setTimeout(
111
- () => {
112
- reject(
113
- new import_nice_grpc_error_details.RichServerError(
114
- import_nice_grpc.Status.DEADLINE_EXCEEDED,
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
- this.port.on("close", () => {
172
- this.close();
108
+ workerPort.on("message", (msg) => {
109
+ const request = msg;
110
+ service?.handleRequest(request, firstRequest.binding, subject);
173
111
  });
174
- }
175
- doSend(req) {
176
- this.port.postMessage(req);
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 DataBinding,\n DBResponse,\n DeepPartial,\n Empty,\n ProcessConfigRequest,\n ProcessConfigResponse,\n ProcessResult,\n ProcessStreamResponse,\n ProcessStreamResponse_Partitions,\n StartRequest\n} from '@sentio/protos'\nimport { CallContext, ServerError, Status } from 'nice-grpc'\nimport { PluginManager } from './plugin.js'\nimport { errorString } from './utils.js'\nimport { freezeGlobalConfig } from './global-config.js'\nimport { DebugInfo, RichServerError } from 'nice-grpc-error-details'\nimport { recordRuntimeInfo } 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 { AbstractStoreContext } from './db-context.js'\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\nasync function getConfig(request: ProcessConfigRequest, context?: CallContext): Promise<ProcessConfigResponse> {\n if (!started) {\n throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')\n }\n\n const newConfig = ProcessConfigResponse.fromPartial({})\n await PluginManager.INSTANCE.configure(newConfig)\n return newConfig\n}\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 getConfig(request)\n}\n\nasync function start(request: StartRequest, options: any): Promise<Empty> {\n if (started) {\n return {}\n }\n freezeGlobalConfig()\n\n try {\n await loader(options)\n } catch (e) {\n throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))\n }\n\n await PluginManager.INSTANCE.start(request)\n started = true\n return {}\n}\n\nexport default async function ({\n request,\n processId,\n workerPort,\n partition\n}: {\n request: DataBinding\n processId: number\n workerPort?: MessagePort\n partition?: boolean\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 getConfig(configRequest)\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 console.debug('Worker', threadId, 'starting request ', processId)\n const now = Date.now()\n const timeoutPromise = new Promise((resolve, reject) => {\n setTimeout(\n () => {\n reject(\n new RichServerError(\n Status.DEADLINE_EXCEEDED,\n `Request ${processId} timed out after ${options['request-timeout']} ms`\n )\n )\n },\n (options['process-timeout'] ?? 60) * 1000\n )\n })\n let resultPromise: Promise<ProcessResult | ProcessStreamResponse_Partitions>\n if (partition) {\n resultPromise = PluginManager.INSTANCE.partition(request)\n } else {\n resultPromise = PluginManager.INSTANCE.processBinding(\n request,\n undefined,\n workerPort ? new WorkerStoreContext(workerPort, processId) : undefined\n ).then((result) => {\n recordRuntimeInfo(result, request.handlerType)\n return result\n })\n }\n\n try {\n const result = await Promise.race([resultPromise, timeoutPromise])\n console.debug(\n 'worker',\n threadId,\n `finished ${partition ? 'partition' : 'process'} request`,\n processId,\n 'took',\n Date.now() - now\n )\n return result\n } catch (e) {\n console.error('worker', threadId, `failed ${partition ? 'partition' : 'process'} request`, processId, 'error:', e)\n if (e instanceof RichServerError) {\n throw e\n } else {\n throw new RichServerError(\n Status.INTERNAL,\n `Worker ${threadId} failed to process request ${processId}: ` + errorString(e),\n [\n DebugInfo.fromPartial({\n detail: errorString(e),\n stackEntries: e.stack?.split('\\n')\n })\n ]\n )\n }\n }\n}\n\nclass WorkerStoreContext extends AbstractStoreContext {\n constructor(\n readonly port: MessagePort,\n processId: number\n ) {\n super(processId)\n this.port.on('message', (resp: DBResponse) => {\n this.result(resp)\n })\n this.port.on('close', () => {\n this.close()\n })\n }\n\n doSend(req: DeepPartial<ProcessStreamResponse>): void {\n this.port.postMessage(req)\n }\n}\n;import(\"node:process\").then((p) => p.stdout.write(\"\"));"],"mappings":"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","names":[]}
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":";;;;;;;;AAGA,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;AAC/D;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":[]}
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentio/runtime",
3
- "version": "2.59.0-rc.39",
3
+ "version": "2.59.0-rc.40",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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 } // 60 seconds
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(options, loader, server.shutdown)
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)
@@ -1,12 +1,8 @@
1
- import { CallContext, ServerError, Status } from 'nice-grpc'
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
- let lastBinding: DataBinding | undefined = undefined
70
+ if (!this.pool) {
71
+ await this.initPool()
72
+ }
81
73
  for await (const request of requests) {
82
- try {
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
- private doPartition(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
124
- const dbContext = this.contexts.new(processId, subject)
125
- const start = Date.now()
126
-
127
- this.process(binding, processId, dbContext, true)
128
- .then(async (partitions) => {
129
- console.debug('partition', processId, 'finished, took:', Date.now() - start)
130
- subject.next({
131
- partitions: partitions as ProcessStreamResponse_Partitions,
132
- processId: processId
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
- { request, workerPort: dbContext?.workerPort, partition, processId },
183
- { transferList: dbContext?.workerPort ? [dbContext?.workerPort] : [] }
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, ChannelStoreContext> = new Map()
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, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
142
+ new(processId: number) {
216
143
  let context = this.get(processId)
217
144
  if (context) {
218
145
  return context
219
146
  }
220
- context = new ChannelStoreContext(subject, processId)
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 ChannelStoreContext implements IStoreContext {
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: DeepPartial<Omit<DBRequest, 'opId'>>, timeoutSecs?: number): Promise<DBResponse> {
248
- throw new Error('should not be used on main thread')
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
  }