@sentio/runtime 2.59.0-rc.8 → 2.59.0

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/src/service.ts CHANGED
@@ -56,14 +56,17 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
56
56
  private readonly enablePreprocess: boolean
57
57
 
58
58
  private preparedData: PreparedData | undefined
59
+ readonly enablePartition: boolean
59
60
 
60
- constructor(loader: () => Promise<any>, shutdownHandler?: () => void) {
61
+ constructor(loader: () => Promise<any>, options?: any, shutdownHandler?: () => void) {
61
62
  this.loader = loader
62
63
  this.shutdownHandler = shutdownHandler
63
64
 
64
65
  this.enablePreprocess = process.env['ENABLE_PREPROCESS']
65
66
  ? process.env['ENABLE_PREPROCESS'].toLowerCase() == 'true'
66
67
  : false
68
+
69
+ this.enablePartition = options?.['enable-partition'] == true
67
70
  }
68
71
 
69
72
  async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {
@@ -413,65 +416,105 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
413
416
  yield* from(subject).pipe(withAbort(context.signal))
414
417
  }
415
418
 
419
+ private dbContexts = new Contexts()
420
+
416
421
  protected async handleRequests(
417
422
  requests: AsyncIterable<ProcessStreamRequest>,
418
423
  subject: Subject<DeepPartial<ProcessStreamResponse>>
419
424
  ) {
420
- const contexts = new Contexts()
425
+ let lastBinding: DataBinding | undefined = undefined
421
426
  for await (const request of requests) {
422
427
  try {
423
- // console.debug('received request:', request)
428
+ // console.log('received request:', request, 'lastBinding:', lastBinding)
424
429
  if (request.binding) {
425
- process_binding_count.add(1)
426
-
427
- // Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
428
- // for older SDK version, so we just return empty result for them here
429
- if (request.binding.handlerType === HandlerType.UNKNOWN) {
430
- subject.next({
431
- processId: request.processId,
432
- result: ProcessResult.create()
433
- })
434
- continue
435
- }
436
-
437
- const binding = request.binding
438
- const dbContext = contexts.new(request.processId, subject)
439
- const start = Date.now()
440
- PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
441
- .then(async (result) => {
442
- // await all pending db requests
443
- await dbContext.awaitPendings()
444
- subject.next({
445
- result,
446
- processId: request.processId
447
- })
448
- recordRuntimeInfo(result, binding.handlerType)
449
- })
450
- .catch((e) => {
451
- console.debug(e)
452
- dbContext.error(request.processId, e)
453
- process_binding_error.add(1)
454
- })
455
- .finally(() => {
456
- const cost = Date.now() - start
457
- process_binding_time.add(cost)
458
- contexts.delete(request.processId)
459
- })
460
- }
461
- if (request.dbResult) {
462
- const dbContext = contexts.get(request.processId)
463
- try {
464
- dbContext?.result(request.dbResult)
465
- } catch (e) {
466
- subject.error(new Error('db result error, process should stop'))
467
- }
430
+ lastBinding = request.binding
468
431
  }
432
+ this.handleRequest(request, lastBinding, subject)
469
433
  } catch (e) {
470
434
  // should not happen
471
435
  console.error('unexpect error during handle loop', e)
472
436
  }
473
437
  }
474
438
  }
439
+
440
+ async handleRequest(
441
+ request: ProcessStreamRequest,
442
+ lastBinding: DataBinding | undefined,
443
+ subject: Subject<DeepPartial<ProcessStreamResponse>>
444
+ ) {
445
+ if (request.binding) {
446
+ process_binding_count.add(1)
447
+
448
+ // Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
449
+ // for older SDK version, so we just return empty result for them here
450
+ if (request.binding.handlerType === HandlerType.UNKNOWN) {
451
+ subject.next({
452
+ processId: request.processId,
453
+ result: ProcessResult.create()
454
+ })
455
+ return
456
+ }
457
+
458
+ if (this.enablePartition) {
459
+ try {
460
+ const partitions = await PluginManager.INSTANCE.partition(request.binding)
461
+ subject.next({
462
+ processId: request.processId,
463
+ partitions
464
+ })
465
+ } catch (e) {
466
+ console.error('Partition error:', e)
467
+ subject.error(new Error('Partition error: ' + errorString(e)))
468
+ return
469
+ }
470
+ } else {
471
+ this.startProcess(request.processId, request.binding, subject)
472
+ }
473
+ }
474
+
475
+ if (request.start) {
476
+ if (!lastBinding) {
477
+ console.error('start request received without binding')
478
+ subject.error(new Error('start request received without binding'))
479
+ return
480
+ }
481
+ this.startProcess(request.processId, lastBinding, subject)
482
+ }
483
+
484
+ if (request.dbResult) {
485
+ const dbContext = this.dbContexts.get(request.processId)
486
+ try {
487
+ dbContext?.result(request.dbResult)
488
+ } catch (e) {
489
+ subject.error(new Error('db result error, process should stop'))
490
+ }
491
+ }
492
+ }
493
+
494
+ private startProcess(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
495
+ const dbContext = this.dbContexts.new(processId, subject)
496
+ const start = Date.now()
497
+ PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
498
+ .then(async (result) => {
499
+ // await all pending db requests
500
+ await dbContext.awaitPendings()
501
+ subject.next({
502
+ result,
503
+ processId: processId
504
+ })
505
+ recordRuntimeInfo(result, binding.handlerType)
506
+ })
507
+ .catch((e) => {
508
+ console.error(e)
509
+ dbContext.error(processId, e)
510
+ process_binding_error.add(1)
511
+ })
512
+ .finally(() => {
513
+ const cost = Date.now() - start
514
+ process_binding_time.add(cost)
515
+ this.dbContexts.delete(processId)
516
+ })
517
+ }
475
518
  }
476
519
 
477
520
  export function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
package/src/utils.ts CHANGED
@@ -10,12 +10,22 @@ export function mergeProcessResults(results: ProcessResult[]): Required<ProcessR
10
10
  ...ProcessResultFull.create(),
11
11
  states: StateResult.create()
12
12
  }
13
+ return mergeProcessResultsInPlace(res, results)
14
+ }
13
15
 
16
+ export function mergeProcessResultsInPlace(
17
+ res: ProcessResult,
18
+ results: ProcessResult[]
19
+ ): Required<ProcessResult, 'states'> {
20
+ res.states = res.states || StateResult.create()
14
21
  for (const r of results) {
15
- res.counters = res.counters.concat(r.counters)
16
- res.gauges = res.gauges.concat(r.gauges)
17
- res.events = res.events.concat(r.events)
18
- res.exports = res.exports.concat(r.exports)
22
+ // not using spread operator since it puts all element on the stack
23
+ // cause maximum call stack size exceeded error if it's a large array
24
+ mergeArrayInPlace(res.counters, r.counters)
25
+ mergeArrayInPlace(res.gauges, r.gauges)
26
+ mergeArrayInPlace(res.events, r.events)
27
+ mergeArrayInPlace(res.exports, r.exports)
28
+ mergeArrayInPlace(res.timeseriesResult, r.timeseriesResult)
19
29
  res.states = {
20
30
  configUpdated: res.states?.configUpdated || r.states?.configUpdated || false
21
31
  }
@@ -23,6 +33,12 @@ export function mergeProcessResults(results: ProcessResult[]): Required<ProcessR
23
33
  return res
24
34
  }
25
35
 
36
+ function mergeArrayInPlace<T>(dst: T[], src: T[]) {
37
+ for (const r of src) {
38
+ dst.push(r)
39
+ }
40
+ }
41
+
26
42
  export function errorString(e: Error): string {
27
43
  return e.message + '\n' + e.stack
28
44
  }