@sentio/runtime 2.59.0-rc.31 → 2.59.0-rc.33

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,11 @@
1
- import * as _sentio_protos from '@sentio/protos';
2
1
  import { DataBinding } from '@sentio/protos';
3
2
  import { MessagePort } from 'worker_threads';
4
3
 
5
- declare function export_default({ request, processId, workerPort }: {
4
+ declare function export_default({ request, processId, workerPort, partition }: {
6
5
  request: DataBinding;
7
6
  processId: number;
8
7
  workerPort?: MessagePort;
9
- }): Promise<_sentio_protos.ProcessResult>;
8
+ partition?: boolean;
9
+ }): Promise<unknown>;
10
10
 
11
11
  export { export_default as default };
@@ -10,7 +10,7 @@ import {
10
10
  recordRuntimeInfo,
11
11
  require_lib3 as require_lib,
12
12
  require_lib4 as require_lib2
13
- } from "./chunk-X2VTMTYL.js";
13
+ } from "./chunk-QWKQ7U4H.js";
14
14
  import {
15
15
  PluginManager,
16
16
  ProcessConfigResponse,
@@ -33,6 +33,8 @@ process.on("uncaughtException", (err) => {
33
33
  }
34
34
  console.error("Unhandled Rejection, please checking if await is properly", reason);
35
35
  unhandled = reason;
36
+ }).on("exit", () => {
37
+ console.info("Worker thread exiting, threadId:", threadId);
36
38
  });
