envio 3.15.0 → 3.16.0-subgraph
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/README.md +40 -34
- package/evm.schema.json +11 -2
- package/fuel.schema.json +10 -1
- package/index.d.ts +6 -0
- package/licenses/README.md +1 -1
- package/package.json +10 -9
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +7 -0
- package/src/Bin.res.mjs +8 -0
- package/src/ChainFetching.res +450 -16
- package/src/ChainFetching.res.mjs +403 -32
- package/src/ChainState.res +124 -7
- package/src/ChainState.res.mjs +128 -67
- package/src/ChainState.resi +26 -0
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +31 -3
- package/src/Config.res.mjs +15 -4
- package/src/ContractRegisterContext.res +3 -6
- package/src/ContractRegisterContext.res.mjs +4 -2
- package/src/Core.res +95 -36
- package/src/Core.res.mjs +90 -1
- package/src/CrossChainState.res +12 -2
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +7 -4
- package/src/Ecosystem.res.mjs +8 -8
- package/src/EffectCacheLookup.res +98 -0
- package/src/EffectCacheLookup.res.mjs +116 -0
- package/src/EffectState.res +10 -2
- package/src/EffectState.res.mjs +8 -4
- package/src/EffectState.resi +3 -1
- package/src/EntityId.res +2 -7
- package/src/EntityId.res.mjs +1 -8
- package/src/EntityTables.res +4 -1
- package/src/EntityTables.res.mjs +1 -1
- package/src/Env.res +20 -0
- package/src/Env.res.mjs +16 -0
- package/src/EventProcessing.res +38 -1
- package/src/EventProcessing.res.mjs +28 -1
- package/src/FetchState.res +408 -73
- package/src/FetchState.res.mjs +361 -91
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +39 -15
- package/src/HandlerLoader.res.mjs +27 -8
- package/src/HandlerRegister.res +4 -0
- package/src/HandlerRegister.res.mjs +5 -2
- package/src/HandlerRegister.resi +2 -0
- package/src/InMemoryTable.res +17 -16
- package/src/InMemoryTable.res.mjs +14 -15
- package/src/IndexerState.res +28 -4
- package/src/IndexerState.res.mjs +19 -6
- package/src/IndexerState.resi +4 -0
- package/src/Internal.res +61 -49
- package/src/Internal.res.mjs +36 -80
- package/src/LoadLayer.res +54 -15
- package/src/LoadLayer.res.mjs +75 -45
- package/src/LoadLayer.resi +26 -0
- package/src/Main.res +14 -1
- package/src/Main.res.mjs +4 -3
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/PreloadLookahead.res +202 -0
- package/src/PreloadLookahead.res.mjs +218 -0
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +3 -0
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +42 -26
- package/src/SimulateItems.res.mjs +40 -16
- package/src/StagedRows.res +106 -0
- package/src/StagedRows.res.mjs +102 -0
- package/src/Staging.res +32 -14
- package/src/Staging.res.mjs +17 -20
- package/src/Storage.res +303 -948
- package/src/Storage.res.mjs +229 -708
- package/src/SubgraphLog.res +19 -0
- package/src/SubgraphLog.res.mjs +26 -0
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- package/src/SyncWrites.res +107 -0
- package/src/SyncWrites.res.mjs +98 -0
- package/src/TestIndexer.res +22 -27
- package/src/TestIndexer.res.mjs +20 -17
- package/src/Throttler.res +15 -8
- package/src/Throttler.res.mjs +9 -6
- package/src/Throttler.resi +1 -1
- package/src/UserContext.res +419 -46
- package/src/UserContext.res.mjs +318 -30
- package/src/Utils.res +17 -6
- package/src/Utils.res.mjs +12 -6
- package/src/Writing.res +0 -1
- package/src/Writing.res.mjs +1 -1
- package/src/bindings/ClickHouse.res +0 -322
- package/src/bindings/ClickHouse.res.mjs +0 -338
- package/src/bindings/StorageClient.res +108 -174
- package/src/bindings/StorageClient.res.mjs +19 -8
- package/src/bindings/Vitest.res +0 -26
- package/src/bindings/Vitest.res.mjs +0 -3
- package/src/db/Table.res +24 -150
- package/src/db/Table.res.mjs +38 -272
- package/src/sources/AddressSet.res +2 -0
- package/src/sources/AddressStore.res +3 -0
- package/src/sources/CallSource.res +274 -0
- package/src/sources/CallSource.res.mjs +196 -0
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +10 -1
- package/src/sources/EvmChain.res.mjs +6 -3
- package/src/sources/EvmHyperSyncSource.res +2 -2
- package/src/sources/EvmHyperSyncSource.res.mjs +1 -1
- package/src/sources/EvmRpcClient.res +13 -3
- package/src/sources/EvmRpcClient.res.mjs +4 -3
- package/src/sources/EvmRpcWs.res +4 -3
- package/src/sources/EvmRpcWs.res.mjs +2 -2
- package/src/sources/HeightFeed.res +31 -10
- package/src/sources/HeightFeed.res.mjs +10 -6
- package/src/sources/HeightStream.res +32 -17
- package/src/sources/HeightStream.res.mjs +10 -10
- package/src/sources/HyperSync.res +2 -1
- package/src/sources/HyperSync.res.mjs +2 -1
- package/src/sources/HyperSyncSSE.res +4 -6
- package/src/sources/HyperSyncSSE.res.mjs +2 -2
- package/src/sources/RpcSource.res +10 -1
- package/src/sources/RpcSource.res.mjs +4 -3
- package/src/sources/Source.res +3 -0
- package/src/sources/SourceManager.res +84 -77
- package/src/sources/SourceManager.res.mjs +17 -20
- package/src/sources/SourceManager.resi +3 -4
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/src/subgraph/abi-types.ts +84 -0
- package/src/subgraph/aggregations.ts +745 -0
- package/src/subgraph/as-package.ts +143 -0
- package/src/subgraph/assemblyscript-regex.ts +67 -0
- package/src/subgraph/assemblyscript.ts +731 -0
- package/src/subgraph/blocks.ts +398 -0
- package/src/subgraph/calls-source.ts +702 -0
- package/src/subgraph/calls.ts +258 -0
- package/src/subgraph/conformance.ts +106 -0
- package/src/subgraph/errors.ts +85 -0
- package/src/subgraph/graft.ts +73 -0
- package/src/subgraph/graph-cli-worker.mjs +35 -0
- package/src/subgraph/graph-cli.ts +900 -0
- package/src/subgraph/graph-node-decimal.ts +199 -0
- package/src/subgraph/graph-ts-enums.ts +28 -0
- package/src/subgraph/graph-ts-types/VERSION +2 -0
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +70 -0
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +327 -0
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +233 -0
- package/src/subgraph/graph-ts-types/chain/near.d.ts +253 -0
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +32 -0
- package/src/subgraph/graph-ts-types/common/collections.d.ts +136 -0
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +11 -0
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +30 -0
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +17 -0
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/value.d.ts +120 -0
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +90 -0
- package/src/subgraph/graph-ts-types/global/global.d.ts +194 -0
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +22 -0
- package/src/subgraph/graph-ts-types/index.d.ts +102 -0
- package/src/subgraph/graph-ts.ts +4339 -0
- package/src/subgraph/hosts.ts +554 -0
- package/src/subgraph/log.ts +28 -0
- package/src/subgraph/receipts.ts +184 -0
- package/src/subgraph/register-slots.ts +93 -0
- package/src/subgraph/rollup.ts +109 -0
- package/src/subgraph/rpc.ts +337 -0
- package/src/subgraph/runtime.ts +2059 -0
- package/src/subgraph/scope.ts +176 -0
- package/src/subgraph/stats.ts +42 -0
- package/src/subgraph/template-instances.ts +66 -0
- package/src/subgraph/toolchain.ts +327 -0
- package/src/subgraph/transient.ts +176 -0
- package/svm.schema.json +11 -2
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -67
package/src/ChainFetching.res
CHANGED
|
@@ -11,21 +11,42 @@ type partitionQueryResponse = {
|
|
|
11
11
|
let hasContractRegister = (item: Internal.item) =>
|
|
12
12
|
switch item {
|
|
13
13
|
| Event({onEventRegistration: {contractRegister: Some(_)}}) => true
|
|
14
|
+
| Block({onBlockRegistration: {contractRegister: ?Some(_)}}) => true
|
|
14
15
|
| Event(_) | Block(_) => false
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
let invokeContractRegister = (runtime: HandlerRuntime.t, item: Internal.item) =>
|
|
18
|
-
|
|
19
|
-
| {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
let invokeContractRegister = (runtime: HandlerRuntime.t, item: Internal.item) =>
|
|
19
|
+
switch item {
|
|
20
|
+
| Event({onEventRegistration}) =>
|
|
21
|
+
let contractRegister = switch onEventRegistration {
|
|
22
|
+
| {contractRegister: Some(contractRegister)} => contractRegister
|
|
23
|
+
| {contractRegister: None, eventConfig: {name: eventName}} =>
|
|
24
|
+
// Unexpected case, since we should pass only events with contract register to this function
|
|
25
|
+
JsError.throwWithMessage("Contract register is not set for event " ++ eventName)
|
|
26
|
+
}
|
|
27
|
+
contractRegister({
|
|
28
|
+
event: item->Ecosystem.getItemEvent(~ecosystem=runtime->HandlerRuntime.ecosystem),
|
|
29
|
+
context: runtime.contractRegisterContext,
|
|
30
|
+
})
|
|
31
|
+
| Block({blockNumber, onBlockRegistration, ?block}) =>
|
|
32
|
+
let contractRegister = switch onBlockRegistration {
|
|
33
|
+
| {contractRegister: ?Some(contractRegister)} => contractRegister
|
|
34
|
+
| {name} => JsError.throwWithMessage("Contract register is not set for block handler " ++ name)
|
|
35
|
+
}
|
|
36
|
+
let ecosystem = runtime->HandlerRuntime.ecosystem
|
|
37
|
+
let context = runtime.contractRegisterContext
|
|
38
|
+
// A block handler's register pass gets what its handler gets, the block (or
|
|
39
|
+
// slot), with the register context in place of the handler's.
|
|
40
|
+
contractRegister(
|
|
41
|
+
switch ecosystem.name {
|
|
42
|
+
| Svm => {slot: blockNumber, ?block, context}
|
|
43
|
+
| Evm | Fuel => {
|
|
44
|
+
block: block->Option.getOr(Ecosystem.makeKeyOnlyBlock(~blockNumber, ~ecosystem)),
|
|
45
|
+
context,
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
)
|
|
23
49
|
}
|
|
24
|
-
contractRegister({
|
|
25
|
-
event: item->Ecosystem.getItemEvent(~ecosystem=runtime->HandlerRuntime.ecosystem),
|
|
26
|
-
context: runtime.contractRegisterContext,
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
50
|
|
|
30
51
|
let runContractRegistersOrThrow = async (
|
|
31
52
|
~itemsWithContractRegister: array<Internal.item>,
|
|
@@ -36,8 +57,11 @@ let runContractRegistersOrThrow = async (
|
|
|
36
57
|
// materialise the selected fields onto the payloads before running them. All
|
|
37
58
|
// items belong to the chain being fetched, and both stores are the chain's
|
|
38
59
|
// own, which this response's pages have already been merged into.
|
|
60
|
+
// A subgraph's block register passes ride along; their blocks have no page.
|
|
39
61
|
await ChainState.materializePageItems(
|
|
40
|
-
~items=
|
|
62
|
+
~items=Env.subgraphMode.contents
|
|
63
|
+
? itemsWithContractRegister->Array.filter(item => item->Internal.getItemKind === 0)
|
|
64
|
+
: itemsWithContractRegister,
|
|
41
65
|
~transactionStore=chainState->ChainState.transactionStore,
|
|
42
66
|
~blockStore=chainState->ChainState.blockStore,
|
|
43
67
|
~shouldChecksum=chainState->ChainState.shouldChecksum,
|
|
@@ -47,6 +71,7 @@ let runContractRegistersOrThrow = async (
|
|
|
47
71
|
let registrations: array<AddressStore.registration> = []
|
|
48
72
|
let chainLogger = chainState->ChainState.logger
|
|
49
73
|
let ecosystem = runtime->HandlerRuntime.ecosystem
|
|
74
|
+
let isSubgraph = Env.subgraphMode.contents
|
|
50
75
|
|
|
51
76
|
let _ = await itemsWithContractRegister
|
|
52
77
|
->Array.map(async item => {
|
|
@@ -56,13 +81,32 @@ let runContractRegistersOrThrow = async (
|
|
|
56
81
|
chainLogger,
|
|
57
82
|
ecosystem,
|
|
58
83
|
registrations,
|
|
84
|
+
creationRound: ?(
|
|
85
|
+
isSubgraph
|
|
86
|
+
? Some(
|
|
87
|
+
switch item {
|
|
88
|
+
// A block handler is one of its block's own triggers.
|
|
89
|
+
| Block(_) => 1
|
|
90
|
+
| Event(_) =>
|
|
91
|
+
chainState->ChainState.fetchState->FetchState.creationRoundOfRegistration(~item)
|
|
92
|
+
},
|
|
93
|
+
)
|
|
94
|
+
: None
|
|
95
|
+
),
|
|
59
96
|
}
|
|
60
97
|
let scope = RunScope.ContractRegister(run)
|
|
61
98
|
switch await RunScope.enter(scope, invokeContractRegister, runtime, item) {
|
|
62
99
|
| exception exn =>
|
|
63
100
|
scope->RunScope.resolve
|
|
64
101
|
exn->ErrorHandling.mkLogAndRaise(
|
|
65
|
-
~msg=
|
|
102
|
+
~msg=switch item {
|
|
103
|
+
| Event(
|
|
104
|
+
_,
|
|
105
|
+
) => "Event contractRegister failed, please fix the error to keep the indexer running smoothly"
|
|
106
|
+
| Block(
|
|
107
|
+
_,
|
|
108
|
+
) => "Block handler contractRegister failed, please fix the error to keep the indexer running smoothly"
|
|
109
|
+
},
|
|
66
110
|
~logger=run->RunScope.contractRegisterLogger,
|
|
67
111
|
)
|
|
68
112
|
| () => scope->RunScope.resolve
|
|
@@ -73,6 +117,221 @@ let runContractRegistersOrThrow = async (
|
|
|
73
117
|
registrations
|
|
74
118
|
}
|
|
75
119
|
|
|
120
|
+
// A response's events and the block passes of the same blocks, in the order
|
|
121
|
+
// graph-node runs their triggers: a `once` handler first in its block, the
|
|
122
|
+
// other block handlers after the block's logs and calls
|
|
123
|
+
// (chain/ethereum/src/trigger.rs, `impl Ord for EthereumTrigger`).
|
|
124
|
+
let mergeRegisterItems = (~eventItems: array<Internal.item>, ~blockItems: array<Internal.item>) => {
|
|
125
|
+
let merged = []
|
|
126
|
+
let i = ref(0)
|
|
127
|
+
let j = ref(0)
|
|
128
|
+
while i.contents < eventItems->Array.length || j.contents < blockItems->Array.length {
|
|
129
|
+
let takeBlock = switch (eventItems->Array.get(i.contents), blockItems->Array.get(j.contents)) {
|
|
130
|
+
| (None, _) => true
|
|
131
|
+
| (_, None) => false
|
|
132
|
+
| (Some(event), Some(block)) =>
|
|
133
|
+
let eventBlock = event->Internal.getItemBlockNumber
|
|
134
|
+
let blockBlock = block->Internal.getItemBlockNumber
|
|
135
|
+
blockBlock < eventBlock || (blockBlock === eventBlock && block->Internal.itemRunsFirst)
|
|
136
|
+
}
|
|
137
|
+
if takeBlock {
|
|
138
|
+
merged->Array.push(blockItems->Array.getUnsafe(j.contents))
|
|
139
|
+
j := j.contents + 1
|
|
140
|
+
} else {
|
|
141
|
+
merged->Array.push(eventItems->Array.getUnsafe(i.contents))
|
|
142
|
+
i := i.contents + 1
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
merged
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// An unfiltered handler has a pass for every block, so a run with block passes
|
|
149
|
+
// starts them a chunk at a time, in chain order: each chunk reads what the ones
|
|
150
|
+
// before it saved, and a pass is started before any later one of its chunk,
|
|
151
|
+
// which is the order the runtime hands out its slots in.
|
|
152
|
+
let blockRegisterChunk = 2048
|
|
153
|
+
// Block passes built at once: a response can span millions of blocks.
|
|
154
|
+
let blockRegisterWindow = 10_000
|
|
155
|
+
let maxResponseBlockPasses = 50_000
|
|
156
|
+
|
|
157
|
+
let runRegisterPassesOrThrow = async (
|
|
158
|
+
~eventItems: array<Internal.item>,
|
|
159
|
+
~blockItems: array<Internal.item>,
|
|
160
|
+
~runtime: HandlerRuntime.t,
|
|
161
|
+
~chainState: ChainState.t,
|
|
162
|
+
) => {
|
|
163
|
+
let items = mergeRegisterItems(~eventItems, ~blockItems)
|
|
164
|
+
let registrations = []
|
|
165
|
+
let start = ref(0)
|
|
166
|
+
while start.contents < items->Array.length {
|
|
167
|
+
let chunk = items->Array.slice(~start=start.contents, ~end=start.contents + blockRegisterChunk)
|
|
168
|
+
registrations
|
|
169
|
+
->Array.pushMany(
|
|
170
|
+
await runContractRegistersOrThrow(~itemsWithContractRegister=chunk, ~runtime, ~chainState),
|
|
171
|
+
)
|
|
172
|
+
->ignore
|
|
173
|
+
start := start.contents + blockRegisterChunk
|
|
174
|
+
}
|
|
175
|
+
registrations
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Passes of a chunk finish in any order, and of two registrations of one
|
|
179
|
+
// address the store keeps the first: it has to be the earlier block's.
|
|
180
|
+
let sortRegistrations = (registrations: array<AddressStore.registration>) =>
|
|
181
|
+
registrations->Array.toSorted((a, b) =>
|
|
182
|
+
switch Int.compare(a.registrationBlock, b.registrationBlock) {
|
|
183
|
+
| 0. => Int.compare(a.creationRound->Option.getOr(1), b.creationRound->Option.getOr(1))
|
|
184
|
+
| order => order
|
|
185
|
+
}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
// A response's event passes with the block passes of the blocks after the last
|
|
189
|
+
// one passed, through `through`, a window of block passes at a time. Returns
|
|
190
|
+
// the registrations and the last block passed, if the block passes advanced.
|
|
191
|
+
let runResponsePassesOrThrow = async (
|
|
192
|
+
~eventItems: array<Internal.item>,
|
|
193
|
+
~through,
|
|
194
|
+
~runtime: HandlerRuntime.t,
|
|
195
|
+
~chainState: ChainState.t,
|
|
196
|
+
) => {
|
|
197
|
+
let registrations = []
|
|
198
|
+
let eventIdx = ref(0)
|
|
199
|
+
let passedThrough = ref(None)
|
|
200
|
+
let after = ref(chainState->ChainState.blockRegisterThrough->Option.getOr(through))
|
|
201
|
+
let isDone = ref(false)
|
|
202
|
+
while !isDone.contents {
|
|
203
|
+
let (blockItems, covered) = switch chainState->ChainState.blockRegisterItemsAfter(
|
|
204
|
+
~after=after.contents,
|
|
205
|
+
~through,
|
|
206
|
+
~limit=blockRegisterWindow,
|
|
207
|
+
) {
|
|
208
|
+
| Some((blockItems, covered)) => (blockItems, Some(covered))
|
|
209
|
+
| None => ([], None)
|
|
210
|
+
}
|
|
211
|
+
// The events up to the window's last block go with it, and the last
|
|
212
|
+
// window takes the rest.
|
|
213
|
+
let eventEnd = switch covered {
|
|
214
|
+
| Some(covered) if covered < through =>
|
|
215
|
+
let idx = ref(eventIdx.contents)
|
|
216
|
+
while (
|
|
217
|
+
idx.contents < eventItems->Array.length &&
|
|
218
|
+
eventItems->Array.getUnsafe(idx.contents)->Internal.getItemBlockNumber <= covered
|
|
219
|
+
) {
|
|
220
|
+
idx := idx.contents + 1
|
|
221
|
+
}
|
|
222
|
+
idx.contents
|
|
223
|
+
| _ => eventItems->Array.length
|
|
224
|
+
}
|
|
225
|
+
let startedAt = Date.now()
|
|
226
|
+
let windowRegistrations = await runRegisterPassesOrThrow(
|
|
227
|
+
~eventItems=eventItems->Array.slice(~start=eventIdx.contents, ~end=eventEnd),
|
|
228
|
+
~blockItems,
|
|
229
|
+
~runtime,
|
|
230
|
+
~chainState,
|
|
231
|
+
)
|
|
232
|
+
chainState
|
|
233
|
+
->ChainState.logger
|
|
234
|
+
->Logger.trace(
|
|
235
|
+
"Ran register passes with block passes",
|
|
236
|
+
~params={
|
|
237
|
+
"after": after.contents,
|
|
238
|
+
"through": covered,
|
|
239
|
+
"blockPasses": blockItems->Array.length,
|
|
240
|
+
"eventPasses": eventEnd - eventIdx.contents,
|
|
241
|
+
"registrations": windowRegistrations->Array.length,
|
|
242
|
+
"ms": Date.now() -. startedAt,
|
|
243
|
+
},
|
|
244
|
+
)
|
|
245
|
+
registrations->Array.pushMany(windowRegistrations)->ignore
|
|
246
|
+
eventIdx := eventEnd
|
|
247
|
+
switch covered {
|
|
248
|
+
| Some(covered) =>
|
|
249
|
+
passedThrough := Some(covered)
|
|
250
|
+
after := covered
|
|
251
|
+
isDone := covered >= through && eventIdx.contents >= eventItems->Array.length
|
|
252
|
+
| None => isDone := true
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
(registrations->sortRegistrations, passedThrough.contents)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// A subgraph's register pass for one event can cost a dozen contract calls, and
|
|
259
|
+
// a response is applied only when every pass of it is done: a 5,000-event
|
|
260
|
+
// response of PairCreated made PancakeSwap v2 wait for ~15,000 calls before it
|
|
261
|
+
// committed anything. A response with more register passes than the calls can
|
|
262
|
+
// answer promptly is cut after a block and the rest fetched again; HyperSync
|
|
263
|
+
// answers that far faster than the RPC does. The runtime reports the calls its
|
|
264
|
+
// finished passes made (`__envioSubgraphRegisterCostPerItem`); until it has
|
|
265
|
+
// seen enough passes the cost is assumed to be high, and a run whose passes
|
|
266
|
+
// make no calls is never cut.
|
|
267
|
+
let registerCallsPerItem: unit => option<float> = %raw(`() => {
|
|
268
|
+
const cost = globalThis.__envioSubgraphRegisterCostPerItem
|
|
269
|
+
const value = cost ? cost() : undefined
|
|
270
|
+
return value === undefined ? undefined : value
|
|
271
|
+
}`)
|
|
272
|
+
|
|
273
|
+
// A subgraph's register store keeps an entity's past versions for the passes
|
|
274
|
+
// still to read them, and none starts below the lowest partition frontier.
|
|
275
|
+
let reportRegisterFrontier: (int, int) => unit = %raw(`(chainId, block) => {
|
|
276
|
+
const report = globalThis.__envioSubgraphRegisterFrontier
|
|
277
|
+
if (report) report(chainId, block)
|
|
278
|
+
}`)
|
|
279
|
+
|
|
280
|
+
let registerCallsPerChunk = 3000.
|
|
281
|
+
let unknownCostChunk = 100
|
|
282
|
+
let minRegisterChunk = 100
|
|
283
|
+
|
|
284
|
+
let registerChunkLimit = () =>
|
|
285
|
+
if !Env.subgraphMode.contents {
|
|
286
|
+
None
|
|
287
|
+
} else {
|
|
288
|
+
switch Env.subgraphRegisterChunk {
|
|
289
|
+
| Some(limit) => limit > 0 ? Some(limit) : None
|
|
290
|
+
| None =>
|
|
291
|
+
switch registerCallsPerItem() {
|
|
292
|
+
| None => Some(unknownCostChunk)
|
|
293
|
+
| Some(cost) if cost < 0.5 => None
|
|
294
|
+
| Some(cost) =>
|
|
295
|
+
Some(Pervasives.max(minRegisterChunk, (registerCallsPerChunk /. cost)->Float.toInt))
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// The items and last block of `response` up to the block holding the `limit`th
|
|
301
|
+
// item that has a register pass. Whole blocks only: a block's items are never
|
|
302
|
+
// split between two responses.
|
|
303
|
+
let cutAtRegisterLimit = (items: array<Internal.item>, ~latestFetchedBlock, ~limit) => {
|
|
304
|
+
let seen = ref(0)
|
|
305
|
+
let cutIdx = ref(-1)
|
|
306
|
+
let idx = ref(0)
|
|
307
|
+
while cutIdx.contents < 0 && idx.contents < items->Array.length {
|
|
308
|
+
if items->Array.getUnsafe(idx.contents)->hasContractRegister {
|
|
309
|
+
seen := seen.contents + 1
|
|
310
|
+
if seen.contents >= limit {
|
|
311
|
+
cutIdx := idx.contents
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
idx := idx.contents + 1
|
|
315
|
+
}
|
|
316
|
+
if cutIdx.contents < 0 {
|
|
317
|
+
(items, latestFetchedBlock)
|
|
318
|
+
} else {
|
|
319
|
+
let cutBlock = items->Array.getUnsafe(cutIdx.contents)->Internal.getItemBlockNumber
|
|
320
|
+
let last = ref(cutIdx.contents)
|
|
321
|
+
while (
|
|
322
|
+
last.contents + 1 < items->Array.length &&
|
|
323
|
+
items->Array.getUnsafe(last.contents + 1)->Internal.getItemBlockNumber <= cutBlock
|
|
324
|
+
) {
|
|
325
|
+
last := last.contents + 1
|
|
326
|
+
}
|
|
327
|
+
if last.contents + 1 >= items->Array.length && cutBlock >= latestFetchedBlock {
|
|
328
|
+
(items, latestFetchedBlock)
|
|
329
|
+
} else {
|
|
330
|
+
(items->Array.slice(~start=0, ~end=last.contents + 1), cutBlock)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
76
335
|
let rec onQueryResponse = async (
|
|
77
336
|
state: IndexerState.t,
|
|
78
337
|
{chainId, response, query}: partitionQueryResponse,
|
|
@@ -94,6 +353,11 @@ let rec onQueryResponse = async (
|
|
|
94
353
|
stats,
|
|
95
354
|
knownHeight,
|
|
96
355
|
} = response
|
|
356
|
+
let (parsedQueueItems, latestFetchedBlockNumber) = switch registerChunkLimit() {
|
|
357
|
+
| Some(limit) =>
|
|
358
|
+
parsedQueueItems->cutAtRegisterLimit(~latestFetchedBlock=latestFetchedBlockNumber, ~limit)
|
|
359
|
+
| None => (parsedQueueItems, latestFetchedBlockNumber)
|
|
360
|
+
}
|
|
97
361
|
|
|
98
362
|
chainState->ChainState.recordBlockRangeFetch(
|
|
99
363
|
~totalTimeElapsed=stats.totalTimeElapsed,
|
|
@@ -216,7 +480,12 @@ let rec onQueryResponse = async (
|
|
|
216
480
|
|
|
217
481
|
// Re-check staleness: contract registration is async, so the chain state
|
|
218
482
|
// may have rolled back by the time we apply the fetched items.
|
|
219
|
-
let proceed = (
|
|
483
|
+
let proceed = (
|
|
484
|
+
~newRegistrations,
|
|
485
|
+
~blockRegisterThrough=?,
|
|
486
|
+
~newItems=newItems,
|
|
487
|
+
~latestFetchedBlock=latestFetchedBlockNumber,
|
|
488
|
+
) =>
|
|
220
489
|
if !(state->IndexerState.isStale(~stateId)) {
|
|
221
490
|
applyQueryResponse(
|
|
222
491
|
state,
|
|
@@ -224,17 +493,93 @@ let rec onQueryResponse = async (
|
|
|
224
493
|
~newItems,
|
|
225
494
|
~newRegistrations,
|
|
226
495
|
~knownHeight,
|
|
227
|
-
~latestFetchedBlock
|
|
496
|
+
~latestFetchedBlock,
|
|
228
497
|
~query,
|
|
229
498
|
)
|
|
499
|
+
switch blockRegisterThrough {
|
|
500
|
+
| Some(through) => chainState->ChainState.markBlockRegistered(~through)
|
|
501
|
+
| None => ()
|
|
502
|
+
}
|
|
230
503
|
ChainMetadata.stage(state)
|
|
231
504
|
scheduleFetch()
|
|
232
505
|
scheduleProcessing()
|
|
233
506
|
}
|
|
234
507
|
|
|
508
|
+
// The block passes run with the response that first fetches their
|
|
509
|
+
// blocks, among its events in chain order, and after every earlier unit
|
|
510
|
+
// of the chain's register work.
|
|
511
|
+
let blockRegisterCeiling = Pervasives.min(
|
|
512
|
+
latestFetchedBlockNumber,
|
|
513
|
+
Pervasives.max(knownHeight, chainState->ChainState.knownHeight),
|
|
514
|
+
)
|
|
515
|
+
let withBlockPasses =
|
|
516
|
+
chainState->ChainState.hasBlockRegisters &&
|
|
517
|
+
(itemsWithContractRegister->Utils.Array.notEmpty ||
|
|
518
|
+
chainState->ChainState.canAdvanceBlockRegisters(~through=blockRegisterCeiling))
|
|
519
|
+
|
|
235
520
|
switch itemsWithContractRegister {
|
|
521
|
+
| _ if withBlockPasses =>
|
|
522
|
+
let (previous, finish) = chainState->ChainState.queueBlockRegisterRun
|
|
523
|
+
let held =
|
|
524
|
+
chainState->ChainState.holdExtraBudget(
|
|
525
|
+
~items=Pervasives.max(0, newItems->Array.length - query.itemsEst),
|
|
526
|
+
)
|
|
527
|
+
await previous
|
|
528
|
+
if state->IndexerState.isStale(~stateId) {
|
|
529
|
+
chainState->ChainState.releaseExtraBudget(held)
|
|
530
|
+
finish()
|
|
531
|
+
scheduleFetch()
|
|
532
|
+
} else {
|
|
533
|
+
// A response spanning millions of blocks of an unfiltered handler is
|
|
534
|
+
// applied only once all their passes ran, so it carries at most
|
|
535
|
+
// `maxResponseBlockPasses` past its first block and is cut after
|
|
536
|
+
// them; the partition fetches the rest again.
|
|
537
|
+
let cutAt = switch chainState->ChainState.blockRegisterItemsAfter(
|
|
538
|
+
~after=Pervasives.max(
|
|
539
|
+
chainState->ChainState.blockRegisterThrough->Option.getOr(blockRegisterCeiling),
|
|
540
|
+
query.fromBlock - 1,
|
|
541
|
+
),
|
|
542
|
+
~through=blockRegisterCeiling,
|
|
543
|
+
~limit=maxResponseBlockPasses,
|
|
544
|
+
) {
|
|
545
|
+
| Some((_, covered)) if covered < blockRegisterCeiling => Some(covered)
|
|
546
|
+
| _ => None
|
|
547
|
+
}
|
|
548
|
+
let through = cutAt->Option.getOr(blockRegisterCeiling)
|
|
549
|
+
let (newItems, eventItems, latestFetchedBlock) = if cutAt !== None {
|
|
550
|
+
let isKept = item => item->Internal.getItemBlockNumber <= through
|
|
551
|
+
(
|
|
552
|
+
newItems->Array.filter(isKept),
|
|
553
|
+
itemsWithContractRegister->Array.filter(isKept),
|
|
554
|
+
through,
|
|
555
|
+
)
|
|
556
|
+
} else {
|
|
557
|
+
(newItems, itemsWithContractRegister, latestFetchedBlockNumber)
|
|
558
|
+
}
|
|
559
|
+
switch await runResponsePassesOrThrow(~eventItems, ~through, ~runtime, ~chainState) {
|
|
560
|
+
| exception exn =>
|
|
561
|
+
finish()
|
|
562
|
+
IndexerState.errorExit(
|
|
563
|
+
state,
|
|
564
|
+
exn->ErrorHandling.make(~logger=chainState->ChainState.logger),
|
|
565
|
+
)
|
|
566
|
+
| (newRegistrations, blockRegisterThrough) =>
|
|
567
|
+
chainState->ChainState.releaseExtraBudget(held)
|
|
568
|
+
proceed(~newRegistrations, ~blockRegisterThrough?, ~newItems, ~latestFetchedBlock)
|
|
569
|
+
finish()
|
|
570
|
+
}
|
|
571
|
+
}
|
|
236
572
|
| [] => proceed(~newRegistrations=[])
|
|
237
573
|
| _ =>
|
|
574
|
+
// A subgraph's register passes run in chunks, long enough for the pool
|
|
575
|
+
// to admit queries on top of a response it doesn't count yet.
|
|
576
|
+
let held = Env.subgraphMode.contents
|
|
577
|
+
? Some(
|
|
578
|
+
chainState->ChainState.holdExtraBudget(
|
|
579
|
+
~items=Pervasives.max(0, newItems->Array.length - query.itemsEst),
|
|
580
|
+
),
|
|
581
|
+
)
|
|
582
|
+
: None
|
|
238
583
|
switch await runContractRegistersOrThrow(
|
|
239
584
|
~itemsWithContractRegister,
|
|
240
585
|
~runtime,
|
|
@@ -245,7 +590,9 @@ let rec onQueryResponse = async (
|
|
|
245
590
|
state,
|
|
246
591
|
exn->ErrorHandling.make(~logger=chainState->ChainState.logger),
|
|
247
592
|
)
|
|
248
|
-
| newRegistrations =>
|
|
593
|
+
| newRegistrations =>
|
|
594
|
+
held->Option.forEach(held => chainState->ChainState.releaseExtraBudget(held))
|
|
595
|
+
proceed(~newRegistrations)
|
|
249
596
|
}
|
|
250
597
|
}
|
|
251
598
|
}
|
|
@@ -270,6 +617,21 @@ and applyQueryResponse = (
|
|
|
270
617
|
~knownHeight,
|
|
271
618
|
)
|
|
272
619
|
|
|
620
|
+
if Env.subgraphMode.contents {
|
|
621
|
+
let fetchState = chainState->ChainState.fetchState
|
|
622
|
+
switch fetchState.optimizedPartitions->FetchState.OptimizedPartitions.getLatestFullyFetchedBlock {
|
|
623
|
+
| Some(frontier) =>
|
|
624
|
+
// A block pass still to run reads at its own block, which can be below
|
|
625
|
+
// every partition.
|
|
626
|
+
let frontier = switch fetchState.blockRegisterThrough {
|
|
627
|
+
| Some(through) => Pervasives.min(frontier, through)
|
|
628
|
+
| None => frontier
|
|
629
|
+
}
|
|
630
|
+
reportRegisterFrontier(chainId->ChainId.toInt, frontier)
|
|
631
|
+
| None => ()
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
273
635
|
// In auto-exit mode, set endBlock to the first event's block when events arrive.
|
|
274
636
|
if state->IndexerState.exitAfterFirstEventBlock {
|
|
275
637
|
switch newItems->Array.find(item =>
|
|
@@ -305,6 +667,68 @@ let finishWaitingForNewBlock = (
|
|
|
305
667
|
scheduleProcessing()
|
|
306
668
|
}
|
|
307
669
|
|
|
670
|
+
// Block passes no response will run: the blocks every partition has already
|
|
671
|
+
// fetched (or, with no partition, the blocks up to the head), up to a limit per
|
|
672
|
+
// run. Owns its error boundary, as it isn't awaited.
|
|
673
|
+
let catchUpBlockRegisters = async (
|
|
674
|
+
state: IndexerState.t,
|
|
675
|
+
~chainId,
|
|
676
|
+
~runtime,
|
|
677
|
+
~stateId,
|
|
678
|
+
~scheduleFetch,
|
|
679
|
+
~scheduleProcessing,
|
|
680
|
+
) => {
|
|
681
|
+
let chainState = state->IndexerState.getChainState(~chainId)
|
|
682
|
+
if chainState->ChainState.isBlockRegisterIdle {
|
|
683
|
+
switch chainState->ChainState.nextBlockRegisterItems(
|
|
684
|
+
~through=chainState->ChainState.blockRegisterFrontier,
|
|
685
|
+
~limit=blockRegisterWindow,
|
|
686
|
+
) {
|
|
687
|
+
| None => ()
|
|
688
|
+
| Some((blockItems, through)) =>
|
|
689
|
+
let (_, finish) = chainState->ChainState.queueBlockRegisterRun
|
|
690
|
+
let startedAt = Date.now()
|
|
691
|
+
switch await runRegisterPassesOrThrow(~eventItems=[], ~blockItems, ~runtime, ~chainState) {
|
|
692
|
+
| exception exn =>
|
|
693
|
+
finish()
|
|
694
|
+
IndexerState.errorExit(
|
|
695
|
+
state,
|
|
696
|
+
exn->ErrorHandling.make(~logger=chainState->ChainState.logger),
|
|
697
|
+
)
|
|
698
|
+
| registrations =>
|
|
699
|
+
let isStale = state->IndexerState.isStale(~stateId)
|
|
700
|
+
chainState
|
|
701
|
+
->ChainState.logger
|
|
702
|
+
->Logger.trace(
|
|
703
|
+
"Ran block passes",
|
|
704
|
+
~params={
|
|
705
|
+
"through": through,
|
|
706
|
+
"blockPasses": blockItems->Array.length,
|
|
707
|
+
"registrations": registrations->Array.length,
|
|
708
|
+
"stale": isStale,
|
|
709
|
+
"ms": Date.now() -. startedAt,
|
|
710
|
+
},
|
|
711
|
+
)
|
|
712
|
+
if !isStale {
|
|
713
|
+
chainState->ChainState.applyBlockRegisters(
|
|
714
|
+
~registrations=registrations->sortRegistrations,
|
|
715
|
+
~through,
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
finish()
|
|
719
|
+
|
|
720
|
+
// A run dropped as stale is run again: the fetch tick that would have
|
|
721
|
+
// started it found this one in flight and passed.
|
|
722
|
+
if !isStale {
|
|
723
|
+
ChainMetadata.stage(state)
|
|
724
|
+
scheduleProcessing()
|
|
725
|
+
}
|
|
726
|
+
scheduleFetch()
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
308
732
|
let fetchChain = async (
|
|
309
733
|
state: IndexerState.t,
|
|
310
734
|
chainId,
|
|
@@ -317,6 +741,16 @@ let fetchChain = async (
|
|
|
317
741
|
) => {
|
|
318
742
|
let chainState = state->IndexerState.getChainState(~chainId)
|
|
319
743
|
if !(state->IndexerState.isResolvingReorg) && !(state->IndexerState.isStopped) {
|
|
744
|
+
if chainState->ChainState.hasBlockRegisters {
|
|
745
|
+
catchUpBlockRegisters(
|
|
746
|
+
state,
|
|
747
|
+
~chainId,
|
|
748
|
+
~runtime,
|
|
749
|
+
~stateId,
|
|
750
|
+
~scheduleFetch,
|
|
751
|
+
~scheduleProcessing,
|
|
752
|
+
)->Promise.ignore
|
|
753
|
+
}
|
|
320
754
|
let isRealtime = state->IndexerState.isRealtime
|
|
321
755
|
let sourceManager = chainState->ChainState.sourceManager
|
|
322
756
|
|