@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/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(request: DataBinding, dbContext?: StoreContext): Promise<PreprocessResult> {
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
  }
@@ -58,7 +58,9 @@ const optionDefinitions = [
58
58
 
59
59
  const options = commandLineArgs(optionDefinitions, { partial: true })
60
60
 
61
- setupLogger(options['log-format'] === 'json', options.debug)
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 { metrics } from '@opentelemetry/api'
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, Interface } from 'ethers'
37
-
38
- const meter = metrics.getMeter('processor_service')
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 ethCallResults = await this.preprocessBindings(request.bindings, undefined, options)
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, { ethCallResults })
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<{ [calldata: string]: any[] }> {
163
- console.log('preprocessBindings start')
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
- if (!providers.has(param.chainId)) {
183
- providers.set(param.chainId, getProvider(param.chainId as EthChainId))
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 = param.chainId + '|' + param.address
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
- console.log(`chain: ${params[0].chainId}, address: ${params[0].address}, totalCalls: ${params.length}`)
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(param.chainId)!
197
+ .get(chainId)!
203
198
  .call({
204
- to: param.address,
205
- data: calldata
199
+ to: address,
200
+ data: param.calldata,
201
+ blockTag
206
202
  })
207
- .then((ret) => [calldata, frag.decodeFunctionResult(param.function, ret).toArray()] as [string, any[]])
203
+ .then((result) => [makeEthCallKey(param), result])
208
204
  )
209
205
  }
210
206
  }
211
- const results = Object.fromEntries(await Promise.all(callPromises))
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 results
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, undefined, dbContext)
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
+ }