37
39
  async function getConfig(request, context) {
38
40
  if (!started) {
@@ -70,7 +72,8 @@ async function start(request, options) {
70
72
  async function service_worker_default({
71
73
  request,
72
74
  processId,
73
- workerPort
75
+ workerPort,
76
+ partition
74
77
  }) {
75
78
  const { startRequest, configRequest, options } = Piscina.workerData;
76
79
  if (!started) {
@@ -79,16 +82,17 @@ async function service_worker_default({
79
82
  configureEndpoints(options);
80
83
  if (startRequest) {
81
84
  await start(startRequest, options);
82
- console.debug("worker started, template instance:", startRequest.templateInstances?.length);
85
+ console.debug("worker", threadId, " started, template instance:", startRequest.templateInstances?.length);
83
86
  }
84
87
  if (configRequest) {
85
88
  await getConfig(configRequest);
86
- console.debug("worker configured");
89
+ console.debug("worker", threadId, " configured");
87
90
  }
88
91
  }
89
92
  if (unhandled) {
90
93
  const err = unhandled;
91
94
  unhandled = void 0;
95
+ console.error("Unhandled exception/rejection in previous request:", err);
92
96
  throw new import_nice_grpc_error_details.RichServerError(
93
97
  import_nice_grpc.Status.UNAVAILABLE,
94
98
  "Unhandled exception/rejection in previous request: " + errorString(err),
@@ -100,13 +104,62 @@ async function service_worker_default({
100
104
  ]
101
105
  );
102
106
  }
103
- const result = await PluginManager.INSTANCE.processBinding(
104
- request,
105
- void 0,
106
- workerPort ? new WorkerStoreContext(workerPort, processId) : void 0
107
- );
108
- recordRuntimeInfo(result, request.handlerType);
109
- return result;
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
+ }
110
163
  }
111
164
  var WorkerStoreContext = class extends AbstractStoreContext {
112
165
  constructor(port, processId) {
@@ -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 ProcessStreamResponse,\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\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}: {\n request: DataBinding\n processId: number\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 started, template instance:', startRequest.templateInstances?.length)\n }\n\n if (configRequest) {\n await getConfig(configRequest)\n console.debug('worker configured')\n }\n }\n\n if (unhandled) {\n const err = unhandled\n unhandled = undefined\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 const result = await PluginManager.INSTANCE.processBinding(\n request,\n undefined,\n workerPort ? new WorkerStoreContext(workerPort, processId) : undefined\n )\n recordRuntimeInfo(result, request.handlerType)\n return result\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 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":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentio/runtime",
3
- "version": "2.59.0-rc.31",
3
+ "version": "2.59.0-rc.33",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "exports": {
package/src/db-context.ts CHANGED
@@ -95,9 +95,6 @@ export abstract class AbstractStoreContext implements IStoreContext {
95
95
 
96
96
  return Promise.race(promises)
97
97
  .then((result: DBResponse) => {
98
- if (timeoutSecs) {
99
- console.debug('db request', requestType, 'op', opId, ' took', Date.now() - start, 'ms')
100
- }
101
98
  request_times[requestType]?.add(Date.now() - start)
102
99
  return result
103
100
  })
@@ -49,7 +49,8 @@ export const optionDefinitions = [
49
49
  { name: 'debug', type: Boolean, defaultValue: false },
50
50
  { name: 'otlp-debug', type: Boolean, defaultValue: false },
51
51
  { name: 'start-action-server', type: Boolean, defaultValue: false },
52
- { name: 'worker', type: Number, defaultValue: workerNum }
52
+ { name: 'worker', type: Number, defaultValue: workerNum },
53
+ { name: 'process-timeout', type: Number, defaultValue: 60 } // 60 seconds
53
54
  ]
54
55
 
55
56
  const options = commandLineArgs(optionDefinitions, { partial: true })
@@ -22,7 +22,6 @@ import { processMetrics } from './metrics.js'
22
22
  import { MessageChannel } from 'node:worker_threads'
23
23
  import { ProcessorServiceImpl } from './service.js'
24
24
  import { TemplateInstanceState } from './state.js'
25
- import { PluginManager } from './plugin.js'
26
25
 
27
26
  const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
28
27
 
@@ -82,8 +81,8 @@ export class ServiceManager extends ProcessorServiceImpl {
82
81
  try {
83
82
  // console.debug('received request:', request)
84
83
  if (request.binding) {
84
+ lastBinding = request.binding
85
85
  process_binding_count.add(1)
86
-
87
86
  // Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
88
87
  // for older SDK version, so we just return empty result for them here
89
88
  if (request.binding.handlerType === HandlerType.UNKNOWN) {
@@ -93,15 +92,8 @@ export class ServiceManager extends ProcessorServiceImpl {
93
92
  })
94
93
  continue
95
94
  }
96
-
97
95
  if (this.enablePartition) {
98
- PluginManager.INSTANCE.partition(request.binding).then((partitions) => {
99
- subject.next({
100
- processId: request.processId,
101
- partitions
102
- })
103
- })
104
- lastBinding = request.binding
96
+ this.doPartition(request.processId, request.binding, subject)
105
97
  } else {
106
98
  this.doProcess(request.processId, request.binding, subject)
107
99
  }
@@ -127,12 +119,44 @@ export class ServiceManager extends ProcessorServiceImpl {
127
119
  }
128
120
  }
129
121
 
122
+ private async doPartition(
123
+ processId: number,
124
+ binding: DataBinding,
125
+ subject: Subject<DeepPartial<ProcessStreamResponse>>
126
+ ) {
127
+ if (!this.pool) {
128
+ await this.initPool()
129
+ }
130
+ const dbContext = this.contexts.new(processId, subject)
131
+ const start = Date.now()
132
+
133
+ this.process(binding, processId, dbContext, true)
134
+ .then(async (result) => {
135
+ console.debug('partition', processId, 'finished, took:', Date.now() - start)
136
+ subject.next({
137
+ result,
138
+ processId: processId
139
+ })
140
+ })
141
+ .catch((e) => {
142
+ dbContext.error(processId, e)
143
+ process_binding_error.add(1)
144
+ console.error('partition', processId, 'failed, took:', Date.now() - start)
145
+ })
146
+ .finally(() => {
147
+ const cost = Date.now() - start
148
+ process_binding_time.add(cost)
149
+ this.contexts.delete(processId)
150
+ })
151
+ }
152
+
130
153
  private doProcess(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
131
154
  const dbContext = this.contexts.new(processId, subject)
132
155
 
133
156
  const start = Date.now()
134
- this.process(binding, dbContext)
157
+ this.process(binding, processId, dbContext, false)
135
158
  .then(async (result) => {
159
+ console.debug('process', processId, 'finished, took:', Date.now() - start)
136
160
  subject.next({
137
161
  result,
138
162
  processId: processId
@@ -141,6 +165,7 @@ export class ServiceManager extends ProcessorServiceImpl {
141
165
  .catch((e) => {
142
166
  dbContext.error(processId, e)
143
167
  process_binding_error.add(1)
168
+ console.error('process', processId, 'failed, took:', Date.now() - start)
144
169
  })
145
170
  .finally(() => {
146
171
  const cost = Date.now() - start
@@ -149,13 +174,18 @@ export class ServiceManager extends ProcessorServiceImpl {
149
174
  })
150
175
  }
151
176
 
152
- async process(request: DataBinding, dbContext?: ChannelStoreContext): Promise<ProcessResult> {
177
+ async process(
178
+ request: DataBinding,
179
+ processId: number,
180
+ dbContext?: ChannelStoreContext,
181
+ partition: boolean = false
182
+ ): Promise<ProcessResult> {
153
183
  if (!this.pool) {
154
184
  await this.initPool()
155
185
  }
156
186
 
157
187
  return this.pool.run(
158
- { request, workerPort: dbContext?.workerPort },
188
+ { request, workerPort: dbContext?.workerPort, partition, processId },
159
189
  { transferList: dbContext?.workerPort ? [dbContext?.workerPort] : [] }
160
190
  )
161
191
  }
@@ -181,8 +211,6 @@ export class ServiceManager extends ProcessorServiceImpl {
181
211
  }
182
212
  }
183
213
 
184
- export type WorkerMessage = DBRequest & { processId: number }
185
-
186
214
  class Contexts {
187
215
  private contexts: Map<number, ChannelStoreContext> = new Map()
188
216
 
@@ -191,7 +219,11 @@ class Contexts {
191
219
  }
192
220
 
193
221
  new(processId: number, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
194
- const context = new ChannelStoreContext(subject, processId)
222
+ let context = this.get(processId)
223
+ if (context) {
224
+ return context
225
+ }
226
+ context = new ChannelStoreContext(subject, processId)
195
227
  this.contexts.set(processId, context)
196
228
  return context
197
229
  }
@@ -5,7 +5,9 @@ import {
5
5
  Empty,
6
6
  ProcessConfigRequest,
7
7
  ProcessConfigResponse,
8
+ ProcessResult,
8
9
  ProcessStreamResponse,
10
+ ProcessStreamResponse_Partitions,
9
11
  StartRequest
10
12
  } from '@sentio/protos'
11
13
  import { CallContext, ServerError, Status } from 'nice-grpc'
@@ -38,6 +40,9 @@ process
38
40
  unhandled = reason as Error
39
41
  // shutdownServers(1)
40
42
  })
43
+ .on('exit', () => {
44
+ console.info('Worker thread exiting, threadId:', threadId)
45
+ })
41
46
 
42
47
  async function getConfig(request: ProcessConfigRequest, context?: CallContext): Promise<ProcessConfigResponse> {
43
48
  if (!started) {
@@ -82,11 +87,13 @@ async function start(request: StartRequest, options: any): Promise<Empty> {
82
87
  export default async function ({
83
88
  request,
84
89
  processId,
85
- workerPort
90
+ workerPort,
91
+ partition
86
92
  }: {
87
93
  request: DataBinding
88
94
  processId: number
89
95
  workerPort?: MessagePort
96
+ partition?: boolean
90
97
  }) {
91
98
  const { startRequest, configRequest, options } = Piscina.workerData
92
99
  if (!started) {
@@ -97,18 +104,19 @@ export default async function ({
97
104
 
98
105
  if (startRequest) {
99
106
  await start(startRequest, options)
100
- console.debug('worker started, template instance:', startRequest.templateInstances?.length)
107
+ console.debug('worker', threadId, ' started, template instance:', startRequest.templateInstances?.length)
101
108
  }
102
109
 
103
110
  if (configRequest) {
104
111
  await getConfig(configRequest)
105
- console.debug('worker configured')
112
+ console.debug('worker', threadId, ' configured')
106
113
  }
107
114
  }
108
115
 
109
116
  if (unhandled) {
110
117
  const err = unhandled
111
118
  unhandled = undefined
119
+ console.error('Unhandled exception/rejection in previous request:', err)
112
120
  throw new RichServerError(
113
121
  Status.UNAVAILABLE,
114
122
  'Unhandled exception/rejection in previous request: ' + errorString(err),
@@ -121,13 +129,63 @@ export default async function ({
121
129
  )
122
130
  }
123
131
 
124
- const result = await PluginManager.INSTANCE.processBinding(
125
- request,
126
- undefined,
127
- workerPort ? new WorkerStoreContext(workerPort, processId) : undefined
128
- )
129
- recordRuntimeInfo(result, request.handlerType)
130
- return result
132
+ console.debug('Worker', threadId, 'starting request ', processId)
133
+ const now = Date.now()
134
+ const timeoutPromise = new Promise((resolve, reject) => {
135
+ setTimeout(
136
+ () => {
137
+ reject(
138
+ new RichServerError(
139
+ Status.DEADLINE_EXCEEDED,
140
+ `Request ${processId} timed out after ${options['request-timeout']} ms`
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
158
+ })
159
+ }
160
+
161
+ try {
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
+ }
131
189
  }
132
190
 
133
191
  class WorkerStoreContext extends AbstractStoreContext {