@sentio/runtime 4.0.0-rc.1 → 4.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/chunk-OLNJTVP4.js +15167 -0
- package/lib/chunk-OLNJTVP4.js.map +1 -0
- package/lib/chunk-VVWSRCNO.js +4988 -0
- package/lib/chunk-VVWSRCNO.js.map +1 -0
- package/lib/index.d.ts +1407 -566
- package/lib/index.js +21290 -40
- package/lib/index.js.map +1 -1
- package/lib/plugin-D-rx1WCp.d.ts +108 -0
- package/lib/processor-runner.js +25516 -525
- package/lib/processor-runner.js.map +1 -1
- package/package.json +1 -1
- package/src/action-server.ts +3 -2
- package/src/db-context.ts +58 -45
- package/src/full-service.ts +18 -400
- package/src/gen/google/type/money_pb.ts +41 -0
- package/src/gen/processor/protos/processor_pb.ts +3855 -0
- package/src/gen/service/common/protos/common_pb.ts +4456 -0
- package/src/global-config.ts +3 -2
- package/src/index.ts +1 -1
- package/src/plugin.ts +26 -16
- package/src/processor-runner.ts +25 -35
- package/src/service-v3.ts +57 -59
- package/src/utils.ts +29 -12
- package/lib/chunk-GUQLAUIA.js +0 -67172
- package/lib/chunk-GUQLAUIA.js.map +0 -1
- package/lib/chunk-KOMGWSWU.js +0 -25311
- package/lib/chunk-KOMGWSWU.js.map +0 -1
- package/lib/processor-StqZovMW.d.ts +0 -554
- package/src/gen/google/protobuf/empty.ts +0 -71
- package/src/gen/google/protobuf/struct.ts +0 -561
- package/src/gen/google/protobuf/timestamp.ts +0 -113
- package/src/gen/google/type/money.ts +0 -134
- package/src/gen/processor/protos/processor.ts +0 -15354
- package/src/gen/service/common/protos/common.ts +0 -16525
- package/src/service.ts +0 -549
package/src/service.ts
DELETED
|
@@ -1,549 +0,0 @@
|
|
|
1
|
-
import { CallContext, ServerError, Status } from 'nice-grpc'
|
|
2
|
-
import { DebugInfo, RichServerError } from 'nice-grpc-error-details'
|
|
3
|
-
import { from } from 'ix/Ix.asynciterable'
|
|
4
|
-
import { withAbort } from 'ix/Ix.asynciterable.operators'
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
DataBinding,
|
|
8
|
-
DeepPartial,
|
|
9
|
-
Empty,
|
|
10
|
-
EthCallParam,
|
|
11
|
-
HandlerType,
|
|
12
|
-
PreparedData,
|
|
13
|
-
PreprocessResult,
|
|
14
|
-
PreprocessStreamRequest,
|
|
15
|
-
PreprocessStreamResponse,
|
|
16
|
-
ProcessBindingResponse,
|
|
17
|
-
ProcessBindingsRequest,
|
|
18
|
-
ProcessConfigRequest,
|
|
19
|
-
ProcessConfigResponse,
|
|
20
|
-
ProcessorServiceImplementation,
|
|
21
|
-
ProcessResult,
|
|
22
|
-
ProcessStreamRequest,
|
|
23
|
-
ProcessStreamResponse,
|
|
24
|
-
StartRequest
|
|
25
|
-
} from '@sentio/protos'
|
|
26
|
-
|
|
27
|
-
import { PluginManager } from './plugin.js'
|
|
28
|
-
import { errorString, makeEthCallKey, mergeProcessResults } from './utils.js'
|
|
29
|
-
import { freezeGlobalConfig, GLOBAL_CONFIG } from './global-config.js'
|
|
30
|
-
|
|
31
|
-
import { StoreContext } from './db-context.js'
|
|
32
|
-
import { Subject } from 'rxjs'
|
|
33
|
-
import { getProvider } from './provider.js'
|
|
34
|
-
import { EthChainId } from '@sentio/chain'
|
|
35
|
-
import { Provider } from 'ethers'
|
|
36
|
-
import { decodeMulticallResult, encodeMulticallData, getMulticallAddress, Multicall3Call } from './multicall.js'
|
|
37
|
-
|
|
38
|
-
import { processMetrics } from './metrics.js'
|
|
39
|
-
import { ProcessorRuntimeOptions } from './processor-runner-program.js'
|
|
40
|
-
|
|
41
|
-
const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
|
|
42
|
-
|
|
43
|
-
;(BigInt.prototype as any).toJSON = function () {
|
|
44
|
-
return this.toString()
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
48
|
-
private started = false
|
|
49
|
-
// When there is unhandled error, stop process and return unavailable error
|
|
50
|
-
unhandled: Error
|
|
51
|
-
// private processorConfig: ProcessConfigResponse
|
|
52
|
-
|
|
53
|
-
private readonly loader: () => Promise<any>
|
|
54
|
-
|
|
55
|
-
private readonly shutdownHandler?: () => void
|
|
56
|
-
|
|
57
|
-
private readonly enablePreprocess: boolean
|
|
58
|
-
|
|
59
|
-
private preparedData: PreparedData | undefined
|
|
60
|
-
readonly enablePartition: boolean
|
|
61
|
-
|
|
62
|
-
constructor(loader: () => Promise<any>, options?: ProcessorRuntimeOptions, shutdownHandler?: () => void) {
|
|
63
|
-
this.loader = loader
|
|
64
|
-
this.shutdownHandler = shutdownHandler
|
|
65
|
-
|
|
66
|
-
this.enablePreprocess = process.env['ENABLE_PREPROCESS']
|
|
67
|
-
? process.env['ENABLE_PREPROCESS'].toLowerCase() == 'true'
|
|
68
|
-
: false
|
|
69
|
-
|
|
70
|
-
this.enablePartition = options?.enablePartition == true
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {
|
|
74
|
-
if (!this.started) {
|
|
75
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
76
|
-
}
|
|
77
|
-
// if (!this.processorConfig) {
|
|
78
|
-
// throw new ServerError(Status.INTERNAL, 'Process config empty.')
|
|
79
|
-
// }
|
|
80
|
-
|
|
81
|
-
// Don't use .create to keep compatiblity
|
|
82
|
-
const newConfig = ProcessConfigResponse.fromPartial({})
|
|
83
|
-
await PluginManager.INSTANCE.configure(newConfig)
|
|
84
|
-
return newConfig
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
//
|
|
88
|
-
// async configure() {
|
|
89
|
-
// this.processorConfig = ProcessConfigResponse.fromPartial({})
|
|
90
|
-
// await PluginManager.INSTANCE.configure(this.processorConfig)
|
|
91
|
-
// }
|
|
92
|
-
|
|
93
|
-
async start(request: StartRequest, context: CallContext): Promise<Empty> {
|
|
94
|
-
if (this.started) {
|
|
95
|
-
return {}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
freezeGlobalConfig()
|
|
99
|
-
|
|
100
|
-
try {
|
|
101
|
-
// for (const plugin of ['@sentio/sdk', '@sentio/sdk/eth']) {
|
|
102
|
-
// try {
|
|
103
|
-
// await import(plugin)
|
|
104
|
-
// } catch (e) {
|
|
105
|
-
// console.error('Failed to load plugin: ', plugin)
|
|
106
|
-
// }
|
|
107
|
-
// }
|
|
108
|
-
//
|
|
109
|
-
// for (const plugin of ['@sentio/sdk/aptos', '@sentio/sdk/solana']) {
|
|
110
|
-
// try {
|
|
111
|
-
// await import(plugin)
|
|
112
|
-
// } catch (e) {}
|
|
113
|
-
// }
|
|
114
|
-
|
|
115
|
-
await this.loader()
|
|
116
|
-
} catch (e) {
|
|
117
|
-
throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
await PluginManager.INSTANCE.start(request)
|
|
121
|
-
|
|
122
|
-
// try {
|
|
123
|
-
// await this.configure()
|
|
124
|
-
// } catch (e) {
|
|
125
|
-
// throw new ServerError(Status.INTERNAL, 'Failed to start processor : ' + errorString(e))
|
|
126
|
-
// }
|
|
127
|
-
this.started = true
|
|
128
|
-
return {}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
async stop(request: Empty, context: CallContext): Promise<Empty> {
|
|
132
|
-
console.log('Server Shutting down in 5 seconds')
|
|
133
|
-
if (this.shutdownHandler) {
|
|
134
|
-
setTimeout(this.shutdownHandler, 5000)
|
|
135
|
-
}
|
|
136
|
-
return {}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
async processBindings(request: ProcessBindingsRequest, options?: CallContext): Promise<ProcessBindingResponse> {
|
|
140
|
-
const preparedData = this.enablePreprocess
|
|
141
|
-
? await this.preprocessBindings(request.bindings, {}, undefined, options)
|
|
142
|
-
: { ethCallResults: {} }
|
|
143
|
-
|
|
144
|
-
const promises = []
|
|
145
|
-
for (const binding of request.bindings) {
|
|
146
|
-
const promise = this.processBinding(binding, preparedData)
|
|
147
|
-
if (GLOBAL_CONFIG.execution.sequential) {
|
|
148
|
-
await promise
|
|
149
|
-
}
|
|
150
|
-
promises.push(promise)
|
|
151
|
-
}
|
|
152
|
-
let promise
|
|
153
|
-
try {
|
|
154
|
-
promise = await Promise.all(promises)
|
|
155
|
-
processMetrics.process_binding_count.add(request.bindings.length)
|
|
156
|
-
} catch (e) {
|
|
157
|
-
processMetrics.process_binding_error.add(request.bindings.length)
|
|
158
|
-
throw e
|
|
159
|
-
}
|
|
160
|
-
const result = mergeProcessResults(promise)
|
|
161
|
-
|
|
162
|
-
// let updated = false
|
|
163
|
-
// if (PluginManager.INSTANCE.stateDiff(this.processorConfig)) {
|
|
164
|
-
// await this.configure()
|
|
165
|
-
// updated = true
|
|
166
|
-
// }
|
|
167
|
-
|
|
168
|
-
return {
|
|
169
|
-
result
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
async preprocessBindings(
|
|
174
|
-
bindings: DataBinding[],
|
|
175
|
-
preprocessStore: { [k: string]: any },
|
|
176
|
-
dbContext?: StoreContext,
|
|
177
|
-
options?: CallContext
|
|
178
|
-
): Promise<PreparedData> {
|
|
179
|
-
// console.debug(`preprocessBindings start, bindings: ${bindings.length}`)
|
|
180
|
-
const promises = []
|
|
181
|
-
for (const binding of bindings) {
|
|
182
|
-
promises.push(this.preprocessBinding(binding, preprocessStore, dbContext, options))
|
|
183
|
-
}
|
|
184
|
-
let preprocessResults: PreprocessResult[]
|
|
185
|
-
try {
|
|
186
|
-
preprocessResults = await Promise.all(promises)
|
|
187
|
-
} catch (e) {
|
|
188
|
-
throw e
|
|
189
|
-
}
|
|
190
|
-
const groupedRequests = new Map<string, EthCallParam[]>()
|
|
191
|
-
const providers = new Map<string, Provider>()
|
|
192
|
-
for (const result of preprocessResults) {
|
|
193
|
-
for (const param of result.ethCallParams) {
|
|
194
|
-
const { chainId, blockTag } = param.context!
|
|
195
|
-
if (!providers.has(chainId)) {
|
|
196
|
-
providers.set(chainId, getProvider(chainId as EthChainId))
|
|
197
|
-
}
|
|
198
|
-
const key = [chainId, blockTag].join('|')
|
|
199
|
-
if (!groupedRequests.has(key)) {
|
|
200
|
-
groupedRequests.set(key, [])
|
|
201
|
-
}
|
|
202
|
-
groupedRequests.get(key)!.push(param)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const start = Date.now()
|
|
207
|
-
const MULTICALL_THRESHOLD = 1
|
|
208
|
-
const callPromises: Promise<[string, string]>[] = []
|
|
209
|
-
const multicallPromises: Promise<[string, string][]>[] = []
|
|
210
|
-
|
|
211
|
-
for (const params of groupedRequests.values()) {
|
|
212
|
-
const { chainId, blockTag } = params[0].context!
|
|
213
|
-
const multicallAddress = getMulticallAddress(chainId as EthChainId)
|
|
214
|
-
if (params.length <= MULTICALL_THRESHOLD || !multicallAddress) {
|
|
215
|
-
for (const param of params) {
|
|
216
|
-
callPromises.push(
|
|
217
|
-
providers
|
|
218
|
-
.get(chainId)!
|
|
219
|
-
.call({
|
|
220
|
-
to: param.context!.address,
|
|
221
|
-
data: param.calldata,
|
|
222
|
-
blockTag
|
|
223
|
-
})
|
|
224
|
-
.then((result) => [makeEthCallKey(param), result])
|
|
225
|
-
)
|
|
226
|
-
}
|
|
227
|
-
continue
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// construct multicalls
|
|
231
|
-
const CHUNK_SIZE = 128
|
|
232
|
-
for (let i = 0; i < params.length; i += CHUNK_SIZE) {
|
|
233
|
-
const chunk = params.slice(i, i + CHUNK_SIZE)
|
|
234
|
-
const calls: Multicall3Call[] = chunk.map((param) => ({
|
|
235
|
-
target: param.context!.address,
|
|
236
|
-
callData: param.calldata
|
|
237
|
-
}))
|
|
238
|
-
const data = encodeMulticallData(calls)
|
|
239
|
-
multicallPromises.push(
|
|
240
|
-
providers
|
|
241
|
-
.get(chainId)!
|
|
242
|
-
.call({
|
|
243
|
-
to: multicallAddress,
|
|
244
|
-
data: data,
|
|
245
|
-
blockTag
|
|
246
|
-
})
|
|
247
|
-
.then((raw) => {
|
|
248
|
-
const result = decodeMulticallResult(raw).returnData
|
|
249
|
-
if (result.length != chunk.length) {
|
|
250
|
-
throw new Error(`multicall result length mismatch, params: ${chunk.length}, result: ${result.length}`)
|
|
251
|
-
}
|
|
252
|
-
const ret: [string, string][] = []
|
|
253
|
-
for (let i = 0; i < chunk.length; i++) {
|
|
254
|
-
ret.push([makeEthCallKey(chunk[i]), result[i]])
|
|
255
|
-
}
|
|
256
|
-
return ret
|
|
257
|
-
})
|
|
258
|
-
)
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
let results: { [p: string]: string } = {}
|
|
263
|
-
try {
|
|
264
|
-
results = Object.fromEntries(await Promise.all(callPromises))
|
|
265
|
-
for (const multicallResult of await Promise.all(multicallPromises)) {
|
|
266
|
-
results = {
|
|
267
|
-
...results,
|
|
268
|
-
...Object.fromEntries(multicallResult)
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
} catch (e) {
|
|
272
|
-
console.error(`eth call error: ${e}`)
|
|
273
|
-
}
|
|
274
|
-
// console.debug(
|
|
275
|
-
// `${Object.keys(results).length} calls finished, actual calls: ${callPromises.length + multicallPromises.length}, elapsed: ${Date.now() - start}ms`
|
|
276
|
-
// )
|
|
277
|
-
return {
|
|
278
|
-
ethCallResults: results
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
async preprocessBinding(
|
|
283
|
-
request: DataBinding,
|
|
284
|
-
preprocessStore: { [k: string]: any },
|
|
285
|
-
dbContext?: StoreContext,
|
|
286
|
-
options?: CallContext
|
|
287
|
-
): Promise<PreprocessResult> {
|
|
288
|
-
if (!this.started) {
|
|
289
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
290
|
-
}
|
|
291
|
-
if (this.unhandled) {
|
|
292
|
-
throw new RichServerError(
|
|
293
|
-
Status.UNAVAILABLE,
|
|
294
|
-
'Unhandled exception/rejection in previous request: ' + errorString(this.unhandled),
|
|
295
|
-
[
|
|
296
|
-
DebugInfo.fromPartial({
|
|
297
|
-
detail: this.unhandled.message,
|
|
298
|
-
stackEntries: this.unhandled.stack?.split('\n')
|
|
299
|
-
})
|
|
300
|
-
]
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
return await PluginManager.INSTANCE.preprocessBinding(request, preprocessStore, dbContext)
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
async processBinding(
|
|
307
|
-
request: DataBinding,
|
|
308
|
-
preparedData: PreparedData | undefined,
|
|
309
|
-
options?: CallContext
|
|
310
|
-
): Promise<ProcessResult> {
|
|
311
|
-
if (!this.started) {
|
|
312
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
313
|
-
}
|
|
314
|
-
if (this.unhandled) {
|
|
315
|
-
throw new RichServerError(
|
|
316
|
-
Status.UNAVAILABLE,
|
|
317
|
-
'Unhandled exception/rejection in previous request: ' + errorString(this.unhandled),
|
|
318
|
-
[
|
|
319
|
-
DebugInfo.fromPartial({
|
|
320
|
-
detail: this.unhandled.message,
|
|
321
|
-
stackEntries: this.unhandled.stack?.split('\n')
|
|
322
|
-
})
|
|
323
|
-
]
|
|
324
|
-
)
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
const result = await PluginManager.INSTANCE.processBinding(
|
|
328
|
-
request,
|
|
329
|
-
preparedData,
|
|
330
|
-
PluginManager.INSTANCE.dbContextLocalStorage.getStore()
|
|
331
|
-
)
|
|
332
|
-
recordRuntimeInfo(result, request.handlerType)
|
|
333
|
-
return result
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
async *processBindingsStream(requests: AsyncIterable<ProcessStreamRequest>, context: CallContext) {
|
|
337
|
-
if (!this.started) {
|
|
338
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const subject = new Subject<DeepPartial<ProcessStreamResponse>>()
|
|
342
|
-
this.handleRequests(requests, subject)
|
|
343
|
-
.then(() => {
|
|
344
|
-
if (this.preparedData) {
|
|
345
|
-
this.preparedData = { ethCallResults: {} }
|
|
346
|
-
}
|
|
347
|
-
subject.complete()
|
|
348
|
-
})
|
|
349
|
-
.catch((e) => {
|
|
350
|
-
console.error(e)
|
|
351
|
-
subject.error(e)
|
|
352
|
-
})
|
|
353
|
-
yield* from(subject).pipe(withAbort(context.signal))
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
async handlePreprocessRequests(
|
|
357
|
-
requests: AsyncIterable<PreprocessStreamRequest>,
|
|
358
|
-
subject: Subject<DeepPartial<PreprocessStreamResponse>>
|
|
359
|
-
) {
|
|
360
|
-
const contexts = new Contexts()
|
|
361
|
-
const preprocessStore: { [k: string]: any } = {}
|
|
362
|
-
|
|
363
|
-
for await (const request of requests) {
|
|
364
|
-
try {
|
|
365
|
-
if (request.bindings) {
|
|
366
|
-
const bindings = request.bindings.bindings
|
|
367
|
-
const dbContext = contexts.new(request.processId, subject)
|
|
368
|
-
const start = Date.now()
|
|
369
|
-
this.preprocessBindings(bindings, preprocessStore, dbContext, undefined)
|
|
370
|
-
.then((preparedData) => {
|
|
371
|
-
// TODO maybe not proper to pass data in this way
|
|
372
|
-
this.preparedData = {
|
|
373
|
-
ethCallResults: {
|
|
374
|
-
...this.preparedData?.ethCallResults,
|
|
375
|
-
...preparedData.ethCallResults
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
subject.next({
|
|
379
|
-
processId: request.processId
|
|
380
|
-
})
|
|
381
|
-
})
|
|
382
|
-
.catch((e) => {
|
|
383
|
-
console.debug(e)
|
|
384
|
-
dbContext.error(request.processId, e)
|
|
385
|
-
})
|
|
386
|
-
.finally(() => {
|
|
387
|
-
const cost = Date.now() - start
|
|
388
|
-
console.debug('preprocessBinding', request.processId, ' took', cost, 'ms')
|
|
389
|
-
contexts.delete(request.processId)
|
|
390
|
-
})
|
|
391
|
-
}
|
|
392
|
-
if (request.dbResult) {
|
|
393
|
-
const dbContext = contexts.get(request.processId)
|
|
394
|
-
dbContext?.result(request.dbResult)
|
|
395
|
-
}
|
|
396
|
-
} catch (e) {
|
|
397
|
-
// should not happen
|
|
398
|
-
console.error('unexpect error during handle loop', e)
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
async *preprocessBindingsStream(requests: AsyncIterable<PreprocessStreamRequest>, context: CallContext) {
|
|
404
|
-
if (!this.started) {
|
|
405
|
-
throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
const subject = new Subject<DeepPartial<PreprocessStreamResponse>>()
|
|
409
|
-
this.handlePreprocessRequests(requests, subject)
|
|
410
|
-
.then(() => {
|
|
411
|
-
subject.complete()
|
|
412
|
-
})
|
|
413
|
-
.catch((e) => {
|
|
414
|
-
console.error(e)
|
|
415
|
-
subject.error(e)
|
|
416
|
-
})
|
|
417
|
-
yield* from(subject).pipe(withAbort(context.signal))
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
private dbContexts = new Contexts()
|
|
421
|
-
|
|
422
|
-
protected async handleRequests(
|
|
423
|
-
requests: AsyncIterable<ProcessStreamRequest>,
|
|
424
|
-
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
|
425
|
-
) {
|
|
426
|
-
let lastBinding: DataBinding | undefined = undefined
|
|
427
|
-
for await (const request of requests) {
|
|
428
|
-
try {
|
|
429
|
-
// console.log('received request:', request, 'lastBinding:', lastBinding)
|
|
430
|
-
if (request.binding) {
|
|
431
|
-
lastBinding = request.binding
|
|
432
|
-
}
|
|
433
|
-
this.handleRequest(request, lastBinding, subject)
|
|
434
|
-
} catch (e) {
|
|
435
|
-
// should not happen
|
|
436
|
-
console.error('unexpect error during handle loop', e)
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
async handleRequest(
|
|
442
|
-
request: ProcessStreamRequest,
|
|
443
|
-
lastBinding: DataBinding | undefined,
|
|
444
|
-
subject: Subject<DeepPartial<ProcessStreamResponse>>
|
|
445
|
-
) {
|
|
446
|
-
if (request.binding) {
|
|
447
|
-
process_binding_count.add(1)
|
|
448
|
-
|
|
449
|
-
// Adjust binding will make some request become invalid by setting UNKNOWN HandlerType
|
|
450
|
-
// for older SDK version, so we just return empty result for them here
|
|
451
|
-
if (request.binding.handlerType === HandlerType.UNKNOWN) {
|
|
452
|
-
subject.next({
|
|
453
|
-
processId: request.processId,
|
|
454
|
-
result: ProcessResult.create()
|
|
455
|
-
})
|
|
456
|
-
return
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
if (this.enablePartition) {
|
|
460
|
-
try {
|
|
461
|
-
const partitions = await PluginManager.INSTANCE.partition(request.binding)
|
|
462
|
-
subject.next({
|
|
463
|
-
processId: request.processId,
|
|
464
|
-
partitions
|
|
465
|
-
})
|
|
466
|
-
} catch (e) {
|
|
467
|
-
console.error('Partition error:', e)
|
|
468
|
-
subject.error(new Error('Partition error: ' + errorString(e)))
|
|
469
|
-
return
|
|
470
|
-
}
|
|
471
|
-
} else {
|
|
472
|
-
this.startProcess(request.processId, request.binding, subject)
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
if (request.start) {
|
|
477
|
-
if (!lastBinding) {
|
|
478
|
-
console.error('start request received without binding')
|
|
479
|
-
subject.error(new Error('start request received without binding'))
|
|
480
|
-
return
|
|
481
|
-
}
|
|
482
|
-
this.startProcess(request.processId, lastBinding, subject)
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
if (request.dbResult) {
|
|
486
|
-
const dbContext = this.dbContexts.get(request.processId)
|
|
487
|
-
try {
|
|
488
|
-
dbContext?.result(request.dbResult)
|
|
489
|
-
} catch (e) {
|
|
490
|
-
subject.error(new Error('db result error, process should stop'))
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
private startProcess(processId: number, binding: DataBinding, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
|
|
496
|
-
const dbContext = this.dbContexts.new(processId, subject)
|
|
497
|
-
const start = Date.now()
|
|
498
|
-
PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
|
|
499
|
-
.then(async (result) => {
|
|
500
|
-
// await all pending db requests
|
|
501
|
-
await dbContext.awaitPendings()
|
|
502
|
-
subject.next({
|
|
503
|
-
result,
|
|
504
|
-
processId: processId
|
|
505
|
-
})
|
|
506
|
-
recordRuntimeInfo(result, binding.handlerType)
|
|
507
|
-
})
|
|
508
|
-
.catch((e) => {
|
|
509
|
-
console.error(e, e.stack)
|
|
510
|
-
dbContext.error(processId, e)
|
|
511
|
-
process_binding_error.add(1)
|
|
512
|
-
})
|
|
513
|
-
.finally(() => {
|
|
514
|
-
const cost = Date.now() - start
|
|
515
|
-
process_binding_time.add(cost)
|
|
516
|
-
this.dbContexts.delete(processId)
|
|
517
|
-
})
|
|
518
|
-
}
|
|
519
|
-
}
|
|
520
|
-
|
|
521
|
-
export function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
|
|
522
|
-
for (const list of [results.gauges, results.counters, results.events, results.exports]) {
|
|
523
|
-
list.forEach((e) => {
|
|
524
|
-
e.runtimeInfo = {
|
|
525
|
-
from: handlerType
|
|
526
|
-
}
|
|
527
|
-
})
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
class Contexts {
|
|
532
|
-
private contexts: Map<number, StoreContext> = new Map()
|
|
533
|
-
|
|
534
|
-
get(processId: number) {
|
|
535
|
-
return this.contexts.get(processId)
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
new(processId: number, subject: Subject<DeepPartial<ProcessStreamResponse>>) {
|
|
539
|
-
const context = new StoreContext(subject, processId)
|
|
540
|
-
this.contexts.set(processId, context)
|
|
541
|
-
return context
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
delete(processId: number) {
|
|
545
|
-
const context = this.get(processId)
|
|
546
|
-
context?.close()
|
|
547
|
-
this.contexts.delete(processId)
|
|
548
|
-
}
|
|
549
|
-
}
|