@sentio/runtime 2.62.0-rc.7 → 2.62.0-rc.9

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.
@@ -10,7 +10,7 @@ import {
10
10
  require_cjs,
11
11
  require_lib3 as require_lib,
12
12
  require_lib4 as require_lib2
13
- } from "./chunk-YBKSM3GO.js";
13
+ } from "./chunk-AHIIUVQL.js";
14
14
  import "./chunk-I5YHR3CE.js";
15
15
  import "./chunk-W3VN25ER.js";
16
16
  import {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/service-worker.ts"],"sourcesContent":["import { DeepPartial, Empty, ProcessStreamRequest, ProcessStreamResponse, StartRequest } 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 { 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 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.logFormat === '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 const timeout = (options.workerTimeout || 0) * 1000 // convert to milliseconds\n const enablePartition = options.enablePartition || false\n await new Promise<void>((resolve, reject) => {\n const subject = new Subject<DeepPartial<ProcessStreamResponse>>()\n let timeoutId: NodeJS.Timeout | undefined = undefined\n subject.subscribe((resp: ProcessStreamResponse) => {\n console.debug('Worker', threadId, 'send response:', resp.result ? 'result' : 'dbResult')\n workerPort.postMessage(resp)\n // receive the response from the processor , close and resolve the promise\n if (resp.result) {\n if (timeoutId) clearTimeout(timeoutId)\n resolve()\n workerPort.close()\n }\n })\n workerPort.on('message', (msg: ProcessStreamRequest) => {\n const request = msg as ProcessStreamRequest\n console.debug('Worker', threadId, 'received request:', request.start ? 'start' : 'dbResult')\n service?.handleRequest(request, firstRequest.binding, subject)\n if (enablePartition && request.start && timeout > 0) {\n timeoutId = setTimeout(async () => {\n reject(new RichServerError(Status.DEADLINE_EXCEEDED, 'Worker timeout exceeded'))\n }, timeout)\n }\n })\n console.debug('Worker', threadId, 'handle request: binding')\n service?.handleRequest(firstRequest, firstRequest.binding, subject)\n if (!enablePartition && timeout > 0) {\n timeoutId = setTimeout(() => {\n reject(new RichServerError(Status.DEADLINE_EXCEEDED, 'Worker timeout exceeded'))\n }, timeout)\n }\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 { DeepPartial, Empty, ProcessStreamRequest, ProcessStreamResponse, StartRequest } 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 { MessagePort, threadId } from 'worker_threads'\nimport { Piscina } from 'piscina'\nimport { configureEndpoints } from './endpoints.js'\nimport { setupLogger } from './logger.js'\nimport { Subject } from 'rxjs'\nimport { ProcessorRuntimeOptions } from 'processor-runner-program.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\nlet service: ProcessorServiceImpl | undefined\n\nconst loader = async (options: ProcessorRuntimeOptions) => {\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 emptyCallContext = <CallContext>{}\n\nasync function start(request: StartRequest, options: ProcessorRuntimeOptions): 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.logFormat === '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 const timeout = (options.workerTimeout || 0) * 1000 // convert to milliseconds\n const enablePartition = options.enablePartition || false\n await new Promise<void>((resolve, reject) => {\n const subject = new Subject<DeepPartial<ProcessStreamResponse>>()\n let timeoutId: NodeJS.Timeout | undefined = undefined\n subject.subscribe((resp: ProcessStreamResponse) => {\n console.debug('Worker', threadId, 'send response:', resp.result ? 'result' : 'dbResult')\n workerPort.postMessage(resp)\n // receive the response from the processor , close and resolve the promise\n if (resp.result) {\n if (timeoutId) clearTimeout(timeoutId)\n resolve()\n workerPort.close()\n }\n })\n workerPort.on('message', (msg: ProcessStreamRequest) => {\n const request = msg as ProcessStreamRequest\n console.debug('Worker', threadId, 'received request:', request.start ? 'start' : 'dbResult')\n service?.handleRequest(request, firstRequest.binding, subject)\n if (enablePartition && request.start && timeout > 0) {\n timeoutId = setTimeout(async () => {\n reject(new RichServerError(Status.DEADLINE_EXCEEDED, 'Worker timeout exceeded'))\n }, timeout)\n }\n })\n console.debug('Worker', threadId, 'handle request: binding')\n service?.handleRequest(firstRequest, firstRequest.binding, subject)\n if (!enablePartition && timeout > 0) {\n timeoutId = setTimeout(() => {\n reject(new RichServerError(Status.DEADLINE_EXCEEDED, 'Worker timeout exceeded'))\n }, timeout)\n }\n })\n}\n;import(\"node:process\").then((p) => p.stdout.write(\"\"));"],"mappings":";;;;;;;;;;;;;;;;;;;;AACA,uBAAiD;AAGjD,qCAA2C;AAE3C,SAAsB,gBAAgB;AACtC,SAAS,eAAe;AAGxB,kBAAwB;AAGxB,IAAI,UAAU;AAEd,IAAI;AAEJ,QACG,GAAG,qBAAqB,CAAC,QAAQ;AAChC,UAAQ,MAAM,iEAAiE,GAAG;AAClF,cAAY;AACd,CAAC,EACA,GAAG,sBAAsB,CAAC,QAAQ,MAAM;AAEvC,MAAI,QAAQ,QAAQ,WAAW,6CAA6C,GAAG;AAC7E;AAAA,EACF;AACA,UAAQ,MAAM,6DAA6D,MAAM;AACjF,cAAY;AAEd,CAAC,EACA,GAAG,QAAQ,MAAM;AAChB,UAAQ,KAAK,oCAAoC,QAAQ;AAC3D,CAAC;AAEH,IAAI;AAEJ,IAAM,SAAS,OAAO,YAAqC;AACzD,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI,MAAM,OAAO,QAAQ;AAC/B,YAAQ,MAAM,wBAAwB,QAAQ,QAAQ,WAAW,CAAC;AAClE,WAAO;AAAA,EACT;AACF;AAEA,IAAM,mBAAgC,CAAC;AAEvC,eAAe,MAAM,SAAuB,SAAkD;AAC5F,MAAI,SAAS;AACX,WAAO,CAAC;AAAA,EACV;AACA,qBAAmB;AAEnB,MAAI;AACF,cAAU,IAAI,qBAAqB,MAAM,OAAO,OAAO,GAAG,OAAO;AAAA,EACnE,SAAS,GAAG;AACV,UAAM,IAAI,6BAAY,wBAAO,kBAAkB,+BAA+B,YAAY,CAAC,CAAC;AAAA,EAC9F;AAEA,QAAM,QAAQ,MAAM,SAAS,gBAAgB;AAC7C,YAAU;AACV,SAAO,CAAC;AACV;AAEA,eAAO,uBAAwB;AAAA,EAC7B;AAAA,EACA,SAAS;AAAA,EACT;AACF,GAIG;AACD,QAAM,EAAE,cAAc,eAAe,QAAQ,IAAI,QAAQ;AACzD,MAAI,CAAC,SAAS;AACZ,UAAM,WAAW,QAAQ,IAAI,WAAW,GAAG,YAAY;AACvD,gBAAY,QAAQ,cAAc,QAAQ,aAAa,UAAU,OAAO,QAAQ,OAAO,QAAQ;AAE/F,uBAAmB,OAAO;AAE1B,QAAI,cAAc;AAChB,YAAM,MAAM,cAAc,OAAO;AACjC,cAAQ,MAAM,UAAU,UAAU,gCAAgC,aAAa,mBAAmB,MAAM;AAAA,IAC1G;AAEA,QAAI,eAAe;AACjB,YAAM,SAAS,UAAU,eAAe,gBAAgB;AACxD,cAAQ,MAAM,UAAU,UAAU,aAAa;AAAA,IACjD;AAAA,EACF;AAEA,MAAI,WAAW;AACb,UAAM,MAAM;AACZ,gBAAY;AACZ,YAAQ,MAAM,sDAAsD,GAAG;AACvE,UAAM,IAAI;AAAA,MACR,wBAAO;AAAA,MACP,wDAAwD,YAAY,GAAG;AAAA,MACvE;AAAA,QACE,yCAAU,YAAY;AAAA,UACpB,QAAQ,IAAI;AAAA,UACZ,cAAc,IAAI,OAAO,MAAM,IAAI;AAAA,QACrC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACA,QAAM,WAAW,QAAQ,iBAAiB,KAAK;AAC/C,QAAM,kBAAkB,QAAQ,mBAAmB;AACnD,QAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAM,UAAU,IAAI,oBAA4C;AAChE,QAAI,YAAwC;AAC5C,YAAQ,UAAU,CAAC,SAAgC;AACjD,cAAQ,MAAM,UAAU,UAAU,kBAAkB,KAAK,SAAS,WAAW,UAAU;AACvF,iBAAW,YAAY,IAAI;AAE3B,UAAI,KAAK,QAAQ;AACf,YAAI,UAAW,cAAa,SAAS;AACrC,gBAAQ;AACR,mBAAW,MAAM;AAAA,MACnB;AAAA,IACF,CAAC;AACD,eAAW,GAAG,WAAW,CAAC,QAA8B;AACtD,YAAM,UAAU;AAChB,cAAQ,MAAM,UAAU,UAAU,qBAAqB,QAAQ,QAAQ,UAAU,UAAU;AAC3F,eAAS,cAAc,SAAS,aAAa,SAAS,OAAO;AAC7D,UAAI,mBAAmB,QAAQ,SAAS,UAAU,GAAG;AACnD,oBAAY,WAAW,YAAY;AACjC,iBAAO,IAAI,+CAAgB,wBAAO,mBAAmB,yBAAyB,CAAC;AAAA,QACjF,GAAG,OAAO;AAAA,MACZ;AAAA,IACF,CAAC;AACD,YAAQ,MAAM,UAAU,UAAU,yBAAyB;AAC3D,aAAS,cAAc,cAAc,aAAa,SAAS,OAAO;AAClE,QAAI,CAAC,mBAAmB,UAAU,GAAG;AACnC,kBAAY,WAAW,MAAM;AAC3B,eAAO,IAAI,+CAAgB,wBAAO,mBAAmB,yBAAyB,CAAC;AAAA,MACjF,GAAG,OAAO;AAAA,IACZ;AAAA,EACF,CAAC;AACH;AACC,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.62.0-rc.7",
3
+ "version": "2.62.0-rc.9",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
@@ -18,6 +18,7 @@
18
18
  "piscina": "5.1.3"
19
19
  },
20
20
  "devDependencies": {
21
+ "@commander-js/extra-typings": "^14.0.0",
21
22
  "@types/fs-extra": "^11.0.4"
22
23
  },
23
24
  "engines": {
@@ -31,7 +32,7 @@
31
32
  "run": "tsx src/processor-runner.ts --log-format=json",
32
33
  "run-benchmark": "tsx src/decode-benchmark.ts",
33
34
  "start_js": "tsx ./lib/processor-runner.js $PWD/../../debug/dist/lib.js",
34
- "start_ts": "tsx ./lib/processor-runner.js --log-format=json $PWD/../../debug/src/processor.ts",
35
+ "start_ts": "tsx src/processor-runner.ts --debug --chains-config chains-config.json $PWD/../../examples/x2y2/src/processor.ts",
35
36
  "test": "glob -c 'tsx --test' '**/*.test.ts'"
36
37
  }
37
38
  }
@@ -0,0 +1,57 @@
1
+ import { Command, InvalidArgumentError } from '@commander-js/extra-typings'
2
+
3
+ let workerNum = 1
4
+ try {
5
+ workerNum = parseInt(process.env['PROCESSOR_WORKER']?.trim() ?? '1')
6
+ } catch (e) {
7
+ console.error('Failed to parse worker number', e)
8
+ }
9
+
10
+ function myParseInt(value: string, dummyPrevious: number): number {
11
+ // parseInt takes a string and a radix
12
+ const parsedValue = parseInt(value, 10)
13
+ if (isNaN(parsedValue)) {
14
+ throw new InvalidArgumentError('Not a number.')
15
+ }
16
+ return parsedValue
17
+ }
18
+
19
+ export const program = new Command('processor-runner')
20
+ .allowUnknownOption()
21
+ .allowExcessArguments()
22
+ .name('processor-runner')
23
+ .description('Sentio Processor Runtime')
24
+ .argument('<target>', 'Path to the processor module to load')
25
+ .option('-p, --port <port>', 'Port to listen on', '4000')
26
+ .option('--concurrency <number>', 'Number of concurrent workers', myParseInt, 4)
27
+ .option('--batch-count <number>', 'Batch count for processing', myParseInt, 1)
28
+ .option('-c, --chains-config <path>', 'Path to chains configuration file', 'chains-config.json')
29
+ .option('--chainquery-server <url>', 'Chain query server URL')
30
+ .option('--pricefeed-server <url>', 'Price feed server URL')
31
+ .option('--log-format <format>', 'Log format (console|json)', 'console')
32
+ .option('--debug', 'Enable debug mode')
33
+ .option('--otlp-debug', 'Enable OTLP debug mode')
34
+ .option('--start-action-server', 'Start action server instead of processor server')
35
+ .option('--worker <number>', 'Number of worker threads', myParseInt, workerNum)
36
+ .option('--process-timeout <seconds>', 'Process timeout in seconds', myParseInt, 60)
37
+ .option(
38
+ '--worker-timeout <seconds>',
39
+ 'Worker timeout in seconds',
40
+ myParseInt,
41
+ parseInt(process.env['WORKER_TIMEOUT_SECONDS'] || '60')
42
+ )
43
+ .option(
44
+ '--enable-partition',
45
+ 'Enable binding data partition',
46
+ process.env['SENTIO_ENABLE_BINDING_DATA_PARTITION'] === 'true'
47
+ )
48
+
49
+ export type ProcessorRuntimeOptions = ReturnType<typeof program.opts> & { target: string }
50
+
51
+ export function getTestConfig(config?: Partial<ProcessorRuntimeOptions>): ProcessorRuntimeOptions {
52
+ return {
53
+ ...program.opts(),
54
+ target: './test-processor.test.js',
55
+ ...config
56
+ }
57
+ }
@@ -3,7 +3,7 @@
3
3
  import fs from 'fs-extra'
4
4
 
5
5
  import { compressionAlgorithms } from '@grpc/grpc-js'
6
- import { Command, InvalidArgumentError } from 'commander'
6
+
7
7
  import { createServer } from 'nice-grpc'
8
8
  import { errorDetailsServerMiddleware } from 'nice-grpc-error-details'
9
9
  // import { registry as niceGrpcRegistry } from 'nice-grpc-prometheus'
@@ -21,241 +21,178 @@ import { setupLogger } from './logger.js'
21
21
  import { setupOTLP } from './otlp.js'
22
22
  import { ActionServer } from './action-server.js'
23
23
  import { ServiceManager } from './service-manager.js'
24
- import path from 'path'
25
24
  import { ProcessorV3Definition } from '@sentio/protos'
26
25
  import { ProcessorServiceImplV3 } from './service-v3.js'
27
- import { readFileSync } from 'fs'
28
- import { fileURLToPath } from 'url'
29
26
  import { dirname, join } from 'path'
27
+ import { program, ProcessorRuntimeOptions } from 'processor-runner-program.js'
30
28
 
31
- const __filename = fileURLToPath(import.meta.url)
32
- const __dirname = dirname(__filename)
33
- const packageJson = JSON.parse(readFileSync(join(__dirname, '../package.json'), 'utf8'))
34
-
35
- // const mergedRegistry = Registry.merge([globalRegistry, niceGrpcRegistry])
36
-
37
- let workerNum = 1
38
- try {
39
- workerNum = parseInt(process.env['PROCESSOR_WORKER']?.trim() ?? '1')
40
- } catch (e) {
41
- console.error('Failed to parse worker number', e)
42
- }
29
+ program.parse()
43
30
 
44
- function myParseInt(value: string, dummyPrevious: unknown): number {
45
- // parseInt takes a string and a radix
46
- const parsedValue = parseInt(value, 10)
47
- if (isNaN(parsedValue)) {
48
- throw new InvalidArgumentError('Not a number.')
49
- }
50
- return parsedValue
31
+ const options: ProcessorRuntimeOptions = {
32
+ ...program.opts(),
33
+ target: program.args[program.args.length - 1]
51
34
  }
52
35
 
53
- // Create Commander.js program
54
- const program = new Command()
55
-
56
- program
57
- .allowUnknownOption()
58
- .allowExcessArguments()
59
- .name('processor-runner')
60
- .description('Sentio Processor Runtime')
61
- .version(packageJson.version)
62
- .option('--target <path>', 'Path to the processor module to load')
63
- .option('-p, --port <port>', 'Port to listen on', '4000')
64
- .option('--concurrency <number>', 'Number of concurrent workers', myParseInt, 4)
65
- .option('--batch-count <number>', 'Batch count for processing', myParseInt, 1)
66
- .option('-c, --chains-config <path>', 'Path to chains configuration file', 'chains-config.json')
67
- .option('--chainquery-server <url>', 'Chain query server URL', '')
68
- .option('--pricefeed-server <url>', 'Price feed server URL', '')
69
- .option('--log-format <format>', 'Log format (console|json)', 'console')
70
- .option('--debug', 'Enable debug mode', false)
71
- .option('--otlp-debug', 'Enable OTLP debug mode', false)
72
- .option('--start-action-server', 'Start action server instead of processor server', false)
73
- .option('--worker <number>', 'Number of worker threads', myParseInt, workerNum)
74
- .option('--process-timeout <seconds>', 'Process timeout in seconds', myParseInt, 60)
75
- .option(
76
- '--worker-timeout <seconds>',
77
- 'Worker timeout in seconds',
78
- myParseInt,
79
- parseInt(process.env['WORKER_TIMEOUT_SECONDS'] || '60')
80
- )
81
- .option(
82
- '--enable-partition',
83
- 'Enable binding data partition',
84
- process.env['SENTIO_ENABLE_BINDING_DATA_PARTITION'] === 'true'
85
- )
86
- .action(async (options: any) => {
87
- try {
88
- await startServer(options)
89
- } catch (error) {
90
- console.error('Failed to start server:', error)
91
- process.exit(1)
92
- }
93
- })
94
-
95
- // Parse arguments
96
- program.parse()
36
+ const logLevel = process.env['LOG_LEVEL']?.toLowerCase()
97
37
 
98
- async function startServer(options: any): Promise<void> {
99
- const logLevel = process.env['LOG_LEVEL']?.toLowerCase()
38
+ setupLogger(options.logFormat === 'json', logLevel === 'debug' ? true : options.debug!)
39
+ console.debug('Starting with', options.target)
100
40
 
101
- setupLogger(options.logFormat === 'json', logLevel === 'debug' ? true : options.debug)
102
- console.debug('Starting with', options.target)
41
+ await setupOTLP(options.otlpDebug)
103
42
 
104
- await setupOTLP(options.otlpDebug)
43
+ Error.stackTraceLimit = 20
105
44
 
106
- Error.stackTraceLimit = 20
45
+ configureEndpoints(options)
107
46
 
108
- configureEndpoints(options)
47
+ console.debug('Starting Server', options)
109
48
 
110
- console.debug('Starting Server', options)
49
+ let server: any
50
+ let baseService: ProcessorServiceImpl | ServiceManager
51
+ const loader = async () => {
52
+ const m = await import(options.target)
53
+ console.debug('Module loaded', m)
54
+ return m
55
+ }
56
+ if (options.startActionServer) {
57
+ server = new ActionServer(loader)
58
+ server.listen(options.port)
59
+ } else {
60
+ server = createServer({
61
+ 'grpc.max_send_message_length': 768 * 1024 * 1024,
62
+ 'grpc.max_receive_message_length': 768 * 1024 * 1024,
63
+ 'grpc.default_compression_algorithm': compressionAlgorithms.gzip
64
+ })
65
+ // .use(prometheusServerMiddleware())
66
+ .use(openTelemetryServerMiddleware())
67
+ .use(errorDetailsServerMiddleware)
111
68
 
112
- let server: any
113
- let baseService: ProcessorServiceImpl | ServiceManager
114
- const loader = async () => {
115
- const m = await import(options.target)
116
- console.debug('Module loaded', m)
117
- return m
118
- }
119
- if (options.startActionServer) {
120
- server = new ActionServer(loader)
121
- server.listen(options.port)
69
+ if (options.worker > 1) {
70
+ baseService = new ServiceManager(loader, options, server.shutdown)
122
71
  } else {
123
- server = createServer({
124
- 'grpc.max_send_message_length': 768 * 1024 * 1024,
125
- 'grpc.max_receive_message_length': 768 * 1024 * 1024,
126
- 'grpc.default_compression_algorithm': compressionAlgorithms.gzip
127
- })
128
- // .use(prometheusServerMiddleware())
129
- .use(openTelemetryServerMiddleware())
130
- .use(errorDetailsServerMiddleware)
131
-
132
- if (options.worker > 1) {
133
- baseService = new ServiceManager(loader, options, server.shutdown)
134
- } else {
135
- baseService = new ProcessorServiceImpl(loader, options, server.shutdown)
136
- }
137
-
138
- const service = new FullProcessorServiceImpl(baseService)
139
-
140
- server.add(ProcessorDefinition, service)
141
- server.add(
142
- ProcessorV3Definition,
143
- new FullProcessorServiceV3Impl(new ProcessorServiceImplV3(loader, options, server.shutdown))
144
- )
145
-
146
- server.listen('0.0.0.0:' + options.port)
147
- console.log('Processor Server Started at:', options.port)
72
+ baseService = new ProcessorServiceImpl(loader, options, server.shutdown)
148
73
  }
149
- const metricsPort = 4040
150
-
151
- const httpServer = http
152
- .createServer(async function (req, res) {
153
- if (req.url) {
154
- const reqUrl = new URL(req.url, `http://${req.headers.host}`)
155
- const queries = reqUrl.searchParams
156
- switch (reqUrl.pathname) {
157
- // case '/metrics':
158
- // const metrics = await mergedRegistry.metrics()
159
- // res.write(metrics)
160
- // break
161
- case '/heap': {
162
- try {
163
- const file = '/tmp/' + Date.now() + '.heapsnapshot'
164
- await dumpHeap(file)
165
- // send the file
166
- const readStream = fs.createReadStream(file)
167
- res.writeHead(200, { 'Content-Type': 'application/json' })
168
- readStream.pipe(res)
169
- res.end()
170
- } catch {
171
- res.writeHead(500)
172
- res.end()
173
- }
174
- break
175
- }
176
- case '/profile': {
177
- try {
178
- const profileTime = parseInt(queries.get('t') || '1000', 10) || 1000
179
- const session = new Session()
180
- session.connect()
181
74
 
182
- await session.post('Profiler.enable')
183
- await session.post('Profiler.start')
75
+ const service = new FullProcessorServiceImpl(baseService)
184
76
 
185
- await new Promise((resolve) => setTimeout(resolve, profileTime))
186
- const { profile } = await session.post('Profiler.stop')
77
+ server.add(ProcessorDefinition, service)
78
+ server.add(
79
+ ProcessorV3Definition,
80
+ new FullProcessorServiceV3Impl(new ProcessorServiceImplV3(loader, options, server.shutdown))
81
+ )
187
82
 
188
- res.writeHead(200, { 'Content-Type': 'application/json' })
189
- res.write(JSON.stringify(profile))
190
- session.disconnect()
191
- } catch {
192
- res.writeHead(500)
193
- }
194
- break
83
+ server.listen('0.0.0.0:' + options.port)
84
+ console.log('Processor Server Started at:', options.port)
85
+ }
86
+ const metricsPort = 4040
87
+
88
+ const httpServer = http
89
+ .createServer(async function (req, res) {
90
+ if (req.url) {
91
+ const reqUrl = new URL(req.url, `http://${req.headers.host}`)
92
+ const queries = reqUrl.searchParams
93
+ switch (reqUrl.pathname) {
94
+ // case '/metrics':
95
+ // const metrics = await mergedRegistry.metrics()
96
+ // res.write(metrics)
97
+ // break
98
+ case '/heap': {
99
+ try {
100
+ const file = '/tmp/' + Date.now() + '.heapsnapshot'
101
+ await dumpHeap(file)
102
+ // send the file
103
+ const readStream = fs.createReadStream(file)
104
+ res.writeHead(200, { 'Content-Type': 'application/json' })
105
+ readStream.pipe(res)
106
+ res.end()
107
+ } catch {
108
+ res.writeHead(500)
109
+ res.end()
195
110
  }
196
- default:
197
- res.writeHead(404)
111
+ break
198
112
  }
199
- } else {
200
- res.writeHead(404)
113
+ case '/profile': {
114
+ try {
115
+ const profileTime = parseInt(queries.get('t') || '1000', 10) || 1000
116
+ const session = new Session()
117
+ session.connect()
118
+
119
+ await session.post('Profiler.enable')
120
+ await session.post('Profiler.start')
121
+
122
+ await new Promise((resolve) => setTimeout(resolve, profileTime))
123
+ const { profile } = await session.post('Profiler.stop')
124
+
125
+ res.writeHead(200, { 'Content-Type': 'application/json' })
126
+ res.write(JSON.stringify(profile))
127
+ session.disconnect()
128
+ } catch {
129
+ res.writeHead(500)
130
+ }
131
+ break
132
+ }
133
+ default:
134
+ res.writeHead(404)
201
135
  }
202
- res.end()
203
- })
204
- .listen(metricsPort)
136
+ } else {
137
+ res.writeHead(404)
138
+ }
139
+ res.end()
140
+ })
141
+ .listen(metricsPort)
205
142
 
206
- console.log('Metric Server Started at:', metricsPort)
143
+ console.log('Metric Server Started at:', metricsPort)
207
144
 
208
- process
209
- .on('SIGINT', function () {
210
- shutdownServers(server, httpServer, 0)
211
- })
212
- .on('uncaughtException', (err) => {
213
- console.error('Uncaught Exception, please checking if await is properly used', err)
214
- if (baseService) {
215
- baseService.unhandled = err
216
- }
217
- // shutdownServers(1)
218
- })
219
- .on('unhandledRejection', (reason, p) => {
220
- // @ts-ignore ignore invalid ens error
221
- if (reason?.message.startsWith('invalid ENS name (disallowed character: "*"')) {
222
- return
223
- }
224
- console.error('Unhandled Rejection, please checking if await is properly', reason)
225
- if (baseService) {
226
- baseService.unhandled = reason as Error
227
- }
228
- // shutdownServers(1)
229
- })
145
+ process
146
+ .on('SIGINT', function () {
147
+ shutdownServers(0)
148
+ })
149
+ .on('uncaughtException', (err) => {
150
+ console.error('Uncaught Exception, please checking if await is properly used', err)
151
+ if (baseService) {
152
+ baseService.unhandled = err
153
+ }
154
+ // shutdownServers(1)
155
+ })
156
+ .on('unhandledRejection', (reason, p) => {
157
+ // @ts-ignore ignore invalid ens error
158
+ if (reason?.message.startsWith('invalid ENS name (disallowed character: "*"')) {
159
+ return
160
+ }
161
+ console.error('Unhandled Rejection, please checking if await is properly', reason)
162
+ if (baseService) {
163
+ baseService.unhandled = reason as Error
164
+ }
165
+ // shutdownServers(1)
166
+ })
230
167
 
231
- if (process.env['OOM_DUMP_MEMORY_SIZE_GB']) {
232
- let dumping = false
233
- const memorySize = parseFloat(process.env['OOM_DUMP_MEMORY_SIZE_GB']!)
234
- console.log('heap dumping is enabled, limit set to ', memorySize, 'gb')
235
- const dir = process.env['OOM_DUMP_DIR'] || '/tmp'
236
- setInterval(async () => {
237
- const mem = process.memoryUsage()
238
- console.log('Current Memory Usage', mem)
239
- // if memory usage is greater this size, dump heap and exit
240
- if (mem.heapTotal > memorySize * 1024 * 1024 * 1024 && !dumping) {
241
- const file = path.join(dir, `${Date.now()}.heapsnapshot`)
242
- dumping = true
243
- await dumpHeap(file)
244
- // force exit and keep pod running
245
- process.exit(11)
246
- }
247
- }, 1000 * 60)
248
- }
168
+ if (process.env['OOM_DUMP_MEMORY_SIZE_GB']) {
169
+ let dumping = false
170
+ const memorySize = parseFloat(process.env['OOM_DUMP_MEMORY_SIZE_GB']!)
171
+ console.log('heap dumping is enabled, limit set to ', memorySize, 'gb')
172
+ const dir = process.env['OOM_DUMP_DIR'] || '/tmp'
173
+ setInterval(async () => {
174
+ const mem = process.memoryUsage()
175
+ console.log('Current Memory Usage', mem)
176
+ // if memory usage is greater this size, dump heap and exit
177
+ if (mem.heapTotal > memorySize * 1024 * 1024 * 1024 && !dumping) {
178
+ const file = join(dir, `${Date.now()}.heapsnapshot`)
179
+ dumping = true
180
+ await dumpHeap(file)
181
+ // force exit and keep pod running
182
+ process.exit(11)
183
+ }
184
+ }, 1000 * 60)
249
185
  }
186
+ // }
250
187
 
251
188
  async function dumpHeap(file: string): Promise<void> {
252
189
  console.log('Heap dumping to', file)
253
190
  const session = new Session()
254
- fs.mkdirSync(path.dirname(file), { recursive: true })
191
+ fs.mkdirSync(dirname(file), { recursive: true })
255
192
  const fd = fs.openSync(file, 'w')
256
193
  try {
257
194
  session.connect()
258
- session.on('HeapProfiler.addHeapSnapshotChunk', (m) => {
195
+ session.on('HeapProfiler.addHeapSnapshotChunk', (m: any) => {
259
196
  fs.writeSync(fd, m.params.chunk)
260
197
  })
261
198
 
@@ -267,8 +204,8 @@ async function dumpHeap(file: string): Promise<void> {
267
204
  }
268
205
  }
269
206
 
270
- function shutdownServers(server: any, httpServer: any, exitCode: number): void {
271
- server?.forceShutdown()
207
+ function shutdownServers(exitCode: number): void {
208
+ server.forceShutdown()
272
209
  console.log('RPC server shut down')
273
210
 
274
211
  httpServer.close(function () {
@@ -16,6 +16,7 @@ import { Subject } from 'rxjs'
16
16
  import { MessageChannel } from 'node:worker_threads'
17
17
  import { ProcessorServiceImpl } from './service.js'
18
18
  import { TemplateInstanceState } from './state.js'
19
+ import { ProcessorRuntimeOptions } from 'processor-runner-program.js'
19
20
  ;(BigInt.prototype as any).toJSON = function () {
20
21
  return this.toString()
21
22
  }
@@ -26,7 +27,7 @@ export class ServiceManager extends ProcessorServiceImpl {
26
27
 
27
28
  constructor(
28
29
  loader: () => Promise<any>,
29
- readonly options: any,
30
+ readonly options: ProcessorRuntimeOptions,
30
31
  shutdownHandler?: () => void
31
32
  ) {
32
33
  super(loader, options, shutdownHandler)
@@ -123,7 +124,7 @@ export class ServiceManager extends ProcessorServiceImpl {
123
124
 
124
125
  if (this.enablePartition) {
125
126
  const concurrent = parseInt(process.env['PROCESS_CONCURRENCY'] || '0')
126
- if (this.options.worker < concurrent) {
127
+ if (this.options.worker! < concurrent) {
127
128
  console.warn(
128
129
  `When partition is enabled, the worker count must >= 'PROCESS_CONCURRENCY', will set worker count to ${concurrent})`
129
130
  )
package/src/service-v3.ts CHANGED
@@ -26,6 +26,7 @@ import { recordRuntimeInfo } from './service.js'
26
26
  import { DataBindingContext } from './db-context.js'
27
27
  import { TemplateInstanceState } from './state.js'
28
28
  import { freezeGlobalConfig } from './global-config.js'
29
+ import { ProcessorRuntimeOptions } from 'processor-runner-program.js'
29
30
 
30
31
  const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
31
32
 
@@ -35,11 +36,11 @@ export class ProcessorServiceImplV3 implements ProcessorV3ServiceImplementation
35
36
  private readonly shutdownHandler?: () => void
36
37
  private started = false
37
38
 
38
- constructor(loader: () => Promise<any>, options?: any, shutdownHandler?: () => void) {
39
+ constructor(loader: () => Promise<any>, options?: ProcessorRuntimeOptions, shutdownHandler?: () => void) {
39
40
  this.loader = loader
40
41
  this.shutdownHandler = shutdownHandler
41
42
 
42
- this.enablePartition = options?.['enable-partition'] == true
43
+ this.enablePartition = options?.enablePartition == true
43
44
  }
44
45
 
45
46
  async start(request: StartRequest, context: CallContext): Promise<Empty> {
@@ -9,6 +9,7 @@ import { Piscina } from 'piscina'
9
9
  import { configureEndpoints } from './endpoints.js'
10
10
  import { setupLogger } from './logger.js'
11
11
  import { Subject } from 'rxjs'
12
+ import { ProcessorRuntimeOptions } from 'processor-runner-program.js'
12
13
 
13
14
  let started = false
14
15
 
@@ -34,7 +35,7 @@ process
34
35
 
35
36
  let service: ProcessorServiceImpl | undefined
36
37
 
37
- const loader = async (options: any) => {
38
+ const loader = async (options: ProcessorRuntimeOptions) => {
38
39
  if (options.target) {
39
40
  const m = await import(options.target)
40
41
  console.debug('Module loaded, path:', options.target, 'module:', m)
@@ -44,7 +45,7 @@ const loader = async (options: any) => {
44
45
 
45
46
  const emptyCallContext = <CallContext>{}
46
47
 
47
- async function start(request: StartRequest, options: any): Promise<Empty> {
48
+ async function start(request: StartRequest, options: ProcessorRuntimeOptions): Promise<Empty> {
48
49
  if (started) {
49
50
  return {}
50
51
  }
package/src/service.ts CHANGED
@@ -36,6 +36,7 @@ import { Provider } from 'ethers'
36
36
  import { decodeMulticallResult, encodeMulticallData, getMulticallAddress, Multicall3Call } from './multicall.js'
37
37
 
38
38
  import { processMetrics } from './metrics.js'
39
+ import { ProcessorRuntimeOptions } from 'processor-runner-program.js'
39
40
 
40
41
  const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
41
42
 
@@ -58,7 +59,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
58
59
  private preparedData: PreparedData | undefined
59
60
  readonly enablePartition: boolean
60
61
 
61
- constructor(loader: () => Promise<any>, options?: any, shutdownHandler?: () => void) {
62
+ constructor(loader: () => Promise<any>, options?: ProcessorRuntimeOptions, shutdownHandler?: () => void) {
62
63
  this.loader = loader
63
64
  this.shutdownHandler = shutdownHandler
64
65
 
@@ -66,7 +67,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
66
67
  ? process.env['ENABLE_PREPROCESS'].toLowerCase() == 'true'
67
68
  : false
68
69
 
69
- this.enablePartition = options?.['enable-partition'] == true
70
+ this.enablePartition = options?.enablePartition == true
70
71
  }
71
72
 
72
73
  async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {