envio 2.28.0-alpha.3 → 2.28.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.
@@ -2,6 +2,7 @@
2
2
  'use strict';
3
3
 
4
4
  var Pino = require("./bindings/Pino.res.js");
5
+ var Utils = require("./Utils.res.js");
5
6
  var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js");
6
7
 
7
8
  function make(intervalMillis, logger) {
@@ -38,7 +39,7 @@ async function startInternal(throttler) {
38
39
  }
39
40
  catch (raw_exn){
40
41
  var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
41
- throttler.logger.error(Pino.createPinoMessageWithError("Scheduled action failed in throttler", exn));
42
+ throttler.logger.error(Pino.createPinoMessageWithError("Scheduled action failed in throttler", Utils.prettifyExn(exn)));
42
43
  }
43
44
  throttler.isRunning = false;
44
45
  return await startInternal(throttler);
package/src/Utils.res CHANGED
@@ -130,6 +130,13 @@ module Dict = {
130
130
  let shallowCopy: dict<'a> => dict<'a> = %raw(`(dict) => ({...dict})`)
131
131
 
132
132
  let size = dict => dict->Js.Dict.keys->Js.Array2.length
133
+
134
+ @set_index
135
+ external setByInt: (dict<'a>, int, 'a) => unit = ""
136
+
137
+ let incrementByInt: (dict<int>, int) => unit = %raw(`(dict, key) => {
138
+ dict[key]++
139
+ }`)
133
140
  }
134
141
 
135
142
  module Math = {
package/src/Utils.res.js CHANGED
@@ -130,6 +130,10 @@ function size(dict) {
130
130
  return Object.keys(dict).length;
131
131
  }
132
132
 
133
+ var incrementByInt = ((dict, key) => {
134
+ dict[key]++
135
+ });
136
+
133
137
  var Dict = {
134
138
  has: has,
135
139
  push: push,
@@ -141,7 +145,8 @@ var Dict = {
141
145
  deleteInPlace: deleteInPlace,
142
146
  updateImmutable: updateImmutable,
143
147
  shallowCopy: shallowCopy,
144
- size: size
148
+ size: size,
149
+ incrementByInt: incrementByInt
145
150
  };
146
151
 
147
152
  function minOptInt(a, b) {
@@ -78,7 +78,7 @@ type poolConfig = {
78
78
  // types?: array<'a>, // Array of custom types, see more below (default: [])
79
79
  onnotice?: string => unit, // Default console.log, set false to silence NOTICE (default: fn)
80
80
  onParameter?: (string, string) => unit, // (key, value) when server param change (default: fn)
81
- debug?: 'a. 'a => unit, //(connection, query, params, types) => unit, // Is called with (connection, query, params, types) (default: fn)
81
+ debug?: (~connection: unknown, ~query: unknown, ~params: unknown, ~types: unknown) => unit, // Is called with (connection, query, params, types)
82
82
  socket?: unit => unit, // fn returning custom socket to use (default: fn)
83
83
  transform?: transformConfig,
84
84
  connection?: connectionConfig,
@@ -7,13 +7,6 @@ import type {Json_t as Js_Json_t} from '../../src/Js.shim';
7
7
 
8
8
  import type {t as Address_t} from '../../src/Address.gen';
9
9
 
10
- export type EventSyncState_t = {
11
- readonly chain_id: number;
12
- readonly block_number: number;
13
- readonly log_index: number;
14
- readonly block_timestamp: number
15
- };
16
-
17
10
  export type RawEvents_t = {
18
11
  readonly chain_id: number;
19
12
  readonly event_id: bigint;
@@ -5,51 +5,15 @@ let isPrimaryKey = true
5
5
  let isNullable = true
6
6
  let isIndex = true
7
7
 
8
- module EventSyncState = {
9
- //Used unsafely in DbFunctions.res so just enforcing the naming here
10
- let blockTimestampFieldName = "block_timestamp"
11
- let blockNumberFieldName = "block_number"
12
- let logIndexFieldName = "log_index"
13
- let isPreRegisteringDynamicContractsFieldName = "is_pre_registering_dynamic_contracts"
14
-
15
- // @genType Used for Test DB
16
- @genType
17
- type t = {
18
- @as("chain_id") chainId: int,
19
- @as("block_number") blockNumber: int,
20
- @as("log_index") logIndex: int,
21
- @as("block_timestamp") blockTimestamp: int,
22
- }
23
-
24
- let table = mkTable(
25
- "event_sync_state",
26
- ~fields=[
27
- mkField("chain_id", Integer, ~fieldSchema=S.int, ~isPrimaryKey),
28
- mkField(blockNumberFieldName, Integer, ~fieldSchema=S.int),
29
- mkField(logIndexFieldName, Integer, ~fieldSchema=S.int),
30
- mkField(blockTimestampFieldName, Integer, ~fieldSchema=S.int),
31
- // Keep it in case Hosted Service relies on it to prevent a breaking changes
32
- mkField(
33
- isPreRegisteringDynamicContractsFieldName,
34
- Boolean,
35
- ~default="false",
36
- ~fieldSchema=S.bool,
37
- ),
38
- ],
39
- )
40
-
41
- //We need to update values here not delet the rows, since restarting without a row
42
- //has a different behaviour to restarting with an initialised row with zero values
43
- let resetCurrentCurrentSyncStateQuery = (~pgSchema) =>
44
- `UPDATE "${pgSchema}"."${table.tableName}"
45
- SET ${blockNumberFieldName} = 0,
46
- ${logIndexFieldName} = 0,
47
- ${blockTimestampFieldName} = 0,
48
- ${isPreRegisteringDynamicContractsFieldName} = false;`
49
- }
50
-
51
8
  module Chains = {
9
+ type progressFields = [
10
+ | #progress_block
11
+ | #_progress_log_index
12
+ | #events_processed
13
+ ]
14
+
52
15
  type field = [
16
+ | progressFields
53
17
  | #id
54
18
  | #start_block
55
19
  | #end_block
@@ -57,10 +21,8 @@ module Chains = {
57
21
  | #first_event_block
58
22
  | #buffer_block
59
23
  | #ready_at
60
- | #events_processed
61
- | #_is_hyper_sync
62
- | #_latest_processed_block
63
24
  | #_num_batches_fetched
25
+ | #_is_hyper_sync
64
26
  ]
65
27
 
66
28
  let fields: array<field> = [
@@ -70,28 +32,34 @@ module Chains = {
70
32
  #source_block,
71
33
  #first_event_block,
72
34
  #buffer_block,
35
+ #progress_block,
73
36
  #ready_at,
74
37
  #events_processed,
38
+ #_progress_log_index,
75
39
  #_is_hyper_sync,
76
- #_latest_processed_block,
77
40
  #_num_batches_fetched,
78
41
  ]
79
42
 
80
- type t = {
81
- @as("id") id: int,
82
- @as("start_block") startBlock: int,
83
- @as("end_block") endBlock: Js.null<int>,
84
- @as("source_block") blockHeight: int,
43
+ type metaFields = {
85
44
  @as("first_event_block") firstEventBlockNumber: Js.null<int>,
86
45
  @as("buffer_block") latestFetchedBlockNumber: int,
46
+ @as("source_block") blockHeight: int,
87
47
  @as("ready_at")
88
48
  timestampCaughtUpToHeadOrEndblock: Js.null<Js.Date.t>,
89
- @as("events_processed") numEventsProcessed: int,
90
- @as("_latest_processed_block") latestProcessedBlock: Js.null<int>,
91
49
  @as("_is_hyper_sync") isHyperSync: bool,
92
50
  @as("_num_batches_fetched") numBatchesFetched: int,
93
51
  }
94
52
 
53
+ type t = {
54
+ @as("id") id: int,
55
+ @as("start_block") startBlock: int,
56
+ @as("end_block") endBlock: Js.null<int>,
57
+ @as("progress_block") progressBlockNumber: int,
58
+ @as("_progress_log_index") progressNextBlockLogIndex: Js.null<int>,
59
+ @as("events_processed") numEventsProcessed: int,
60
+ ...metaFields,
61
+ }
62
+
95
63
  let table = mkTable(
96
64
  "envio_chains",
97
65
  ~fields=[
@@ -118,15 +86,20 @@ module Chains = {
118
86
  ~fieldSchema=S.null(Utils.Schema.dbDate),
119
87
  ~isNullable,
120
88
  ),
121
- mkField((#events_processed: field :> string), Integer, ~fieldSchema=S.int), // TODO: In the future it should reference a table with sources
89
+ mkField((#events_processed: field :> string), Integer, ~fieldSchema=S.int),
90
+ // TODO: In the future it should reference a table with sources
122
91
  mkField((#_is_hyper_sync: field :> string), Boolean, ~fieldSchema=S.bool),
123
- // TODO: Make the data more public facing
92
+ // Fully processed block number
93
+ mkField((#progress_block: field :> string), Integer, ~fieldSchema=S.int),
94
+ // Optional log index of the next block after progress block
95
+ // To correctly resume indexing when we processed half of the block.
124
96
  mkField(
125
- (#_latest_processed_block: field :> string),
97
+ (#_progress_log_index: field :> string),
126
98
  Integer,
127
99
  ~fieldSchema=S.null(S.int),
128
100
  ~isNullable,
129
101
  ),
102
+ // TODO: Should deprecate after changing the ETA calculation logic
130
103
  mkField((#_num_batches_fetched: field :> string), Integer, ~fieldSchema=S.int),
131
104
  ],
132
105
  )
@@ -140,7 +113,8 @@ module Chains = {
140
113
  firstEventBlockNumber: Js.Null.empty,
141
114
  latestFetchedBlockNumber: -1,
142
115
  timestampCaughtUpToHeadOrEndblock: Js.Null.empty,
143
- latestProcessedBlock: Js.Null.empty,
116
+ progressBlockNumber: -1,
117
+ progressNextBlockLogIndex: Js.Null.empty,
144
118
  isHyperSync: false,
145
119
  numEventsProcessed: 0,
146
120
  numBatchesFetched: 0,
@@ -178,21 +152,19 @@ VALUES ${valuesRows->Js.Array2.joinWith(",\n ")};`,
178
152
  }
179
153
  }
180
154
 
181
- // Fields that should be updated on conflict (excluding static config fields)
182
- let updateFields: array<field> = [
155
+ // Fields that can be updated outside of the batch transaction
156
+ let metaFields: array<field> = [
183
157
  #source_block,
184
- #first_event_block,
185
158
  #buffer_block,
159
+ #first_event_block,
186
160
  #ready_at,
187
- #events_processed,
188
161
  #_is_hyper_sync,
189
- #_latest_processed_block,
190
162
  #_num_batches_fetched,
191
163
  ]
192
164
 
193
- let makeSingleUpdateQuery = (~pgSchema) => {
165
+ let makeMetaFieldsUpdateQuery = (~pgSchema) => {
194
166
  // Generate SET clauses with parameter placeholders
195
- let setClauses = Belt.Array.mapWithIndex(updateFields, (index, field) => {
167
+ let setClauses = Belt.Array.mapWithIndex(metaFields, (index, field) => {
196
168
  let fieldName = (field :> string)
197
169
  let paramIndex = index + 2 // +2 because $1 is for id in WHERE clause
198
170
  `"${fieldName}" = $${Belt.Int.toString(paramIndex)}`
@@ -203,27 +175,80 @@ SET ${setClauses->Js.Array2.joinWith(",\n ")}
203
175
  WHERE "id" = $1;`
204
176
  }
205
177
 
206
- let setValues = (sql, ~pgSchema, ~chainsData: array<t>) => {
207
- let query = makeSingleUpdateQuery(~pgSchema)
178
+ let progressFields: array<progressFields> = [
179
+ #progress_block,
180
+ #_progress_log_index,
181
+ #events_processed,
182
+ ]
183
+
184
+ let makeProgressFieldsUpdateQuery = (~pgSchema) => {
185
+ let setClauses = Belt.Array.mapWithIndex(progressFields, (index, field) => {
186
+ let fieldName = (field :> string)
187
+ let paramIndex = index + 2 // +2 because $1 is for id in WHERE clause
188
+ `"${fieldName}" = $${Belt.Int.toString(paramIndex)}`
189
+ })
190
+
191
+ `UPDATE "${pgSchema}"."${table.tableName}"
192
+ SET ${setClauses->Js.Array2.joinWith(",\n ")}
193
+ WHERE "id" = $1;`
194
+ }
195
+
196
+ let setMeta = (sql, ~pgSchema, ~chainsData: dict<metaFields>) => {
197
+ let query = makeMetaFieldsUpdateQuery(~pgSchema)
198
+
199
+ let promises = []
208
200
 
209
- let promises = chainsData->Belt.Array.map(chain => {
201
+ chainsData->Utils.Dict.forEachWithKey((chainId, data) => {
210
202
  let params = []
211
203
 
212
204
  // Push id first (for WHERE clause)
213
- let idValue = chain->(Utils.magic: t => dict<unknown>)->Js.Dict.get("id")
214
- params->Js.Array2.push(idValue)->ignore
205
+ params->Js.Array2.push(chainId->(Utils.magic: string => unknown))->ignore
215
206
 
216
207
  // Then push all updateable field values (for SET clause)
217
- updateFields->Js.Array2.forEach(field => {
218
- let value = chain->(Utils.magic: t => dict<unknown>)->Js.Dict.get((field :> string))
208
+ metaFields->Js.Array2.forEach(field => {
209
+ let value =
210
+ data->(Utils.magic: metaFields => dict<unknown>)->Js.Dict.unsafeGet((field :> string))
219
211
  params->Js.Array2.push(value)->ignore
220
212
  })
221
213
 
222
- sql->Postgres.preparedUnsafe(query, params->Obj.magic)
214
+ promises->Js.Array2.push(sql->Postgres.preparedUnsafe(query, params->Obj.magic))->ignore
223
215
  })
224
216
 
225
217
  Promise.all(promises)
226
218
  }
219
+
220
+ let setProgressedChains = (sql, ~pgSchema, ~progressedChains: array<Batch.progressedChain>) => {
221
+ let query = makeProgressFieldsUpdateQuery(~pgSchema)
222
+
223
+ let promises = []
224
+
225
+ progressedChains->Js.Array2.forEach(data => {
226
+ let params = []
227
+
228
+ // Push id first (for WHERE clause)
229
+ params->Js.Array2.push(data.chainId->(Utils.magic: int => unknown))->ignore
230
+
231
+ // Then push all updateable field values (for SET clause)
232
+ progressFields->Js.Array2.forEach(field => {
233
+ params
234
+ ->Js.Array2.push(
235
+ switch field {
236
+ | #progress_block => data.progressBlockNumber->(Utils.magic: int => unknown)
237
+ | #_progress_log_index =>
238
+ data.progressNextBlockLogIndex
239
+ ->Js.Null.fromOption
240
+ ->(Utils.magic: Js.null<int> => unknown)
241
+ | #events_processed => data.totalEventsProcessed->(Utils.magic: int => unknown)
242
+ },
243
+ )
244
+ ->ignore
245
+ })
246
+
247
+ promises->Js.Array2.push(sql->Postgres.preparedUnsafe(query, params->Obj.magic))->ignore
248
+ })
249
+
250
+ Promise.all(promises)->Promise.ignoreValue
251
+ }
227
252
  }
228
253
 
229
254
  module PersistedState = {
@@ -336,10 +361,12 @@ module Views = {
336
361
  "${(#id: Chains.field :> string)}" AS "chainId",
337
362
  "${(#start_block: Chains.field :> string)}" AS "startBlock",
338
363
  "${(#end_block: Chains.field :> string)}" AS "endBlock",
364
+ "${(#progress_block: Chains.field :> string)}" AS "progressBlock",
339
365
  "${(#buffer_block: Chains.field :> string)}" AS "bufferBlock",
340
- "${(#ready_at: Chains.field :> string)}" AS "readyAt",
341
366
  "${(#first_event_block: Chains.field :> string)}" AS "firstEventBlock",
342
367
  "${(#events_processed: Chains.field :> string)}" AS "eventsProcessed",
368
+ "${(#source_block: Chains.field :> string)}" AS "sourceBlock",
369
+ "${(#ready_at: Chains.field :> string)}" AS "readyAt",
343
370
  ("${(#ready_at: Chains.field :> string)}" IS NOT NULL) AS "isReady"
344
371
  FROM "${pgSchema}"."${Chains.table.tableName}"
345
372
  ORDER BY "${(#id: Chains.field :> string)}";`
@@ -354,7 +381,7 @@ module Views = {
354
381
  "${(#first_event_block: Chains.field :> string)}" AS "first_event_block_number",
355
382
  "${(#_is_hyper_sync: Chains.field :> string)}" AS "is_hyper_sync",
356
383
  "${(#buffer_block: Chains.field :> string)}" AS "latest_fetched_block_number",
357
- "${(#_latest_processed_block: Chains.field :> string)}" AS "latest_processed_block",
384
+ "${(#progress_block: Chains.field :> string)}" AS "latest_processed_block",
358
385
  "${(#_num_batches_fetched: Chains.field :> string)}" AS "num_batches_fetched",
359
386
  "${(#events_processed: Chains.field :> string)}" AS "num_events_processed",
360
387
  "${(#start_block: Chains.field :> string)}" AS "start_block",
@@ -427,4 +454,42 @@ module DynamicContractRegistry = {
427
454
  table,
428
455
  entityHistory,
429
456
  }->Internal.fromGenericEntityConfig
457
+
458
+ let makeCleanUpOnRestartQuery = (~pgSchema, ~chains: array<Chains.t>) => {
459
+ let query = ref(``)
460
+
461
+ chains->Js.Array2.forEach(chain => {
462
+ if query.contents !== "" {
463
+ query := query.contents ++ "\n"
464
+ }
465
+ query :=
466
+ query.contents ++
467
+ `DELETE FROM "${pgSchema}"."${table.tableName}"
468
+ WHERE chain_id = ${chain.id->Belt.Int.toString}${switch chain {
469
+ | {progressBlockNumber, progressNextBlockLogIndex: Value(progressNextBlockLogIndex)} =>
470
+ ` AND (
471
+ registering_event_block_number > ${(progressBlockNumber + 1)->Belt.Int.toString}
472
+ OR registering_event_block_number = ${(progressBlockNumber + 1)->Belt.Int.toString}
473
+ AND registering_event_log_index > ${progressNextBlockLogIndex->Belt.Int.toString}
474
+ )`
475
+ | {progressBlockNumber: -1, progressNextBlockLogIndex: Null} => ``
476
+ | {progressBlockNumber, progressNextBlockLogIndex: Null} =>
477
+ ` AND registering_event_block_number > ${progressBlockNumber->Belt.Int.toString}`
478
+ }};
479
+ DELETE FROM "${pgSchema}"."${table.tableName}_history"
480
+ WHERE entity_history_chain_id = ${chain.id->Belt.Int.toString}${switch chain {
481
+ | {progressBlockNumber, progressNextBlockLogIndex: Value(progressNextBlockLogIndex)} =>
482
+ ` AND (
483
+ entity_history_block_number > ${(progressBlockNumber + 1)->Belt.Int.toString}
484
+ OR entity_history_block_number = ${(progressBlockNumber + 1)->Belt.Int.toString}
485
+ AND entity_history_log_index > ${progressNextBlockLogIndex->Belt.Int.toString}
486
+ )`
487
+ | {progressBlockNumber: -1, progressNextBlockLogIndex: Null} => ``
488
+ | {progressBlockNumber, progressNextBlockLogIndex: Null} =>
489
+ ` AND entity_history_block_number > ${progressBlockNumber->Belt.Int.toString}`
490
+ }};`
491
+ })
492
+
493
+ query.contents
494
+ }
430
495
  }