envio 3.11.0 → 3.12.1
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/index.d.ts +3 -0
- package/package.json +6 -6
- package/src/Batch.res +111 -78
- package/src/Batch.res.mjs +53 -46
- package/src/BatchProcessing.res +1 -11
- package/src/BatchProcessing.res.mjs +1 -4
- package/src/ChainFetching.res +7 -3
- package/src/ChainFetching.res.mjs +1 -1
- package/src/ChainState.res +82 -20
- package/src/ChainState.res.mjs +51 -13
- package/src/ChainState.resi +6 -1
- package/src/Config.res +58 -37
- package/src/Config.res.mjs +63 -53
- package/src/CrossChainState.res +50 -53
- package/src/CrossChainState.res.mjs +25 -23
- package/src/CrossChainState.resi +6 -13
- package/src/EffectState.res +13 -4
- package/src/EffectState.resi +7 -1
- package/src/EventProcessing.res +6 -6
- package/src/EventProcessing.res.mjs +6 -6
- package/src/FetchState.res +65 -46
- package/src/FetchState.res.mjs +68 -36
- package/src/FinalizeBackfill.res +16 -12
- package/src/FinalizeBackfill.res.mjs +3 -3
- package/src/HistoryPolicy.res +46 -0
- package/src/HistoryPolicy.res.mjs +50 -0
- package/src/InMemoryStore.res +30 -32
- package/src/InMemoryStore.res.mjs +33 -28
- package/src/InMemoryTable.res +155 -67
- package/src/InMemoryTable.res.mjs +151 -60
- package/src/IndexerState.res +66 -45
- package/src/IndexerState.res.mjs +57 -29
- package/src/IndexerState.resi +8 -13
- package/src/Internal.res +4 -2
- package/src/LoadLayer.res +68 -38
- package/src/LoadLayer.res.mjs +50 -64
- package/src/LoadLayer.resi +1 -1
- package/src/Main.res +25 -24
- package/src/Main.res.mjs +24 -25
- package/src/Metrics.res +49 -1
- package/src/Metrics.res.mjs +26 -3
- package/src/Persistence.res +55 -16
- package/src/Persistence.res.mjs +18 -3
- package/src/PgStorage.res +286 -180
- package/src/PgStorage.res.mjs +209 -122
- package/src/PruneStaleHistory.res +9 -15
- package/src/PruneStaleHistory.res.mjs +11 -20
- package/src/Rollback.res +9 -14
- package/src/Rollback.res.mjs +5 -3
- package/src/Sink.res +7 -5
- package/src/Sink.res.mjs +4 -4
- package/src/Staging.res +298 -0
- package/src/Staging.res.mjs +257 -0
- package/src/TestIndexer.res +14 -30
- package/src/TestIndexer.res.mjs +11 -9
- package/src/UserContext.res +20 -36
- package/src/UserContext.res.mjs +6 -9
- package/src/Utils.res +10 -4
- package/src/Utils.res.mjs +16 -16
- package/src/Writing.res +34 -20
- package/src/Writing.res.mjs +15 -20
- package/src/bindings/ClickHouse.res +41 -34
- package/src/bindings/ClickHouse.res.mjs +36 -30
- package/src/bindings/ClickHouseSink.res +45 -186
- package/src/bindings/ClickHouseSink.res.mjs +21 -174
- package/src/db/CheckpointSequence.res +109 -0
- package/src/db/CheckpointSequence.res.mjs +99 -0
- package/src/db/EntityFilter.res +487 -275
- package/src/db/EntityFilter.res.mjs +557 -309
- package/src/db/EntityHistory.res +6 -5
- package/src/db/EntityHistory.res.mjs +5 -5
- package/src/db/Frontier.res +86 -0
- package/src/db/Frontier.res.mjs +126 -0
- package/src/db/InternalTable.res +145 -27
- package/src/db/InternalTable.res.mjs +115 -29
- package/src/db/RollbackFloors.res +34 -41
- package/src/db/RollbackFloors.res.mjs +27 -71
- package/src/db/Table.res +21 -6
- package/src/db/Table.res.mjs +13 -4
- package/src/sources/BlockStore.res +8 -0
- package/src/sources/EvmHyperSyncSource.res +5 -1
- package/src/sources/EvmHyperSyncSource.res.mjs +4 -3
- package/src/sources/FuelHyperSyncSource.res +1 -0
- package/src/sources/FuelHyperSyncSource.res.mjs +1 -1
- package/src/sources/HyperSync.res +6 -0
- package/src/sources/HyperSync.res.mjs +5 -2
- package/src/sources/HyperSync.resi +2 -0
- package/src/sources/HyperSyncClient.res +6 -0
- package/src/sources/HyperSyncSSE.res +1 -1
- package/src/sources/HyperSyncSSE.res.mjs +4 -10
- package/src/sources/RequestStat.res +8 -1
- package/src/sources/RpcSource.res +1 -0
- package/src/sources/RpcSource.res.mjs +1 -1
- package/src/sources/SimulateSource.res +1 -0
- package/src/sources/SimulateSource.res.mjs +1 -1
- package/src/sources/Source.res +11 -3
- package/src/sources/SourceManager.res +34 -13
- package/src/sources/SourceManager.res.mjs +26 -9
- package/src/sources/SourceManager.resi +2 -0
- package/src/sources/SvmHyperSyncClient.res +5 -0
- package/src/sources/SvmHyperSyncSource.res +3 -0
- package/src/sources/SvmHyperSyncSource.res.mjs +4 -2
- package/src/tui/Tui.res +56 -143
- package/src/tui/Tui.res.mjs +70 -166
- package/src/tui/components/TuiData.res +61 -16
- package/src/tui/components/TuiData.res.mjs +53 -15
- package/svm.schema.json +0 -7
- package/src/db/CheckpointBounds.res +0 -53
- package/src/db/CheckpointBounds.res.mjs +0 -44
package/src/PgStorage.res
CHANGED
|
@@ -28,15 +28,46 @@ let formatSeconds = (timeRef: Performance.timeRef) =>
|
|
|
28
28
|
// stalled-looking indexer is explainable from the logs alone.
|
|
29
29
|
let slowOnLargeDatabaseNotice = "This can take a long time on a large database."
|
|
30
30
|
|
|
31
|
+
// A per-chain entity's rows are partitioned by the chain that owns them, so a
|
|
32
|
+
// chain-filtered read scans one chain's partition rather than the whole table.
|
|
33
|
+
// `$` can't occur in a GraphQL entity name, so a partition name can never
|
|
34
|
+
// collide with the table another entity claims; past the identifier limit the
|
|
35
|
+
// entity index keeps what survives truncation unique.
|
|
36
|
+
let partitionTableName = (~entityConfig: Internal.entityConfig, ~chainId: ChainId.t) => {
|
|
37
|
+
let chainIdStr = chainId->ChainId.toString
|
|
38
|
+
Table.fitPgTableName(
|
|
39
|
+
`${entityConfig.table.tableName}$${chainIdStr}`,
|
|
40
|
+
~uniqueSuffix=`$${entityConfig.index->Int.toString}$${chainIdStr}`,
|
|
41
|
+
)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// The physical tables an index on the entity is built on. A per-chain entity's
|
|
45
|
+
// rows are partitioned by chain, and an index declared on the parent cascades
|
|
46
|
+
// to every partition, which is what a run driving every chain wants: one
|
|
47
|
+
// declaration, one build. An isolated run instead builds on its own chains'
|
|
48
|
+
// partitions only: the planner uses a partition's own index either way, and
|
|
49
|
+
// building one chain's index then neither waits for nor locks the rows of a
|
|
50
|
+
// chain a sibling process drives.
|
|
51
|
+
let indexTableNames = (entityConfig: Internal.entityConfig, ~partitionChainIds) =>
|
|
52
|
+
switch (entityConfig.table->Table.getChainIdField, partitionChainIds) {
|
|
53
|
+
| (Some(_), Some(chainIds)) =>
|
|
54
|
+
chainIds->Array.map(chainId => partitionTableName(~entityConfig, ~chainId))
|
|
55
|
+
| _ => [entityConfig.table.tableName]
|
|
56
|
+
}
|
|
57
|
+
|
|
31
58
|
// Every index the entity schema promises: an `@index` field, a composite index,
|
|
32
|
-
// or the index backing a derived relationship. Deferred past the initial DDL
|
|
33
|
-
// created
|
|
34
|
-
//
|
|
59
|
+
// or the index backing a derived relationship. Deferred past the initial DDL
|
|
60
|
+
// and created once backfill completes, so a chain that reports itself ready
|
|
61
|
+
// always has all of them. With `partitionChainIds`, a per-chain entity's index
|
|
62
|
+
// is one per partition of those chains rather than one on the parent.
|
|
35
63
|
//
|
|
36
64
|
// `entities` is the Postgres-backed set, and every `@derivedFrom` target within
|
|
37
65
|
// it resolves: config parsing rejects a Postgres entity deriving from one that
|
|
38
66
|
// isn't in Postgres (`validate_relationship_storage`).
|
|
39
|
-
let getSchemaIndexes = (
|
|
67
|
+
let getSchemaIndexes = (
|
|
68
|
+
~entities: array<Internal.entityConfig>,
|
|
69
|
+
~partitionChainIds: option<array<ChainId.t>>=?,
|
|
70
|
+
): array<IndexDefinition.t> => {
|
|
40
71
|
let derivedSchema = Schema.make(entities->Array.map(e => e.table))
|
|
41
72
|
let all = []
|
|
42
73
|
|
|
@@ -70,7 +101,13 @@ let getSchemaIndexes = (~entities: array<Internal.entityConfig>): array<IndexDef
|
|
|
70
101
|
// An `@index` field and a derived relationship pointing at it describe the
|
|
71
102
|
// same index, so the list is deduped on identity rather than on name.
|
|
72
103
|
let seen = Utils.Set.make()
|
|
73
|
-
|
|
104
|
+
let entityByTableName = Dict.make()
|
|
105
|
+
entities->Array.forEach(entityConfig =>
|
|
106
|
+
entityByTableName->Dict.set(entityConfig.table.tableName, entityConfig)
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
all
|
|
110
|
+
->Array.filter(definition => {
|
|
74
111
|
let key = definition->IndexDefinition.key
|
|
75
112
|
if seen->Utils.Set.has(key) {
|
|
76
113
|
false
|
|
@@ -79,6 +116,13 @@ let getSchemaIndexes = (~entities: array<Internal.entityConfig>): array<IndexDef
|
|
|
79
116
|
true
|
|
80
117
|
}
|
|
81
118
|
})
|
|
119
|
+
->Array.flatMap(definition =>
|
|
120
|
+
entityByTableName
|
|
121
|
+
->Dict.get(definition.tableName)
|
|
122
|
+
->Option.getOrThrow
|
|
123
|
+
->indexTableNames(~partitionChainIds)
|
|
124
|
+
->Array.map(tableName => {...definition, IndexDefinition.tableName})
|
|
125
|
+
)
|
|
82
126
|
}
|
|
83
127
|
|
|
84
128
|
let makeCreateTableQuery = (
|
|
@@ -122,19 +166,6 @@ let makeCreateTableQuery = (
|
|
|
122
166
|
}};`
|
|
123
167
|
}
|
|
124
168
|
|
|
125
|
-
// A per-chain entity's rows are partitioned by the chain that owns them, so a
|
|
126
|
-
// chain-filtered read scans one chain's partition rather than the whole table.
|
|
127
|
-
// `$` can't occur in a GraphQL entity name, so a partition name can never
|
|
128
|
-
// collide with the table another entity claims; past the identifier limit the
|
|
129
|
-
// entity index keeps what survives truncation unique.
|
|
130
|
-
let partitionTableName = (~entityConfig: Internal.entityConfig, ~chainId: ChainId.t) => {
|
|
131
|
-
let chainIdStr = chainId->ChainId.toString
|
|
132
|
-
Table.fitPgTableName(
|
|
133
|
-
`${entityConfig.table.tableName}$${chainIdStr}`,
|
|
134
|
-
~uniqueSuffix=`$${entityConfig.index->Int.toString}$${chainIdStr}`,
|
|
135
|
-
)
|
|
136
|
-
}
|
|
137
|
-
|
|
138
169
|
// The entity as it's stored: the handler-visible schema plus the chain-id
|
|
139
170
|
// column a per-chain entity's table carries. The value for that column is
|
|
140
171
|
// stamped from the flush group's scope right before serialization, so it never
|
|
@@ -277,6 +308,10 @@ let makeInitializeTransaction = (
|
|
|
277
308
|
~pgSchema,
|
|
278
309
|
~pgUser,
|
|
279
310
|
~isHasuraEnabled,
|
|
311
|
+
// The whole schema's sequence, not one derived from `entities`: those are the
|
|
312
|
+
// entities Postgres stores, and an entity kept only in a sink still decides
|
|
313
|
+
// how the run counts its checkpoints.
|
|
314
|
+
~checkpointSequence: CheckpointSequence.t,
|
|
280
315
|
~chainConfigs=[],
|
|
281
316
|
~entities=[],
|
|
282
317
|
~enums=[],
|
|
@@ -292,7 +327,7 @@ let makeInitializeTransaction = (
|
|
|
292
327
|
InternalTable.EnvioInfo.table,
|
|
293
328
|
InternalTable.EnvioContracts.table,
|
|
294
329
|
InternalTable.EnvioAddresses.table,
|
|
295
|
-
InternalTable.Checkpoints.
|
|
330
|
+
InternalTable.Checkpoints.tableFor(checkpointSequence),
|
|
296
331
|
InternalTable.RawEvents.table,
|
|
297
332
|
]
|
|
298
333
|
|
|
@@ -374,9 +409,10 @@ let makeLoadQuery = (~pgSchema, ~tableName, ~condition) => {
|
|
|
374
409
|
// Field names are spliced as quoted identifiers only after the queryFields
|
|
375
410
|
// lookup proves they exist on the table (and they originate from
|
|
376
411
|
// codegen-validated schemas), so the interpolation can't be abused.
|
|
377
|
-
let
|
|
412
|
+
let makeFilterCondition = (
|
|
378
413
|
~filter: EntityFilter.t,
|
|
379
414
|
~table: Table.table,
|
|
415
|
+
~pgSchema,
|
|
380
416
|
~params: array<unknown>,
|
|
381
417
|
) => {
|
|
382
418
|
// Filters reference fields by API name, while the SQL references columns
|
|
@@ -412,60 +448,89 @@ let rec makeFilterCondition = (
|
|
|
412
448
|
params->Array.push(param)->ignore
|
|
413
449
|
`$${params->Array.length->Int.toString}`
|
|
414
450
|
}
|
|
415
|
-
|
|
451
|
+
|
|
452
|
+
let condition = ref("")
|
|
453
|
+
filter
|
|
454
|
+
->EntityFilter.entries
|
|
455
|
+
->Utils.Dict.forEachWithKey((operators, fieldName) => {
|
|
416
456
|
let queryField = getQueryFieldOrThrow(fieldName)
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
457
|
+
operators->Utils.Dict.forEachWithKey((fieldValue, operator) => {
|
|
458
|
+
let column = `"${queryField.pgDbFieldName}"`
|
|
459
|
+
let part = switch operator {
|
|
460
|
+
// A per-chain entity's table is partitioned by its chain-id column, and
|
|
461
|
+
// Postgres can only prune a plan it caches when that column is a constant
|
|
462
|
+
// in the SQL. Bound, the cached plan has to keep every partition, and the
|
|
463
|
+
// planner ends up throwing it away and re-planning on every execution
|
|
464
|
+
// instead — measured at 315us per load against 218us with the id written
|
|
465
|
+
// in, on 30 chains.
|
|
466
|
+
//
|
|
467
|
+
// The cost is that each chain gets its own query text, so Postgres caches
|
|
468
|
+
// a prepared statement per (entity, chain, filter shape) rather than per
|
|
469
|
+
// (entity, filter shape). Measured at ~8KB of plan cache each, which is
|
|
470
|
+
// ~10MB per connection for 40 entities across 30 chains — accepted, since
|
|
471
|
+
// the alternative is a cached plan that can't prune.
|
|
472
|
+
//
|
|
473
|
+
// `EntityFilter.scoped` is what puts this filter here, and the value is
|
|
474
|
+
// range-checked to a non-negative safe integer, so it can carry nothing
|
|
475
|
+
// but digits.
|
|
476
|
+
| "_eq" if queryField.isChainId =>
|
|
477
|
+
`${column} = ${fieldValue->ChainId.normalizeOrThrow->ChainId.toString}`
|
|
478
|
+
// Postgres arrays are rectangular, so candidates for a list column can't
|
|
479
|
+
// be bound as one array unless they all have the same length, and
|
|
480
|
+
// postgres.js can't bind a boolean array at all
|
|
481
|
+
// (https://github.com/porsager/postgres/issues/471). One equality per
|
|
482
|
+
// candidate has neither problem.
|
|
483
|
+
| "_in" if queryField.isArray || queryField.fieldType === Boolean =>
|
|
484
|
+
switch fieldValue->EntityFilter.asArray {
|
|
485
|
+
| [] => "FALSE"
|
|
486
|
+
| candidates =>
|
|
487
|
+
`(${candidates
|
|
488
|
+
->Array.map(
|
|
489
|
+
candidate =>
|
|
490
|
+
`${column} = ${serializeParamOrThrow(
|
|
491
|
+
~queryField,
|
|
492
|
+
~fieldName,
|
|
493
|
+
~fieldValue=candidate,
|
|
494
|
+
~isArray=false,
|
|
495
|
+
)}`,
|
|
496
|
+
)
|
|
497
|
+
->Array.join(" OR ")})`
|
|
498
|
+
}
|
|
499
|
+
| "_in" =>
|
|
500
|
+
let param = serializeParamOrThrow(~queryField, ~fieldName, ~fieldValue, ~isArray=true)
|
|
501
|
+
switch queryField.fieldType {
|
|
502
|
+
// A bound array of strings is text[], which has no equality with an
|
|
503
|
+
// enum. The insert casts the same way.
|
|
504
|
+
| Enum({config}) => `${column} = ANY(${param}::TEXT[]::"${pgSchema}".${config.name}[])`
|
|
505
|
+
| _ => `${column} = ANY(${param})`
|
|
506
|
+
}
|
|
507
|
+
| _ =>
|
|
508
|
+
let sqlOperator = switch operator {
|
|
509
|
+
| "_eq" => "="
|
|
510
|
+
| "_gt" => ">"
|
|
511
|
+
| "_lt" => "<"
|
|
512
|
+
| "_gte" => ">="
|
|
513
|
+
| "_lte" => "<="
|
|
514
|
+
| _ =>
|
|
515
|
+
throw(
|
|
516
|
+
Persistence.StorageError({
|
|
517
|
+
message: `Failed loading "${table.tableName}" from storage. Unknown filter operator "${operator}".`,
|
|
518
|
+
reason: Utils.Error.make(`Unknown filter operator "${operator}"`),
|
|
519
|
+
}),
|
|
520
|
+
)
|
|
521
|
+
}
|
|
522
|
+
`${column} ${sqlOperator} ${serializeParamOrThrow(
|
|
523
|
+
~queryField,
|
|
524
|
+
~fieldName,
|
|
525
|
+
~fieldValue,
|
|
526
|
+
~isArray=false,
|
|
527
|
+
)}`
|
|
528
|
+
}
|
|
529
|
+
condition := (condition.contents === "" ? part : condition.contents ++ " AND " ++ part)
|
|
530
|
+
})
|
|
531
|
+
})
|
|
532
|
+
|
|
533
|
+
condition.contents
|
|
469
534
|
}
|
|
470
535
|
|
|
471
536
|
// The chain-id predicate a per-chain entity's row-level SQL needs, already
|
|
@@ -1042,12 +1107,21 @@ let executeSet = (
|
|
|
1042
1107
|
}
|
|
1043
1108
|
}
|
|
1044
1109
|
|
|
1110
|
+
// The checkpoints a write inserts: every one the batch made, or those of the
|
|
1111
|
+
// chains whose history it keeps.
|
|
1112
|
+
type pickedCheckpoints = AllCheckpoints | CheckpointIndexes(array<int>)
|
|
1113
|
+
|
|
1114
|
+
let pickCheckpoints = (column, picked) =>
|
|
1115
|
+
switch picked {
|
|
1116
|
+
| AllCheckpoints => column
|
|
1117
|
+
| CheckpointIndexes(indexes) => indexes->Array.map(index => column->Array.getUnsafe(index))
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1045
1120
|
let rec writeBatch = async (
|
|
1046
1121
|
sql,
|
|
1047
1122
|
~batch: Batch.t,
|
|
1048
1123
|
~pgSchema,
|
|
1049
1124
|
~rollback: option<Persistence.rollback>,
|
|
1050
|
-
~isInReorgThreshold,
|
|
1051
1125
|
~config: Config.t,
|
|
1052
1126
|
~allEntities: array<Internal.entityConfig>,
|
|
1053
1127
|
~setEffectCacheOrThrow,
|
|
@@ -1061,7 +1135,22 @@ let rec writeBatch = async (
|
|
|
1061
1135
|
) => {
|
|
1062
1136
|
try {
|
|
1063
1137
|
let chainIdMode = config.chainIdMode
|
|
1064
|
-
|
|
1138
|
+
// A checkpoint anchors the history its chain keeps, so the batch's
|
|
1139
|
+
// decision picks the checkpoints chain by chain.
|
|
1140
|
+
let pickedCheckpoints = {
|
|
1141
|
+
let indexes =
|
|
1142
|
+
batch.checkpointChainIds->Array.filterMapWithIndex((chainId, index) =>
|
|
1143
|
+
batch.history->HistoryPolicy.forChain(chainId) ? Some(index) : None
|
|
1144
|
+
)
|
|
1145
|
+
if indexes->Utils.Array.isEmpty {
|
|
1146
|
+
None
|
|
1147
|
+
} else if indexes->Array.length === batch.checkpointIds->Array.length {
|
|
1148
|
+
Some(AllCheckpoints)
|
|
1149
|
+
} else {
|
|
1150
|
+
Some(CheckpointIndexes(indexes))
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
let writtenFrontier = Persistence.writtenFrontier(~batch, ~rollback)
|
|
1065
1154
|
|
|
1066
1155
|
let specificError = ref(None)
|
|
1067
1156
|
|
|
@@ -1111,7 +1200,12 @@ let rec writeBatch = async (
|
|
|
1111
1200
|
}
|
|
1112
1201
|
}
|
|
1113
1202
|
|
|
1114
|
-
let setEntities = updatedEntities->Array.map(({
|
|
1203
|
+
let setEntities = updatedEntities->Array.map(({
|
|
1204
|
+
entityConfig,
|
|
1205
|
+
scope,
|
|
1206
|
+
changes,
|
|
1207
|
+
shouldSaveHistory,
|
|
1208
|
+
}) => {
|
|
1115
1209
|
let entitiesToSet = []
|
|
1116
1210
|
let idsToDelete = []
|
|
1117
1211
|
|
|
@@ -1121,8 +1215,6 @@ let rec writeBatch = async (
|
|
|
1121
1215
|
| Internal.CrossChain => None
|
|
1122
1216
|
| Chain(chainId) => Some(chainId)
|
|
1123
1217
|
}
|
|
1124
|
-
let shouldSaveHistory =
|
|
1125
|
-
config->Config.shouldSaveHistory(~isInReorgThreshold, ~chainId=?scopeChainId)
|
|
1126
1218
|
let changes = switch (entityConfig.table->Table.getChainIdField, scopeChainId) {
|
|
1127
1219
|
| (Some(field), Some(chainId)) =>
|
|
1128
1220
|
changes->Array.map(change =>
|
|
@@ -1147,7 +1239,10 @@ let rec writeBatch = async (
|
|
|
1147
1239
|
|
|
1148
1240
|
// The rollback-diff change is written to the entity table only, never the
|
|
1149
1241
|
// history table; when present it is an id's oldest change.
|
|
1150
|
-
let diffCheckpointId =
|
|
1242
|
+
let diffCheckpointId =
|
|
1243
|
+
rollback->Option.flatMap(r =>
|
|
1244
|
+
config.checkpointSequence->CheckpointSequence.findForScope(r.diffFrontier, ~scope)
|
|
1245
|
+
)
|
|
1151
1246
|
|
|
1152
1247
|
// History batches, populated only when saving history.
|
|
1153
1248
|
let batchSetUpdates = []
|
|
@@ -1384,6 +1479,7 @@ let rec writeBatch = async (
|
|
|
1384
1479
|
): InternalTable.Chains.progressedChain => {
|
|
1385
1480
|
chainId: chainAfterBatch.fetchState.chainId,
|
|
1386
1481
|
progressBlockNumber: chainAfterBatch.progressBlockNumber,
|
|
1482
|
+
progressBlockTime: chainAfterBatch.progressBlockTime,
|
|
1387
1483
|
sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
|
|
1388
1484
|
totalEventsProcessed: chainAfterBatch.totalEventsProcessed,
|
|
1389
1485
|
}),
|
|
@@ -1411,18 +1507,30 @@ let rec writeBatch = async (
|
|
|
1411
1507
|
)
|
|
1412
1508
|
}
|
|
1413
1509
|
|
|
1414
|
-
if
|
|
1510
|
+
if !(writtenFrontier->Utils.Dict.isEmpty) {
|
|
1511
|
+
setOperations->Array.push(sql =>
|
|
1512
|
+
sql->InternalTable.Chains.setCheckpointFrontier(
|
|
1513
|
+
~pgSchema,
|
|
1514
|
+
~frontier=writtenFrontier,
|
|
1515
|
+
~chainIdMode,
|
|
1516
|
+
)
|
|
1517
|
+
)
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
switch pickedCheckpoints {
|
|
1521
|
+
| Some(picked) =>
|
|
1415
1522
|
setOperations->Array.push(sql =>
|
|
1416
1523
|
sql->InternalTable.Checkpoints.insert(
|
|
1417
1524
|
~pgSchema,
|
|
1418
|
-
~checkpointIds=batch.checkpointIds,
|
|
1419
|
-
~checkpointChainIds=batch.checkpointChainIds,
|
|
1420
|
-
~checkpointBlockNumbers=batch.checkpointBlockNumbers,
|
|
1421
|
-
~checkpointBlockHashes=batch.checkpointBlockHashes,
|
|
1422
|
-
~checkpointEventsProcessed=batch.checkpointEventsProcessed,
|
|
1525
|
+
~checkpointIds=batch.checkpointIds->pickCheckpoints(picked),
|
|
1526
|
+
~checkpointChainIds=batch.checkpointChainIds->pickCheckpoints(picked),
|
|
1527
|
+
~checkpointBlockNumbers=batch.checkpointBlockNumbers->pickCheckpoints(picked),
|
|
1528
|
+
~checkpointBlockHashes=batch.checkpointBlockHashes->pickCheckpoints(picked),
|
|
1529
|
+
~checkpointEventsProcessed=batch.checkpointEventsProcessed->pickCheckpoints(picked),
|
|
1423
1530
|
~chainIdMode,
|
|
1424
1531
|
)
|
|
1425
1532
|
)
|
|
1533
|
+
| None => ()
|
|
1426
1534
|
}
|
|
1427
1535
|
|
|
1428
1536
|
await setOperations
|
|
@@ -1479,7 +1587,6 @@ let rec writeBatch = async (
|
|
|
1479
1587
|
~pgSchema,
|
|
1480
1588
|
~setQueryCache,
|
|
1481
1589
|
~rollback,
|
|
1482
|
-
~isInReorgThreshold,
|
|
1483
1590
|
~config,
|
|
1484
1591
|
~setEffectCacheOrThrow,
|
|
1485
1592
|
~updatedEffectsCache,
|
|
@@ -1532,7 +1639,7 @@ let makeGetRollbackPreTargetRowsQuery = (
|
|
|
1532
1639
|
let keyMatch =
|
|
1533
1640
|
keyColumns->Array.map(c => `h."${c}" = ${tableRef}."${c}"`)->Array.joinUnsafe(" AND ")
|
|
1534
1641
|
let bounds =
|
|
1535
|
-
floors.
|
|
1642
|
+
floors.checkpointBounds->CheckpointSequence.sql(
|
|
1536
1643
|
~chainIdColumn=entityConfig.table->Table.getPgChainIdColumn,
|
|
1537
1644
|
~tableRef,
|
|
1538
1645
|
)
|
|
@@ -1565,7 +1672,7 @@ let makeGetRollbackRemovedIdsQuery = (
|
|
|
1565
1672
|
let keyMatch =
|
|
1566
1673
|
keyColumns->Array.map(c => `h."${c}" = ${tableRef}."${c}"`)->Array.joinUnsafe(" AND ")
|
|
1567
1674
|
let bounds =
|
|
1568
|
-
floors.
|
|
1675
|
+
floors.checkpointBounds->CheckpointSequence.sql(
|
|
1569
1676
|
~chainIdColumn=entityConfig.table->Table.getPgChainIdColumn,
|
|
1570
1677
|
~tableRef,
|
|
1571
1678
|
)
|
|
@@ -1623,13 +1730,16 @@ let make = (
|
|
|
1623
1730
|
// encoded at initialize and when stored rows are grouped on resume.
|
|
1624
1731
|
~ecosystem: Ecosystem.name,
|
|
1625
1732
|
~sink: option<Sink.t>=?,
|
|
1733
|
+
// An `envio start --chain` process: builds and looks for a per-chain entity's
|
|
1734
|
+
// indexes on its own chains' partitions, never on the parent table, so no
|
|
1735
|
+
// build reaches into the rows a sibling process drives.
|
|
1736
|
+
~isolated=false,
|
|
1626
1737
|
~onInitialize=?,
|
|
1627
|
-
~onNewTables=?,
|
|
1628
1738
|
): Persistence.storage => {
|
|
1629
1739
|
// Must match PG_CONTAINER in packages/cli/src/docker_env.rs
|
|
1630
1740
|
let containerName = "envio-postgres"
|
|
1631
1741
|
let psqlExecOptions: NodeJs.ChildProcess.execOptions = {
|
|
1632
|
-
env:
|
|
1742
|
+
env: dict{"PGPASSWORD": pgPassword, "PATH": %raw(`process.env.PATH`)},
|
|
1633
1743
|
}
|
|
1634
1744
|
|
|
1635
1745
|
let cacheDirPath = NodeJs.Path.resolve([
|
|
@@ -1784,19 +1894,6 @@ let make = (
|
|
|
1784
1894
|
|
|
1785
1895
|
let cacheTableInfo = await queryCacheTableInfo()
|
|
1786
1896
|
|
|
1787
|
-
if withUpload && cacheTableInfo->Utils.Array.notEmpty {
|
|
1788
|
-
// Integration with other tools like Hasura
|
|
1789
|
-
switch onNewTables {
|
|
1790
|
-
| Some(onNewTables) =>
|
|
1791
|
-
await onNewTables(
|
|
1792
|
-
~tableNames=cacheTableInfo->Array.map(info => {
|
|
1793
|
-
info.tableName
|
|
1794
|
-
}),
|
|
1795
|
-
)
|
|
1796
|
-
| None => ()
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
|
|
1800
1897
|
let cache = Dict.make()
|
|
1801
1898
|
cacheTableInfo->Array.forEach(({tableName, count}) => {
|
|
1802
1899
|
switch Internal.EffectCache.fromTableName(tableName) {
|
|
@@ -1856,6 +1953,7 @@ let make = (
|
|
|
1856
1953
|
let queries = makeInitializeTransaction(
|
|
1857
1954
|
~pgSchema,
|
|
1858
1955
|
~pgUser,
|
|
1956
|
+
~checkpointSequence=CheckpointSequence.fromEntities(entities),
|
|
1859
1957
|
~entities=pgEntities,
|
|
1860
1958
|
~enums,
|
|
1861
1959
|
~chainConfigs,
|
|
@@ -1925,15 +2023,16 @@ let make = (
|
|
|
1925
2023
|
firstEventBlockNumber: None,
|
|
1926
2024
|
timestampCaughtUpToHeadOrEndblock: None,
|
|
1927
2025
|
addressRows: rowsByChain->Array.getUnsafe(idx)->AddressRows.seedRowsOf,
|
|
2026
|
+
progressBlockTime: None,
|
|
1928
2027
|
sourceBlockNumber: 0,
|
|
1929
2028
|
}),
|
|
1930
|
-
|
|
2029
|
+
checkpointFrontier: Frontier.empty(),
|
|
1931
2030
|
}
|
|
1932
2031
|
}
|
|
1933
2032
|
|
|
1934
2033
|
let loadOrThrow = async (~filter: EntityFilter.t, ~table: Table.table) => {
|
|
1935
2034
|
let params = []
|
|
1936
|
-
let condition = makeFilterCondition(~filter, ~table, ~params)
|
|
2035
|
+
let condition = makeFilterCondition(~filter, ~table, ~pgSchema, ~params)
|
|
1937
2036
|
switch await sql->Postgres.preparedUnsafe(
|
|
1938
2037
|
makeLoadQuery(~pgSchema, ~tableName=table.tableName, ~condition),
|
|
1939
2038
|
params->Obj.magic,
|
|
@@ -1964,9 +2063,10 @@ let make = (
|
|
|
1964
2063
|
let queryFields = table->Table.queryFields
|
|
1965
2064
|
let columns = []
|
|
1966
2065
|
let seen = Utils.Set.make()
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
2066
|
+
filters->Array.forEach(filter =>
|
|
2067
|
+
filter
|
|
2068
|
+
->EntityFilter.entries
|
|
2069
|
+
->Utils.Dict.forEachWithKey((_, fieldName) =>
|
|
1970
2070
|
switch queryFields->Utils.Dict.dangerouslyGetNonOption(fieldName) {
|
|
1971
2071
|
| Some({pgDbFieldName}) =>
|
|
1972
2072
|
if !(seen->Utils.Set.has(pgDbFieldName)) {
|
|
@@ -1975,9 +2075,8 @@ let make = (
|
|
|
1975
2075
|
}
|
|
1976
2076
|
| None => ()
|
|
1977
2077
|
}
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
filters->Array.forEach(collect)
|
|
2078
|
+
)
|
|
2079
|
+
)
|
|
1981
2080
|
columns
|
|
1982
2081
|
}
|
|
1983
2082
|
|
|
@@ -2012,11 +2111,31 @@ let make = (
|
|
|
2012
2111
|
})
|
|
2013
2112
|
}
|
|
2014
2113
|
|
|
2015
|
-
let
|
|
2016
|
-
|
|
2114
|
+
let partitionChainIds = chainIds => isolated ? Some(chainIds) : None
|
|
2115
|
+
|
|
2116
|
+
// The physical table a query index for `scope` is built on. A per-chain
|
|
2117
|
+
// entity's query carries the scope's chain id, so it is planned against that
|
|
2118
|
+
// chain's partition, which an index on the parent covers by cascading.
|
|
2119
|
+
let queryTableName = (~entityConfig: Internal.entityConfig, ~scope: Internal.chainScope) =>
|
|
2120
|
+
switch scope {
|
|
2121
|
+
| Chain(chainId) =>
|
|
2122
|
+
indexTableNames(
|
|
2123
|
+
entityConfig,
|
|
2124
|
+
~partitionChainIds=partitionChainIds([chainId]),
|
|
2125
|
+
)->Array.getUnsafe(0)
|
|
2126
|
+
| CrossChain => entityConfig.table.tableName
|
|
2127
|
+
}
|
|
2128
|
+
|
|
2129
|
+
let ensureQueryIndexes = async (
|
|
2130
|
+
~entityConfig: Internal.entityConfig,
|
|
2131
|
+
~scope: Internal.chainScope,
|
|
2132
|
+
~filters: array<EntityFilter.t>,
|
|
2133
|
+
) => {
|
|
2134
|
+
let tableName = queryTableName(~entityConfig, ~scope)
|
|
2135
|
+
let columns = filterColumns(~table=entityConfig.table, ~filters)
|
|
2017
2136
|
let _ = await columns
|
|
2018
2137
|
->Array.map(column => {
|
|
2019
|
-
let definition = IndexDefinition.single(~tableName
|
|
2138
|
+
let definition = IndexDefinition.single(~tableName, ~column)
|
|
2020
2139
|
indexManager
|
|
2021
2140
|
->IndexManager.ensure(~definition, ~coverage=LeadingColumns, ~build=async () => {
|
|
2022
2141
|
// Resolved before logging so a rebuild is reported as one, and an
|
|
@@ -2033,7 +2152,7 @@ let make = (
|
|
|
2033
2152
|
// actually creates the index, not the ones waiting on it.
|
|
2034
2153
|
Logging.info({
|
|
2035
2154
|
"storage": storageName,
|
|
2036
|
-
"msg": `${verb} "${prepared.name}" to serve a getWhere query on "${
|
|
2155
|
+
"msg": `${verb} "${prepared.name}" to serve a getWhere query on "${tableName}". Writes to the table are paused until it completes. ${slowOnLargeDatabaseNotice}`,
|
|
2037
2156
|
})
|
|
2038
2157
|
let timeRef = Performance.now()
|
|
2039
2158
|
let entry = await sql->runAndVerify(prepared)
|
|
@@ -2049,7 +2168,7 @@ let make = (
|
|
|
2049
2168
|
->Promise.catch(async exn => {
|
|
2050
2169
|
Logging.warn({
|
|
2051
2170
|
"storage": storageName,
|
|
2052
|
-
"msg": `Failed to create an index on "${
|
|
2171
|
+
"msg": `Failed to create an index on "${tableName}"("${column}") for a getWhere query. The query runs without it.`,
|
|
2053
2172
|
"err": exn->Utils.prettifyExn,
|
|
2054
2173
|
})
|
|
2055
2174
|
await resyncIndex(definition->IndexDefinition.name)
|
|
@@ -2063,9 +2182,10 @@ let make = (
|
|
|
2063
2182
|
// is already ready, so handlers may be issuing getWhere queries alongside it
|
|
2064
2183
|
// and the per-table queues are what keep the two from colliding. Nothing here
|
|
2065
2184
|
// writes `ready_at` — the chains already carry theirs.
|
|
2066
|
-
let ensureSchemaIndexes = async (~entities: array<Internal.entityConfig
|
|
2185
|
+
let ensureSchemaIndexes = async (~entities: array<Internal.entityConfig>, ~chainIds) => {
|
|
2067
2186
|
let schemaIndexes = getSchemaIndexes(
|
|
2068
2187
|
~entities=entities->Array.filter((e: Internal.entityConfig) => e.storage.postgres),
|
|
2188
|
+
~partitionChainIds=?partitionChainIds(chainIds),
|
|
2069
2189
|
)
|
|
2070
2190
|
|
|
2071
2191
|
let _ = await schemaIndexes
|
|
@@ -2116,6 +2236,7 @@ let make = (
|
|
|
2116
2236
|
) => {
|
|
2117
2237
|
let schemaIndexes = getSchemaIndexes(
|
|
2118
2238
|
~entities=entities->Array.filter((e: Internal.entityConfig) => e.storage.postgres),
|
|
2239
|
+
~partitionChainIds=?partitionChainIds(chainIds),
|
|
2119
2240
|
)
|
|
2120
2241
|
|
|
2121
2242
|
// Resolved up front so a name held by an unrelated index fails before any
|
|
@@ -2181,9 +2302,9 @@ let make = (
|
|
|
2181
2302
|
// crash either leaves `ready_at` null and the retry finds the indexes
|
|
2182
2303
|
// already built, or commits readiness the schema backs.
|
|
2183
2304
|
//
|
|
2184
|
-
// One transaction for the whole set:
|
|
2185
|
-
// a crash part way through would otherwise leave some
|
|
2186
|
-
//
|
|
2305
|
+
// One transaction for the whole set: the chains this process drives caught
|
|
2306
|
+
// up together, and a crash part way through would otherwise leave some of
|
|
2307
|
+
// them stamped and some not.
|
|
2187
2308
|
let setReadyAtQuery = InternalTable.Chains.makeSetReadyAtQuery(~pgSchema)
|
|
2188
2309
|
let _ = await sql->Postgres.beginSql(async sql => {
|
|
2189
2310
|
for idx in 0 to chainIds->Array.length - 1 {
|
|
@@ -2232,11 +2353,6 @@ let make = (
|
|
|
2232
2353
|
let _ = await sql->Postgres.unsafe(
|
|
2233
2354
|
makeCreateTableQuery(table, ~pgSchema, ~isNumericArrayAsText=false),
|
|
2234
2355
|
)
|
|
2235
|
-
// Integration with other tools like Hasura
|
|
2236
|
-
switch onNewTables {
|
|
2237
|
-
| Some(onNewTables) => await onNewTables(~tableNames=[table.tableName])
|
|
2238
|
-
| None => ()
|
|
2239
|
-
}
|
|
2240
2356
|
}
|
|
2241
2357
|
|
|
2242
2358
|
await setOrThrow(~items, ~table, ~itemSchema)
|
|
@@ -2313,36 +2429,49 @@ let make = (
|
|
|
2313
2429
|
}
|
|
2314
2430
|
}
|
|
2315
2431
|
|
|
2316
|
-
let resumeInitialState = async (
|
|
2432
|
+
let resumeInitialState = async (
|
|
2433
|
+
~entities,
|
|
2434
|
+
~chainIds,
|
|
2435
|
+
~throwIfIncompatible,
|
|
2436
|
+
): Persistence.initialState => {
|
|
2317
2437
|
let (
|
|
2318
2438
|
cache,
|
|
2319
|
-
chains,
|
|
2320
|
-
checkpointIdResult,
|
|
2439
|
+
(chains, checkpointFrontier),
|
|
2321
2440
|
reorgCheckpoints,
|
|
2322
2441
|
(storedEnvioInfo, storedContractMapping),
|
|
2323
|
-
) = await Promise.
|
|
2442
|
+
) = await Promise.all4((
|
|
2324
2443
|
restoreEffectCache(~withUpload=false),
|
|
2325
2444
|
InternalTable.Chains.getInitialState(
|
|
2326
2445
|
sql,
|
|
2327
2446
|
~pgSchema,
|
|
2328
2447
|
)->Promise.thenResolve(rawInitialStates => {
|
|
2329
|
-
rawInitialStates
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2448
|
+
let rawInitialStates =
|
|
2449
|
+
rawInitialStates->Array.filter(rawInitialState =>
|
|
2450
|
+
chainIds->Array.includes(rawInitialState.id)
|
|
2451
|
+
)
|
|
2452
|
+
(
|
|
2453
|
+
rawInitialStates->Array.map((rawInitialState): Persistence.initialChainState => {
|
|
2454
|
+
id: rawInitialState.id,
|
|
2455
|
+
startBlock: rawInitialState.startBlock,
|
|
2456
|
+
endBlock: rawInitialState.endBlock->Null.toOption,
|
|
2457
|
+
maxReorgDepth: rawInitialState.maxReorgDepth,
|
|
2458
|
+
firstEventBlockNumber: rawInitialState.firstEventBlockNumber->Null.toOption,
|
|
2459
|
+
timestampCaughtUpToHeadOrEndblock: rawInitialState.timestampCaughtUpToHeadOrEndblock->Null.toOption,
|
|
2460
|
+
numEventsProcessed: rawInitialState.numEventsProcessed,
|
|
2461
|
+
progressBlockNumber: rawInitialState.progressBlockNumber,
|
|
2462
|
+
progressBlockTime: rawInitialState.progressBlockTime->InternalTable.Chains.blockTimeFromDb,
|
|
2463
|
+
addressRows: rawInitialState.addressRows,
|
|
2464
|
+
sourceBlockNumber: rawInitialState.sourceBlockNumber,
|
|
2465
|
+
}),
|
|
2466
|
+
Frontier.fromEntries(
|
|
2467
|
+
rawInitialStates->Array.map(rawInitialState => (
|
|
2468
|
+
rawInitialState.id,
|
|
2469
|
+
rawInitialState.checkpointId->BigInt.fromStringOrThrow,
|
|
2470
|
+
)),
|
|
2471
|
+
),
|
|
2472
|
+
)
|
|
2341
2473
|
}),
|
|
2342
2474
|
sql
|
|
2343
|
-
->Postgres.unsafe(InternalTable.Checkpoints.makeCommitedCheckpointIdQuery(~pgSchema))
|
|
2344
|
-
->(Utils.magic: promise<array<unknown>> => promise<array<{"id": string}>>),
|
|
2345
|
-
sql
|
|
2346
2475
|
->Postgres.unsafe(InternalTable.Checkpoints.makeGetReorgCheckpointsQuery(~pgSchema))
|
|
2347
2476
|
->(
|
|
2348
2477
|
Utils.magic: promise<array<unknown>> => promise<
|
|
@@ -2372,8 +2501,6 @@ let make = (
|
|
|
2372
2501
|
|
|
2373
2502
|
await reloadIndexCatalog()
|
|
2374
2503
|
|
|
2375
|
-
let checkpointId = (checkpointIdResult->Array.getUnsafe(0))["id"]->BigInt.fromStringOrThrow
|
|
2376
|
-
|
|
2377
2504
|
// Convert string checkpoint IDs from DB to bigint
|
|
2378
2505
|
let reorgCheckpoints = Array.map(reorgCheckpoints, (raw): Internal.reorgCheckpoint => {
|
|
2379
2506
|
checkpointId: raw["id"]->BigInt.fromStringOrThrow,
|
|
@@ -2384,7 +2511,7 @@ let make = (
|
|
|
2384
2511
|
|
|
2385
2512
|
// Resume sink if present - needed to rollback any reorg changes
|
|
2386
2513
|
switch sink {
|
|
2387
|
-
| Some(sink) => await sink.resume(~
|
|
2514
|
+
| Some(sink) => await sink.resume(~frontier=checkpointFrontier, ~chains, ~entities)
|
|
2388
2515
|
| None => ()
|
|
2389
2516
|
}
|
|
2390
2517
|
|
|
@@ -2393,7 +2520,7 @@ let make = (
|
|
|
2393
2520
|
reorgCheckpoints,
|
|
2394
2521
|
cache,
|
|
2395
2522
|
chains,
|
|
2396
|
-
|
|
2523
|
+
checkpointFrontier,
|
|
2397
2524
|
contractMapping: storedContractMapping,
|
|
2398
2525
|
envioInfo: storedEnvioInfo,
|
|
2399
2526
|
}
|
|
@@ -2434,7 +2561,7 @@ let make = (
|
|
|
2434
2561
|
InternalTable.Checkpoints.getRollbackProgressDiff(sql, ~pgSchema, ~floors)
|
|
2435
2562
|
|
|
2436
2563
|
let getRollbackData = async (~entityConfig: Internal.entityConfig, ~floors: RollbackFloors.t) => {
|
|
2437
|
-
let params = floors.
|
|
2564
|
+
let params = floors.checkpointBounds->CheckpointSequence.params
|
|
2438
2565
|
let (removedIdRows, rollbackRows) = await Promise.all2((
|
|
2439
2566
|
// Get IDs of entities that should be deleted (created after rollback target with no prior history)
|
|
2440
2567
|
sql
|
|
@@ -2482,7 +2609,6 @@ let make = (
|
|
|
2482
2609
|
let writeBatchMethod = async (
|
|
2483
2610
|
~batch,
|
|
2484
2611
|
~rollback,
|
|
2485
|
-
~isInReorgThreshold,
|
|
2486
2612
|
~config,
|
|
2487
2613
|
~allEntities,
|
|
2488
2614
|
~updatedEffectsCache,
|
|
@@ -2509,7 +2635,14 @@ let make = (
|
|
|
2509
2635
|
| Some(sink) => {
|
|
2510
2636
|
let timerRef = Performance.now()
|
|
2511
2637
|
Some(
|
|
2512
|
-
sink.writeBatch(
|
|
2638
|
+
sink.writeBatch(
|
|
2639
|
+
~batch,
|
|
2640
|
+
~diffCheckpoints=switch (rollback: option<Persistence.rollback>) {
|
|
2641
|
+
| Some({diffCheckpoints}) => diffCheckpoints
|
|
2642
|
+
| None => []
|
|
2643
|
+
},
|
|
2644
|
+
~updatedEntities=chUpdates,
|
|
2645
|
+
)
|
|
2513
2646
|
->Promise.thenResolve(_ => {
|
|
2514
2647
|
onWrite(~storage=sink.name, ~timeSeconds=timerRef->Performance.secondsSince)
|
|
2515
2648
|
None
|
|
@@ -2528,7 +2661,6 @@ let make = (
|
|
|
2528
2661
|
~pgSchema,
|
|
2529
2662
|
~setQueryCache,
|
|
2530
2663
|
~rollback,
|
|
2531
|
-
~isInReorgThreshold,
|
|
2532
2664
|
~config,
|
|
2533
2665
|
~allEntities,
|
|
2534
2666
|
~setEffectCacheOrThrow,
|
|
@@ -2581,6 +2713,7 @@ let makeStorageFromEnv = (
|
|
|
2581
2713
|
~pgPassword=Env.Db.password,
|
|
2582
2714
|
~chainIdMode=config.chainIdMode,
|
|
2583
2715
|
~ecosystem=config.ecosystem.name,
|
|
2716
|
+
~isolated=config.isolated,
|
|
2584
2717
|
~sink=?{
|
|
2585
2718
|
// Internally ClickHouse storage is implemented as a sync of the
|
|
2586
2719
|
// Postgres storage. Required env vars are validated here only when
|
|
@@ -2613,6 +2746,7 @@ let makeStorageFromEnv = (
|
|
|
2613
2746
|
~database=database->Option.getUnsafe,
|
|
2614
2747
|
~username=username->Option.getUnsafe,
|
|
2615
2748
|
~password=password->Option.getUnsafe,
|
|
2749
|
+
~sequence=config.checkpointSequence,
|
|
2616
2750
|
~chainIdMode=config.chainIdMode,
|
|
2617
2751
|
),
|
|
2618
2752
|
)
|
|
@@ -2644,34 +2778,6 @@ let makeStorageFromEnv = (
|
|
|
2644
2778
|
None
|
|
2645
2779
|
}
|
|
2646
2780
|
},
|
|
2647
|
-
~onNewTables=?{
|
|
2648
|
-
if isHasuraEnabled {
|
|
2649
|
-
Some(
|
|
2650
|
-
(~tableNames) => {
|
|
2651
|
-
Hasura.trackTables(
|
|
2652
|
-
~endpoint=Env.Hasura.graphqlEndpoint,
|
|
2653
|
-
~auth={
|
|
2654
|
-
role: Env.Hasura.role,
|
|
2655
|
-
secret: Env.Hasura.secret,
|
|
2656
|
-
},
|
|
2657
|
-
~pgSchema,
|
|
2658
|
-
~tableConfigs=tableNames->Array.map(tableName => {
|
|
2659
|
-
Hasura.tableName,
|
|
2660
|
-
description: None,
|
|
2661
|
-
columnConfigs: dict{},
|
|
2662
|
-
}),
|
|
2663
|
-
)->Promise.catch(err => {
|
|
2664
|
-
Logging.errorWithExn(
|
|
2665
|
-
err->Utils.prettifyExn,
|
|
2666
|
-
`Error tracking new tables`,
|
|
2667
|
-
)->Promise.resolve
|
|
2668
|
-
})
|
|
2669
|
-
},
|
|
2670
|
-
)
|
|
2671
|
-
} else {
|
|
2672
|
-
None
|
|
2673
|
-
}
|
|
2674
|
-
},
|
|
2675
2781
|
~isHasuraEnabled,
|
|
2676
2782
|
)
|
|
2677
2783
|
}
|