@sentio/runtime 2.40.0-rc.2 → 2.40.0-rc.21
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-WDKQZPD5.js → chunk-HF2KLDBY.js} +291 -134
- package/lib/index.d.ts +16 -8
- package/lib/index.js +3 -1
- package/lib/processor-runner.js +3 -2
- package/package.json +1 -1
- package/src/db-context.ts +15 -36
- package/src/gen/processor/protos/processor.ts +121 -76
- package/src/metrics.ts +138 -0
- package/src/plugin.ts +7 -3
- package/src/processor-runner.ts +3 -1
- package/src/provider.ts +4 -9
- package/src/service.ts +48 -36
- package/src/utils.ts +11 -3
package/src/plugin.ts
CHANGED
@@ -28,7 +28,7 @@ export abstract class Plugin {
|
|
28
28
|
return ProcessResult.create()
|
29
29
|
}
|
30
30
|
|
31
|
-
async preprocessBinding(request: DataBinding): Promise<PreprocessResult> {
|
31
|
+
async preprocessBinding(request: DataBinding, preprocessStore: { [k: string]: any }): Promise<PreprocessResult> {
|
32
32
|
return PreprocessResult.create()
|
33
33
|
}
|
34
34
|
}
|
@@ -84,13 +84,17 @@ export class PluginManager {
|
|
84
84
|
})
|
85
85
|
}
|
86
86
|
|
87
|
-
preprocessBinding(
|
87
|
+
preprocessBinding(
|
88
|
+
request: DataBinding,
|
89
|
+
preprocessStore: { [k: string]: any },
|
90
|
+
dbContext?: StoreContext
|
91
|
+
): Promise<PreprocessResult> {
|
88
92
|
const plugin = this.typesToPlugin.get(request.handlerType)
|
89
93
|
if (!plugin) {
|
90
94
|
throw new Error(`No plugin for ${request.handlerType}`)
|
91
95
|
}
|
92
96
|
return this.dbContextLocalStorage.run(dbContext, () => {
|
93
|
-
return plugin.preprocessBinding(request)
|
97
|
+
return plugin.preprocessBinding(request, preprocessStore)
|
94
98
|
})
|
95
99
|
}
|
96
100
|
}
|
package/src/processor-runner.ts
CHANGED
@@ -58,7 +58,9 @@ const optionDefinitions = [
|
|
58
58
|
|
59
59
|
const options = commandLineArgs(optionDefinitions, { partial: true })
|
60
60
|
|
61
|
-
|
61
|
+
const logLevel = process.env['LOG_LEVEL']?.toUpperCase()
|
62
|
+
|
63
|
+
setupLogger(options['log-format'] === 'json', logLevel === 'debug' ? true : options.debug)
|
62
64
|
console.debug('Starting with', options.target)
|
63
65
|
|
64
66
|
Error.stackTraceLimit = 20
|
package/src/provider.ts
CHANGED
@@ -4,17 +4,10 @@ import PQueue from 'p-queue'
|
|
4
4
|
import { Endpoints } from './endpoints.js'
|
5
5
|
import { EthChainId } from '@sentio/chain'
|
6
6
|
import { LRUCache } from 'lru-cache'
|
7
|
-
import {
|
8
|
-
|
7
|
+
import { providerMetrics } from './metrics.js'
|
8
|
+
const { miss_count, hit_count, total_duration, total_queued, queue_size } = providerMetrics
|
9
9
|
export const DummyProvider = new JsonRpcProvider('', Network.from(1))
|
10
10
|
|
11
|
-
const meter = metrics.getMeter('processor_provider')
|
12
|
-
const hit_count = meter.createCounter('provider_hit_count')
|
13
|
-
const miss_count = meter.createCounter('provider_miss_count')
|
14
|
-
const queue_size = meter.createGauge('provider_queue_size')
|
15
|
-
const total_duration = meter.createCounter('provider_total_duration')
|
16
|
-
const total_queued = meter.createCounter('provider_total_queued')
|
17
|
-
|
18
11
|
const providers = new Map<string, JsonRpcProvider>()
|
19
12
|
|
20
13
|
// export function getEthChainId(networkish?: EthContext | EthChainId): EthChainId {
|
@@ -37,6 +30,8 @@ export function getProvider(chainId?: EthChainId): Provider {
|
|
37
30
|
|
38
31
|
const address = Endpoints.INSTANCE.chainServer.get(chainId)
|
39
32
|
const key = network.chainId.toString() + '-' + address
|
33
|
+
|
34
|
+
console.debug(`init provider for ${chainId}, address: ${address}`)
|
40
35
|
let provider = providers.get(key)
|
41
36
|
|
42
37
|
if (provider) {
|
package/src/service.ts
CHANGED
@@ -25,20 +25,16 @@ import {
|
|
25
25
|
} from '@sentio/protos'
|
26
26
|
|
27
27
|
import { PluginManager } from './plugin.js'
|
28
|
-
import { errorString, mergeProcessResults } from './utils.js'
|
28
|
+
import { errorString, makeEthCallKey, mergeProcessResults } from './utils.js'
|
29
29
|
import { freezeGlobalConfig, GLOBAL_CONFIG } from './global-config.js'
|
30
30
|
|
31
31
|
import { StoreContext } from './db-context.js'
|
32
32
|
import { Subject } from 'rxjs'
|
33
|
-
import { metrics } from '@opentelemetry/api'
|
34
33
|
import { getProvider } from './provider.js'
|
35
34
|
import { EthChainId } from '@sentio/chain'
|
36
|
-
import { Provider
|
37
|
-
|
38
|
-
const
|
39
|
-
const process_binding_count = meter.createCounter('process_binding_count')
|
40
|
-
const process_binding_time = meter.createCounter('process_binding_time')
|
41
|
-
const process_binding_error = meter.createCounter('process_binding_error')
|
35
|
+
import { Provider } from 'ethers'
|
36
|
+
import { processMetrics, providerMetrics, dbMetrics } from './metrics.js'
|
37
|
+
const { process_binding_count, process_binding_time, process_binding_error } = processMetrics
|
42
38
|
|
43
39
|
;(BigInt.prototype as any).toJSON = function () {
|
44
40
|
return this.toString()
|
@@ -54,6 +50,8 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
54
50
|
|
55
51
|
private readonly shutdownHandler?: () => void
|
56
52
|
|
53
|
+
private preparedData: PreparedData | undefined
|
54
|
+
|
57
55
|
constructor(loader: () => Promise<any>, shutdownHandler?: () => void) {
|
58
56
|
this.loader = loader
|
59
57
|
this.shutdownHandler = shutdownHandler
|
@@ -126,11 +124,11 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
126
124
|
}
|
127
125
|
|
128
126
|
async processBindings(request: ProcessBindingsRequest, options?: CallContext): Promise<ProcessBindingResponse> {
|
129
|
-
const
|
127
|
+
const preparedData = await this.preprocessBindings(request.bindings, {}, undefined, options)
|
130
128
|
|
131
129
|
const promises = []
|
132
130
|
for (const binding of request.bindings) {
|
133
|
-
const promise = this.processBinding(binding,
|
131
|
+
const promise = this.processBinding(binding, preparedData)
|
134
132
|
if (GLOBAL_CONFIG.execution.sequential) {
|
135
133
|
await promise
|
136
134
|
}
|
@@ -157,13 +155,14 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
157
155
|
|
158
156
|
async preprocessBindings(
|
159
157
|
bindings: DataBinding[],
|
158
|
+
preprocessStore: { [k: string]: any },
|
160
159
|
dbContext?: StoreContext,
|
161
160
|
options?: CallContext
|
162
|
-
): Promise<
|
163
|
-
console.
|
161
|
+
): Promise<PreparedData> {
|
162
|
+
console.debug(`preprocessBindings start, bindings: ${bindings.length}`)
|
164
163
|
const promises = []
|
165
164
|
for (const binding of bindings) {
|
166
|
-
promises.push(this.preprocessBinding(binding, dbContext, options))
|
165
|
+
promises.push(this.preprocessBinding(binding, preprocessStore, dbContext, options))
|
167
166
|
}
|
168
167
|
let preprocessResults: PreprocessResult[]
|
169
168
|
try {
|
@@ -171,18 +170,15 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
171
170
|
} catch (e) {
|
172
171
|
throw e
|
173
172
|
}
|
174
|
-
console.log(
|
175
|
-
'ethCallParams: ',
|
176
|
-
preprocessResults.map((r) => r.ethCallParams)
|
177
|
-
)
|
178
173
|
const groupedRequests = new Map<string, EthCallParam[]>()
|
179
174
|
const providers = new Map<string, Provider>()
|
180
175
|
for (const result of preprocessResults) {
|
181
176
|
for (const param of result.ethCallParams) {
|
182
|
-
|
183
|
-
|
177
|
+
const { chainId, address, blockTag } = param.context!
|
178
|
+
if (!providers.has(chainId)) {
|
179
|
+
providers.set(chainId, getProvider(chainId as EthChainId))
|
184
180
|
}
|
185
|
-
const key =
|
181
|
+
const key = [chainId, address, blockTag].join('|')
|
186
182
|
if (!groupedRequests.has(key)) {
|
187
183
|
groupedRequests.set(key, [])
|
188
184
|
}
|
@@ -193,28 +189,36 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
193
189
|
const start = Date.now()
|
194
190
|
const callPromises = []
|
195
191
|
for (const params of groupedRequests.values()) {
|
196
|
-
|
192
|
+
const { chainId, address, blockTag } = params[0].context!
|
193
|
+
// TODO multicall
|
197
194
|
for (const param of params) {
|
198
|
-
const frag = new Interface(param.signature)
|
199
|
-
const calldata = frag.encodeFunctionData(param.function, param.args)
|
200
195
|
callPromises.push(
|
201
196
|
providers
|
202
|
-
.get(
|
197
|
+
.get(chainId)!
|
203
198
|
.call({
|
204
|
-
to:
|
205
|
-
data: calldata
|
199
|
+
to: address,
|
200
|
+
data: param.calldata,
|
201
|
+
blockTag
|
206
202
|
})
|
207
|
-
.then((
|
203
|
+
.then((result) => [makeEthCallKey(param), result])
|
208
204
|
)
|
209
205
|
}
|
210
206
|
}
|
211
|
-
|
207
|
+
let results: { [p: string]: string } = {}
|
208
|
+
try {
|
209
|
+
results = Object.fromEntries(await Promise.all(callPromises))
|
210
|
+
} catch (e) {
|
211
|
+
console.error(`eth call error: ${e}`)
|
212
|
+
}
|
212
213
|
console.log(`${callPromises.length} calls finished, elapsed: ${Date.now() - start}ms`)
|
213
|
-
return
|
214
|
+
return {
|
215
|
+
ethCallResults: results
|
216
|
+
}
|
214
217
|
}
|
215
218
|
|
216
219
|
async preprocessBinding(
|
217
220
|
request: DataBinding,
|
221
|
+
preprocessStore: { [k: string]: any },
|
218
222
|
dbContext?: StoreContext,
|
219
223
|
options?: CallContext
|
220
224
|
): Promise<PreprocessResult> {
|
@@ -233,7 +237,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
233
237
|
]
|
234
238
|
)
|
235
239
|
}
|
236
|
-
return await PluginManager.INSTANCE.preprocessBinding(request, dbContext)
|
240
|
+
return await PluginManager.INSTANCE.preprocessBinding(request, preprocessStore, dbContext)
|
237
241
|
}
|
238
242
|
|
239
243
|
async processBinding(
|
@@ -269,6 +273,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
269
273
|
const subject = new Subject<DeepPartial<ProcessStreamResponse>>()
|
270
274
|
this.handleRequests(requests, subject)
|
271
275
|
.then(() => {
|
276
|
+
this.preparedData = { ethCallResults: {} }
|
272
277
|
subject.complete()
|
273
278
|
})
|
274
279
|
.catch((e) => {
|
@@ -283,16 +288,23 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
283
288
|
subject: Subject<DeepPartial<PreprocessStreamResponse>>
|
284
289
|
) {
|
285
290
|
const contexts = new Contexts()
|
291
|
+
const preprocessStore: { [k: string]: any } = {}
|
286
292
|
|
287
293
|
for await (const request of requests) {
|
288
294
|
try {
|
289
|
-
console.debug('received request:', request)
|
290
295
|
if (request.bindings) {
|
291
296
|
const bindings = request.bindings.bindings
|
292
297
|
const dbContext = contexts.new(request.processId, subject)
|
293
298
|
const start = Date.now()
|
294
|
-
this.preprocessBindings(bindings, dbContext)
|
295
|
-
.then(() => {
|
299
|
+
this.preprocessBindings(bindings, preprocessStore, dbContext, undefined)
|
300
|
+
.then((preparedData) => {
|
301
|
+
// TODO maybe not proper to pass data in this way
|
302
|
+
this.preparedData = {
|
303
|
+
ethCallResults: {
|
304
|
+
...this.preparedData?.ethCallResults,
|
305
|
+
...preparedData.ethCallResults
|
306
|
+
}
|
307
|
+
}
|
296
308
|
subject.next({
|
297
309
|
processId: request.processId
|
298
310
|
})
|
@@ -300,12 +312,10 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
300
312
|
.catch((e) => {
|
301
313
|
console.debug(e)
|
302
314
|
dbContext.error(request.processId, e)
|
303
|
-
process_binding_error.add(1)
|
304
315
|
})
|
305
316
|
.finally(() => {
|
306
317
|
const cost = Date.now() - start
|
307
318
|
console.debug('preprocessBinding', request.processId, ' took', cost, 'ms')
|
308
|
-
process_binding_time.add(cost)
|
309
319
|
contexts.delete(request.processId)
|
310
320
|
})
|
311
321
|
}
|
@@ -351,7 +361,7 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
351
361
|
const binding = request.binding
|
352
362
|
const dbContext = contexts.new(request.processId, subject)
|
353
363
|
const start = Date.now()
|
354
|
-
PluginManager.INSTANCE.processBinding(binding,
|
364
|
+
PluginManager.INSTANCE.processBinding(binding, this.preparedData, dbContext)
|
355
365
|
.then((result) => {
|
356
366
|
subject.next({
|
357
367
|
result,
|
@@ -369,6 +379,8 @@ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
|
|
369
379
|
console.debug('processBinding', request.processId, ' took', cost, 'ms')
|
370
380
|
process_binding_time.add(cost)
|
371
381
|
contexts.delete(request.processId)
|
382
|
+
console.debug('db stats', JSON.stringify(dbMetrics.stats()))
|
383
|
+
console.debug('provider stats', JSON.stringify(providerMetrics.stats()))
|
372
384
|
})
|
373
385
|
}
|
374
386
|
if (request.dbResult) {
|
package/src/utils.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { ProcessResult } from '@sentio/protos'
|
1
|
+
import { EthCallParam, ProcessResult } from '@sentio/protos'
|
2
2
|
|
3
3
|
// TODO better handling this, because old proto doesn't have this
|
4
4
|
import { StateResult, ProcessResult as ProcessResultFull } from './gen/processor/protos/processor.js'
|
@@ -8,7 +8,7 @@ import { Required } from 'utility-types'
|
|
8
8
|
export function mergeProcessResults(results: ProcessResult[]): Required<ProcessResult, 'states'> {
|
9
9
|
const res = {
|
10
10
|
...ProcessResultFull.create(),
|
11
|
-
states: StateResult.create()
|
11
|
+
states: StateResult.create()
|
12
12
|
}
|
13
13
|
|
14
14
|
for (const r of results) {
|
@@ -17,7 +17,7 @@ export function mergeProcessResults(results: ProcessResult[]): Required<ProcessR
|
|
17
17
|
res.events = res.events.concat(r.events)
|
18
18
|
res.exports = res.exports.concat(r.exports)
|
19
19
|
res.states = {
|
20
|
-
configUpdated: res.states?.configUpdated || r.states?.configUpdated || false
|
20
|
+
configUpdated: res.states?.configUpdated || r.states?.configUpdated || false
|
21
21
|
}
|
22
22
|
}
|
23
23
|
return res
|
@@ -28,3 +28,11 @@ export function errorString(e: Error): string {
|
|
28
28
|
}
|
29
29
|
|
30
30
|
export const USER_PROCESSOR = 'user_processor'
|
31
|
+
|
32
|
+
export function makeEthCallKey(param: EthCallParam) {
|
33
|
+
if (!param.context) {
|
34
|
+
throw new Error('null context for eth call')
|
35
|
+
}
|
36
|
+
const { chainId, address, blockTag } = param.context
|
37
|
+
return `${chainId}|${address}|${blockTag}|${param.calldata}`.toLowerCase()
|
38
|
+
}
|