envio 3.15.0-subgraph → 3.16.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.
- package/README.md +40 -34
- package/evm.schema.json +3 -3
- package/fuel.schema.json +2 -2
- package/index.d.ts +3 -3
- package/licenses/README.md +1 -1
- package/package.json +9 -10
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/Bin.res +0 -7
- package/src/Bin.res.mjs +0 -8
- package/src/ChainFetching.res +17 -456
- package/src/ChainFetching.res.mjs +33 -404
- package/src/ChainState.res +48 -83
- package/src/ChainState.res.mjs +83 -115
- package/src/ChainState.resi +0 -26
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +7 -16
- package/src/Config.res.mjs +4 -11
- package/src/ContractRegisterContext.res +6 -3
- package/src/ContractRegisterContext.res.mjs +2 -4
- package/src/Core.res +5 -136
- package/src/Core.res.mjs +1 -90
- package/src/CrossChainState.res +2 -8
- package/src/CrossChainState.res.mjs +3 -3
- package/src/Ecosystem.res +4 -7
- package/src/Ecosystem.res.mjs +8 -8
- 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 +0 -16
- package/src/Env.res.mjs +0 -11
- package/src/EventProcessing.res +1 -38
- package/src/EventProcessing.res.mjs +1 -28
- package/src/FetchState.res +160 -319
- package/src/FetchState.res.mjs +182 -292
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- package/src/HandlerLoader.res +15 -39
- package/src/HandlerLoader.res.mjs +8 -27
- package/src/HandlerRegister.res +0 -4
- package/src/HandlerRegister.res.mjs +2 -5
- package/src/HandlerRegister.resi +0 -2
- 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 +0 -110
- package/src/Internal.res.mjs +1 -115
- package/src/LoadLayer.res +21 -47
- package/src/LoadLayer.res.mjs +47 -72
- package/src/LoadLayer.resi +0 -26
- package/src/Main.res +1 -14
- package/src/Main.res.mjs +3 -4
- package/src/Persistence.res +3 -23
- package/src/Persistence.res.mjs +3 -3
- package/src/Rollback.res +0 -1
- package/src/Rollback.res.mjs +1 -1
- package/src/RunScope.res +0 -3
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- package/src/SimulateItems.res +26 -42
- package/src/SimulateItems.res.mjs +16 -40
- 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 +277 -998
- package/src/Storage.res.mjs +216 -731
- package/src/Supervisor.res +75 -56
- package/src/Supervisor.res.mjs +55 -26
- 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 +46 -410
- package/src/UserContext.res.mjs +30 -318
- package/src/Utils.res +0 -23
- package/src/Utils.res.mjs +1 -17
- 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 +107 -175
- 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 +0 -2
- package/src/sources/AddressStore.res +0 -3
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +4 -7
- package/src/sources/EvmChain.res.mjs +5 -4
- 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 +1 -2
- package/src/sources/HyperSync.res.mjs +1 -2
- 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 +85 -104
- package/src/sources/SourceManager.res.mjs +18 -37
- package/src/sources/SourceManager.resi +3 -5
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +4 -3
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/svm.schema.json +3 -3
- package/src/EffectCacheLookup.res +0 -98
- package/src/EffectCacheLookup.res.mjs +0 -116
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/PreloadLookahead.res +0 -184
- package/src/PreloadLookahead.res.mjs +0 -197
- package/src/SubgraphLog.res +0 -19
- package/src/SubgraphLog.res.mjs +0 -26
- package/src/SyncWrites.res +0 -101
- package/src/SyncWrites.res.mjs +0 -98
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -60
- package/src/sources/CallSource.res +0 -274
- package/src/sources/CallSource.res.mjs +0 -196
- package/src/subgraph/abi-types.ts +0 -84
- package/src/subgraph/aggregations.ts +0 -745
- package/src/subgraph/as-package.ts +0 -143
- package/src/subgraph/assemblyscript-regex.ts +0 -67
- package/src/subgraph/assemblyscript.ts +0 -731
- package/src/subgraph/blocks.ts +0 -398
- package/src/subgraph/calls-source.ts +0 -702
- package/src/subgraph/calls.ts +0 -258
- package/src/subgraph/conformance.ts +0 -106
- package/src/subgraph/errors.ts +0 -85
- package/src/subgraph/graft.ts +0 -73
- package/src/subgraph/graph-cli-worker.mjs +0 -35
- package/src/subgraph/graph-cli.ts +0 -900
- package/src/subgraph/graph-node-decimal.ts +0 -199
- package/src/subgraph/graph-ts-enums.ts +0 -28
- package/src/subgraph/graph-ts-types/VERSION +0 -2
- package/src/subgraph/graph-ts-types/chain/arweave.d.ts +0 -70
- package/src/subgraph/graph-ts-types/chain/cosmos.d.ts +0 -327
- package/src/subgraph/graph-ts-types/chain/ethereum.d.ts +0 -233
- package/src/subgraph/graph-ts-types/chain/near.d.ts +0 -253
- package/src/subgraph/graph-ts-types/chain/starknet.d.ts +0 -32
- package/src/subgraph/graph-ts-types/common/collections.d.ts +0 -136
- package/src/subgraph/graph-ts-types/common/conversion.d.ts +0 -11
- package/src/subgraph/graph-ts-types/common/datasource.d.ts +0 -30
- package/src/subgraph/graph-ts-types/common/eager-offset.d.ts +0 -0
- package/src/subgraph/graph-ts-types/common/json.d.ts +0 -17
- package/src/subgraph/graph-ts-types/common/numbers.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/value.d.ts +0 -120
- package/src/subgraph/graph-ts-types/common/yaml.d.ts +0 -90
- package/src/subgraph/graph-ts-types/global/global.d.ts +0 -194
- package/src/subgraph/graph-ts-types/helper-functions.d.ts +0 -22
- package/src/subgraph/graph-ts-types/index.d.ts +0 -102
- package/src/subgraph/graph-ts.ts +0 -4280
- package/src/subgraph/hosts.ts +0 -554
- package/src/subgraph/log.ts +0 -28
- package/src/subgraph/receipts.ts +0 -184
- package/src/subgraph/register-slots.ts +0 -93
- package/src/subgraph/rollup.ts +0 -109
- package/src/subgraph/rpc.ts +0 -337
- package/src/subgraph/runtime.ts +0 -2060
- package/src/subgraph/scope.ts +0 -177
- package/src/subgraph/stats.ts +0 -42
- package/src/subgraph/template-instances.ts +0 -66
- package/src/subgraph/toolchain.ts +0 -327
- package/src/subgraph/transient.ts +0 -176
package/src/UserContext.res
CHANGED
|
@@ -1,91 +1,3 @@
|
|
|
1
|
-
// Thrown by a sync op that can't be served from memory. Owned by envio so the
|
|
2
|
-
// subgraph runtime's replay loop can tell it apart from a real handler error.
|
|
3
|
-
// User code never swallows it: AssemblyScript has no try/catch, and the ops
|
|
4
|
-
// keep re-throwing it while the round stays aborted.
|
|
5
|
-
exception Suspend
|
|
6
|
-
|
|
7
|
-
let isSuspend = exn =>
|
|
8
|
-
switch exn {
|
|
9
|
-
| Suspend => true
|
|
10
|
-
| _ => false
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Every suspend throws this one value: `throw(Suspend)` builds a fresh Error,
|
|
14
|
-
// stack trace and all, and a mapping suspends several times per event.
|
|
15
|
-
let suspend: exn = Suspend
|
|
16
|
-
|
|
17
|
-
type roundStatus =
|
|
18
|
-
| Active
|
|
19
|
-
// Set by a suspended sync op. Every context access re-throws the stored
|
|
20
|
-
// error, so even a caught suspend stops the round at its next one.
|
|
21
|
-
| Aborted(exn)
|
|
22
|
-
|
|
23
|
-
// One `context.runSync` call. Its body is replayed from the top each time it
|
|
24
|
-
// suspends on a read that wasn't in memory yet.
|
|
25
|
-
type syncRun = {
|
|
26
|
-
run: RunScope.handlerRun,
|
|
27
|
-
mutable status: roundStatus,
|
|
28
|
-
// Ops scheduled by this round's suspended reads, awaited before the replay.
|
|
29
|
-
mutable pending: array<promise<unit>>,
|
|
30
|
-
// Effect outputs read from the in-memory table during this call, reused by
|
|
31
|
-
// its later rounds.
|
|
32
|
-
memo: dict<Internal.effectOutput>,
|
|
33
|
-
// This round's writes, held until it completes.
|
|
34
|
-
mutable writes: SyncWrites.t,
|
|
35
|
-
// Preload reads this round answered as absent while their load was in flight.
|
|
36
|
-
mutable speculated: int,
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// The round running right now. A round's body runs synchronously from start to
|
|
40
|
-
// end, so no other run's code can observe it.
|
|
41
|
-
let currentSync: ref<option<syncRun>> = ref(None)
|
|
42
|
-
|
|
43
|
-
let syncOf = (run: RunScope.handlerRun) =>
|
|
44
|
-
switch currentSync.contents {
|
|
45
|
-
| Some(sync) if sync.run === run => Some(sync)
|
|
46
|
-
| _ => None
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// The handler run a `context.${prop}` access belongs to, refused while its
|
|
50
|
-
// round is aborted.
|
|
51
|
-
let currentRunOrThrow = (~prop) => {
|
|
52
|
-
let run = RunScope.currentHandlerRunOrThrow(~prop)
|
|
53
|
-
switch run->syncOf {
|
|
54
|
-
| Some({status: Aborted(exn)}) => throw(exn)
|
|
55
|
-
| _ => ()
|
|
56
|
-
}
|
|
57
|
-
run
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
let syncOrThrow = (run, ~prop) =>
|
|
61
|
-
switch run->syncOf {
|
|
62
|
-
| Some(sync) => sync
|
|
63
|
-
| None => JsError.throwWithMessage(`context.${prop} is only available inside context.runSync.`)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Fires the async op behind a sync miss, records it for the replay and aborts
|
|
67
|
-
// the round.
|
|
68
|
-
let scheduleAndSuspend = (sync: syncRun, promise: promise<'a>): 'b => {
|
|
69
|
-
sync.pending->Array.push(promise->Utils.Promise.ignoreValue)
|
|
70
|
-
sync.status = Aborted(suspend)
|
|
71
|
-
throw(suspend)
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
// Preload reads past a miss: the read answers as absent and the round runs on,
|
|
75
|
-
// so the reads after it that don't depend on it join this round's batch rather
|
|
76
|
-
// than taking a round each. Nothing preload computes is kept, and the round is
|
|
77
|
-
// replayed once its reads land. Bounded, for a mapping that loops on a read.
|
|
78
|
-
let maxSpeculatedReads = 32
|
|
79
|
-
|
|
80
|
-
let missOrSuspend = (sync: syncRun, promise: promise<'a>, ~absent: 'b): 'b =>
|
|
81
|
-
if sync.run.isPreload && sync.speculated < maxSpeculatedReads {
|
|
82
|
-
sync.pending->Array.push(promise->Utils.Promise.ignoreValue)
|
|
83
|
-
sync.speculated = sync.speculated + 1
|
|
84
|
-
absent
|
|
85
|
-
} else {
|
|
86
|
-
sync->scheduleAndSuspend(promise)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
1
|
// What every handler run of an indexer shares. The run itself — its item,
|
|
90
2
|
// checkpoint and whether it's a preload — is read from the RunScope it was
|
|
91
3
|
// entered with, so one context object serves every run, concurrent ones and
|
|
@@ -146,7 +58,7 @@ let isCrossChain = (params: params, effect: Internal.effect) =>
|
|
|
146
58
|
|
|
147
59
|
// A chain-scoped effect always resolves against the chain of the handler run
|
|
148
60
|
// that triggered the call, even several effects deep.
|
|
149
|
-
let rec
|
|
61
|
+
let rec callEffect = (
|
|
150
62
|
params: params,
|
|
151
63
|
~run: RunScope.handlerRun,
|
|
152
64
|
~caller: option<Internal.effect>,
|
|
@@ -176,73 +88,23 @@ let rec prepareEffectCall = (
|
|
|
176
88
|
~callEffect=(child, input) => params->callEffect(~run, ~caller=Some(effect), child, input),
|
|
177
89
|
)
|
|
178
90
|
effectContext->RunScope.attachEffectRun({caller: run, effectName: effect.name})
|
|
179
|
-
let effectArgs: Internal.effectArgs = {
|
|
180
|
-
input,
|
|
181
|
-
context: effectContext,
|
|
182
|
-
cacheKey: input
|
|
183
|
-
->S.reverseConvertOrThrow(effect.input)
|
|
184
|
-
->Utils.Hash.makeOrThrow
|
|
185
|
-
->Utils.Hash.boundCacheKey,
|
|
186
|
-
chainId: handlerChainId,
|
|
187
|
-
checkpointId: run.checkpointId,
|
|
188
|
-
}
|
|
189
|
-
(scope, effectArgs)
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
and callEffect = (params: params, ~run: RunScope.handlerRun, ~caller, effect, input) => {
|
|
193
|
-
let (scope, effectArgs) = params->prepareEffectCall(~run, ~caller, effect, input)
|
|
194
91
|
LoadLayer.loadEffect(
|
|
195
92
|
~loadManager=params.loadManager,
|
|
196
93
|
~persistence=params.persistence,
|
|
197
94
|
~effect,
|
|
198
|
-
~effectArgs
|
|
95
|
+
~effectArgs={
|
|
96
|
+
input,
|
|
97
|
+
context: effectContext,
|
|
98
|
+
cacheKey: input->S.reverseConvertOrThrow(effect.input)->Utils.Hash.makeOrThrow,
|
|
99
|
+
chainId: handlerChainId,
|
|
100
|
+
checkpointId: run.checkpointId,
|
|
101
|
+
},
|
|
199
102
|
~scope,
|
|
200
103
|
~indexerState=params.indexerState,
|
|
201
104
|
~shouldGroup=run.isPreload,
|
|
202
105
|
)
|
|
203
106
|
}
|
|
204
107
|
|
|
205
|
-
let effectMemoKey = (~effect: Internal.effect, ~scope: Internal.chainScope, ~cacheKey) =>
|
|
206
|
-
switch scope {
|
|
207
|
-
| CrossChain => `${effect.name}.${cacheKey}`
|
|
208
|
-
| Chain(chainId) => `${effect.name}.${chainId->ChainId.toString}.${cacheKey}`
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
// An effect's output when it's in memory; otherwise the round suspends on it.
|
|
212
|
-
let effectSync = (params: params, effect: Internal.effect, input: Internal.effectInput) => {
|
|
213
|
-
let run = currentRunOrThrow(~prop="effectSync")
|
|
214
|
-
let sync = run->syncOrThrow(~prop="effectSync")
|
|
215
|
-
let (scope, effectArgs) = params->prepareEffectCall(~run, ~caller=None, effect, input)
|
|
216
|
-
let memoKey = effectMemoKey(~effect, ~scope, ~cacheKey=effectArgs.cacheKey)
|
|
217
|
-
switch sync.memo->Utils.Dict.dangerouslyGetNonOption(memoKey) {
|
|
218
|
-
| Some(output) => output
|
|
219
|
-
| None =>
|
|
220
|
-
let inMemTable = params.indexerState->InMemoryStore.getEffectInMemTable(~effect, ~scope)
|
|
221
|
-
if inMemTable->InMemoryStore.hasEffectOutput(effectArgs.cacheKey) {
|
|
222
|
-
let output = inMemTable->InMemoryStore.getEffectOutputUnsafe(effectArgs.cacheKey)
|
|
223
|
-
sync.memo->Dict.set(memoKey, output)
|
|
224
|
-
output
|
|
225
|
-
} else if sync.speculated > 0 {
|
|
226
|
-
// The input may be computed from a speculated read: an effect can cost
|
|
227
|
-
// an RPC call, so it waits to be asked with the real values.
|
|
228
|
-
sync.status = Aborted(suspend)
|
|
229
|
-
throw(suspend)
|
|
230
|
-
} else {
|
|
231
|
-
sync->scheduleAndSuspend(
|
|
232
|
-
LoadLayer.loadEffect(
|
|
233
|
-
~loadManager=params.loadManager,
|
|
234
|
-
~persistence=params.persistence,
|
|
235
|
-
~effect,
|
|
236
|
-
~effectArgs,
|
|
237
|
-
~scope,
|
|
238
|
-
~indexerState=params.indexerState,
|
|
239
|
-
~shouldGroup=run.isPreload,
|
|
240
|
-
),
|
|
241
|
-
)
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
108
|
// A TTL drops an entity's rows whenever ClickHouse merges them, so for one kept
|
|
247
109
|
// there alone a read would turn on merge timing rather than on the events.
|
|
248
110
|
let expiresUnread = (entityConfig: Internal.entityConfig) =>
|
|
@@ -267,15 +129,8 @@ type entityContext = {
|
|
|
267
129
|
deleteUnsafe: EntityId.t => unit,
|
|
268
130
|
}
|
|
269
131
|
|
|
270
|
-
type syncEntityContext = {
|
|
271
|
-
getSync: string => option<Internal.entity>,
|
|
272
|
-
getWhereSync: dict<dict<unknown>> => array<Internal.entity>,
|
|
273
|
-
getInBlockSync: string => option<Internal.entity>,
|
|
274
|
-
}
|
|
275
|
-
|
|
276
132
|
let makeEntityContext = (params: params, ~entityConfig: Internal.entityConfig) => {
|
|
277
133
|
let prop = entityConfig.name
|
|
278
|
-
let entityName = entityConfig.name
|
|
279
134
|
// The handler context is always chain-scoped, so a per-chain entity resolves
|
|
280
135
|
// to the chain the handler runs on and a cross-chain one to the shared
|
|
281
136
|
// partition.
|
|
@@ -283,7 +138,7 @@ let makeEntityContext = (params: params, ~entityConfig: Internal.entityConfig) =
|
|
|
283
138
|
entityConfig->InMemoryStore.entityScope(~chainId=run.item->Internal.getItemChainId)
|
|
284
139
|
|
|
285
140
|
let loadById = entityId => {
|
|
286
|
-
let run =
|
|
141
|
+
let run = RunScope.currentHandlerRunOrThrow(~prop)
|
|
287
142
|
LoadLayer.loadById(
|
|
288
143
|
~loadManager=params.loadManager,
|
|
289
144
|
~persistence=params.persistence,
|
|
@@ -295,34 +150,35 @@ let makeEntityContext = (params: params, ~entityConfig: Internal.entityConfig) =
|
|
|
295
150
|
)
|
|
296
151
|
}
|
|
297
152
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
| (None, false) =>
|
|
308
|
-
params.indexerState->SyncWrites.write(~entityConfig, ~scope=run->scopeOf, change)
|
|
309
|
-
| (_, true) => ()
|
|
310
|
-
}
|
|
153
|
+
let write = (run, change: Change.t<Internal.entity>) => {
|
|
154
|
+
let scope = run->scopeOf
|
|
155
|
+
params.indexerState
|
|
156
|
+
->InMemoryStore.getInMemTable(~entityConfig, ~scope)
|
|
157
|
+
->InMemoryTable.Entity.set(
|
|
158
|
+
~committedCheckpointId=params.indexerState->IndexerState.committedCheckpointIdFor(~scope),
|
|
159
|
+
change,
|
|
160
|
+
)
|
|
161
|
+
}
|
|
311
162
|
|
|
163
|
+
// A preload run's writes are no-ops: its run proper makes them.
|
|
312
164
|
let set = (entity: Internal.entity) => {
|
|
313
|
-
let run =
|
|
314
|
-
run
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
165
|
+
let run = RunScope.currentHandlerRunOrThrow(~prop)
|
|
166
|
+
if !run.isPreload {
|
|
167
|
+
run->write(
|
|
168
|
+
Set({
|
|
169
|
+
entityId: entity.id->EntityId.unsafeOfString,
|
|
170
|
+
checkpointId: run.checkpointId,
|
|
171
|
+
entity,
|
|
172
|
+
}),
|
|
173
|
+
)
|
|
174
|
+
}
|
|
321
175
|
}
|
|
322
176
|
|
|
323
177
|
let deleteUnsafe = entityId => {
|
|
324
|
-
let run =
|
|
325
|
-
|
|
178
|
+
let run = RunScope.currentHandlerRunOrThrow(~prop)
|
|
179
|
+
if !run.isPreload {
|
|
180
|
+
run->write(Delete({entityId, checkpointId: run.checkpointId}))
|
|
181
|
+
}
|
|
326
182
|
}
|
|
327
183
|
|
|
328
184
|
let entityContext: entityContext = if entityConfig->expiresUnread {
|
|
@@ -338,7 +194,7 @@ let makeEntityContext = (params: params, ~entityConfig: Internal.entityConfig) =
|
|
|
338
194
|
{
|
|
339
195
|
get: loadById,
|
|
340
196
|
getWhere: filter => {
|
|
341
|
-
let run =
|
|
197
|
+
let run = RunScope.currentHandlerRunOrThrow(~prop)
|
|
342
198
|
LoadLayer.loadByFilter(
|
|
343
199
|
~loadManager=params.loadManager,
|
|
344
200
|
~persistence=params.persistence,
|
|
@@ -375,198 +231,11 @@ let makeEntityContext = (params: params, ~entityConfig: Internal.entityConfig) =
|
|
|
375
231
|
}
|
|
376
232
|
}
|
|
377
233
|
|
|
378
|
-
|
|
379
|
-
Utils.StrictObject.
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
)->Utils.StrictObject.assign(entityContext)
|
|
384
|
-
|
|
385
|
-
// The subgraph runtime's synchronous reads: answered from memory, or the
|
|
386
|
-
// round suspends on the load and is replayed once it lands.
|
|
387
|
-
if Env.subgraphMode.contents {
|
|
388
|
-
let syncEntityContext: syncEntityContext = {
|
|
389
|
-
getSync: entityId => {
|
|
390
|
-
let run = currentRunOrThrow(~prop)
|
|
391
|
-
let sync = run->syncOrThrow(~prop=`${prop}.getSync`)
|
|
392
|
-
let scope = run->scopeOf
|
|
393
|
-
switch sync.writes->SyncWrites.getById(~entityConfig, ~entityId) {
|
|
394
|
-
| Some(entity) => entity
|
|
395
|
-
| None =>
|
|
396
|
-
switch run.isPreload
|
|
397
|
-
? PreloadLookahead.overlayGet(~item=run.item, ~entityName, ~scope, ~entityId)
|
|
398
|
-
: None {
|
|
399
|
-
| Some(entity) => entity
|
|
400
|
-
| None =>
|
|
401
|
-
switch LoadLayer.getByIdInMemory(
|
|
402
|
-
~entityConfig,
|
|
403
|
-
~scope,
|
|
404
|
-
~indexerState=params.indexerState,
|
|
405
|
-
~entityId,
|
|
406
|
-
) {
|
|
407
|
-
| Some(entity) =>
|
|
408
|
-
if run.isPreload && entity->Option.isNone {
|
|
409
|
-
PreloadLookahead.recordAbsent(~item=run.item, ~entityName, ~scope, ~entityId)
|
|
410
|
-
}
|
|
411
|
-
entity
|
|
412
|
-
| None =>
|
|
413
|
-
sync->missOrSuspend(
|
|
414
|
-
~absent=None,
|
|
415
|
-
LoadLayer.loadById(
|
|
416
|
-
~loadManager=params.loadManager,
|
|
417
|
-
~persistence=params.persistence,
|
|
418
|
-
~entityConfig,
|
|
419
|
-
~scope,
|
|
420
|
-
~indexerState=params.indexerState,
|
|
421
|
-
~shouldGroup=run.isPreload,
|
|
422
|
-
~entityId,
|
|
423
|
-
),
|
|
424
|
-
)
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
},
|
|
429
|
-
getWhereSync: filter => {
|
|
430
|
-
let run = currentRunOrThrow(~prop)
|
|
431
|
-
let sync = run->syncOrThrow(~prop=`${prop}.getWhereSync`)
|
|
432
|
-
let scope = run->scopeOf
|
|
433
|
-
let filter = filter->EntityFilter.parseOrThrow(~entityName, ~table=entityConfig.table)
|
|
434
|
-
switch LoadLayer.getByFilterInMemory(
|
|
435
|
-
~entityConfig,
|
|
436
|
-
~scope,
|
|
437
|
-
~indexerState=params.indexerState,
|
|
438
|
-
~filter,
|
|
439
|
-
) {
|
|
440
|
-
| Some(entities) => sync.writes->SyncWrites.overlayFilter(entities, ~entityConfig, ~filter)
|
|
441
|
-
| None =>
|
|
442
|
-
sync->missOrSuspend(
|
|
443
|
-
~absent=[],
|
|
444
|
-
LoadLayer.loadByParsedFilter(
|
|
445
|
-
~loadManager=params.loadManager,
|
|
446
|
-
~persistence=params.persistence,
|
|
447
|
-
~entityConfig,
|
|
448
|
-
~scope,
|
|
449
|
-
~indexerState=params.indexerState,
|
|
450
|
-
~shouldGroup=run.isPreload,
|
|
451
|
-
~filter,
|
|
452
|
-
),
|
|
453
|
-
)
|
|
454
|
-
}
|
|
455
|
-
},
|
|
456
|
-
// Never suspends: the in-memory table spans the whole batch, so a change
|
|
457
|
-
// only counts as "in this block" when it was written at this run's
|
|
458
|
-
// checkpoint.
|
|
459
|
-
getInBlockSync: entityId => {
|
|
460
|
-
let run = currentRunOrThrow(~prop)
|
|
461
|
-
let sync = run->syncOrThrow(~prop=`${prop}.getInBlockSync`)
|
|
462
|
-
switch sync.writes->SyncWrites.getById(~entityConfig, ~entityId) {
|
|
463
|
-
| Some(entity) => entity
|
|
464
|
-
| None =>
|
|
465
|
-
let inMemTable =
|
|
466
|
-
params.indexerState->InMemoryStore.getInMemTable(~entityConfig, ~scope=run->scopeOf)
|
|
467
|
-
switch inMemTable.latestEntityChangeById->Utils.Dict.dangerouslyGetNonOption(entityId) {
|
|
468
|
-
| Some(change) if change->Change.getCheckpointId == run.checkpointId =>
|
|
469
|
-
change->InMemoryTable.Entity.mapChangeToEntity
|
|
470
|
-
| _ => None
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
}
|
|
475
|
-
object->Utils.StrictObject.assign(
|
|
476
|
-
syncEntityContext->(Utils.magic: syncEntityContext => entityContext),
|
|
477
|
-
)
|
|
478
|
-
} else {
|
|
479
|
-
object
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
// Deterministic mappings always make progress, so the cap only exists to turn
|
|
484
|
-
// a non-deterministic one into a clear error instead of a hang.
|
|
485
|
-
let maxSyncRounds = 10000
|
|
486
|
-
|
|
487
|
-
let rec runSyncRound = async (params: params, sync: syncRun, fn: unit => unit, ~round) => {
|
|
488
|
-
if round > maxSyncRounds {
|
|
489
|
-
JsError.throwWithMessage(
|
|
490
|
-
`The handler suspended on a synchronous read too many times: gave up after ${maxSyncRounds->Int.toString} rounds. This usually means the code isn't deterministic across reruns.`,
|
|
491
|
-
)
|
|
492
|
-
}
|
|
493
|
-
let run = sync.run
|
|
494
|
-
sync.status = Active
|
|
495
|
-
sync.pending = []
|
|
496
|
-
sync.speculated = 0
|
|
497
|
-
sync.writes = SyncWrites.make()
|
|
498
|
-
let startedAt = PreloadLookahead.roundStarted(~isPreload=run.isPreload)
|
|
499
|
-
|
|
500
|
-
let previous = currentSync.contents
|
|
501
|
-
currentSync := Some(sync)
|
|
502
|
-
let outcome = try {
|
|
503
|
-
fn()
|
|
504
|
-
Ok()
|
|
505
|
-
} catch {
|
|
506
|
-
| exn => Error(exn)
|
|
507
|
-
}
|
|
508
|
-
currentSync := previous
|
|
509
|
-
PreloadLookahead.roundEnded(~item=run.item, ~startedAt)
|
|
510
|
-
let writes = sync.writes
|
|
511
|
-
|
|
512
|
-
// Neither a suspended round nor a failed one keeps what it wrote.
|
|
513
|
-
sync.writes = SyncWrites.make()
|
|
514
|
-
|
|
515
|
-
let suspended = switch outcome {
|
|
516
|
-
| Ok() =>
|
|
517
|
-
switch sync.status {
|
|
518
|
-
// The body caught the suspend and returned anyway, so it didn't see the
|
|
519
|
-
// reads it asked for: it is replayed like any other suspended round.
|
|
520
|
-
| Aborted(_) => true
|
|
521
|
-
// Ran on reads it was told were absent.
|
|
522
|
-
| Active if sync.speculated > 0 => true
|
|
523
|
-
| Active =>
|
|
524
|
-
if run.isPreload {
|
|
525
|
-
writes->SyncWrites.forEachChange((~entityConfig, ~scope, change) =>
|
|
526
|
-
PreloadLookahead.overlaySet(
|
|
527
|
-
~item=run.item,
|
|
528
|
-
~entityName=entityConfig.name,
|
|
529
|
-
~scope,
|
|
530
|
-
~entityId=change->Change.getEntityId->EntityId.toKey,
|
|
531
|
-
~entity=change->InMemoryTable.Entity.mapChangeToEntity,
|
|
532
|
-
)
|
|
533
|
-
)
|
|
534
|
-
} else {
|
|
535
|
-
writes->SyncWrites.commit(~indexerState=params.indexerState)
|
|
536
|
-
}
|
|
537
|
-
false
|
|
538
|
-
}
|
|
539
|
-
// A round handed a speculated absence may well throw on it: the replay that
|
|
540
|
-
// sees the real value decides whether the error is the handler's.
|
|
541
|
-
| Error(exn) if exn->isSuspend || sync.speculated > 0 => true
|
|
542
|
-
| Error(exn) =>
|
|
543
|
-
// The body threw its own error after scheduling a read. Those ops are
|
|
544
|
-
// nobody's result now, and an unhandled rejection would replace the error
|
|
545
|
-
// the handler actually raised.
|
|
546
|
-
sync.pending->Array.forEach(promise => promise->Utils.Promise.catchResolve(_ => ())->ignore)
|
|
547
|
-
sync.pending = []
|
|
548
|
-
throw(exn)
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
if suspended {
|
|
552
|
-
// A round only suspends by scheduling the read it missed.
|
|
553
|
-
let pending = sync.pending
|
|
554
|
-
sync.pending = []
|
|
555
|
-
let errors = []
|
|
556
|
-
let _ = await pending
|
|
557
|
-
->Array.map(promise =>
|
|
558
|
-
promise->Promise.catch(exn => {
|
|
559
|
-
errors->Array.push(exn)
|
|
560
|
-
Promise.resolve()
|
|
561
|
-
})
|
|
562
|
-
)
|
|
563
|
-
->Promise.all
|
|
564
|
-
switch errors->Array.get(0) {
|
|
565
|
-
| Some(exn) => throw(exn)
|
|
566
|
-
| None => ()
|
|
567
|
-
}
|
|
568
|
-
await params->runSyncRound(sync, fn, ~round=round + 1)
|
|
569
|
-
}
|
|
234
|
+
Utils.StrictObject.make(
|
|
235
|
+
Utils.StrictObject.makePrototype(op =>
|
|
236
|
+
JsError.throwWithMessage(`Invalid context.${entityConfig.name}.${op} operation.`)
|
|
237
|
+
),
|
|
238
|
+
)->Utils.StrictObject.assign(entityContext)
|
|
570
239
|
}
|
|
571
240
|
|
|
572
241
|
// One context for every handler run of an indexer.
|
|
@@ -582,7 +251,7 @@ let makeHandlerContext = (params: params): Internal.handlerContext => {
|
|
|
582
251
|
context
|
|
583
252
|
->Utils.Object.defineProperty(
|
|
584
253
|
name,
|
|
585
|
-
{enumerable: true, get: () => get(
|
|
254
|
+
{enumerable: true, get: () => get(RunScope.currentHandlerRunOrThrow(~prop=name))},
|
|
586
255
|
)
|
|
587
256
|
->ignore
|
|
588
257
|
|
|
@@ -594,7 +263,12 @@ let makeHandlerContext = (params: params): Internal.handlerContext => {
|
|
|
594
263
|
// Reads the run when called, so a kept `context.effect` is refused once its
|
|
595
264
|
// handler resolved, like a kept entity accessor.
|
|
596
265
|
let effect = (effect, input) =>
|
|
597
|
-
params->callEffect(
|
|
266
|
+
params->callEffect(
|
|
267
|
+
~run=RunScope.currentHandlerRunOrThrow(~prop="effect"),
|
|
268
|
+
~caller=None,
|
|
269
|
+
effect,
|
|
270
|
+
input,
|
|
271
|
+
)
|
|
598
272
|
context
|
|
599
273
|
->Utils.Object.definePropertyWithValue(
|
|
600
274
|
"effect",
|
|
@@ -608,44 +282,6 @@ let makeHandlerContext = (params: params): Internal.handlerContext => {
|
|
|
608
282
|
},
|
|
609
283
|
)
|
|
610
284
|
->ignore
|
|
611
|
-
if Env.subgraphMode.contents {
|
|
612
|
-
context
|
|
613
|
-
->Utils.Object.definePropertyWithValue(
|
|
614
|
-
"effectSync",
|
|
615
|
-
{
|
|
616
|
-
enumerable: true,
|
|
617
|
-
value: ((effect, input) => params->effectSync(effect, input))->(
|
|
618
|
-
Utils.magic: ((Internal.effect, Internal.effectInput) => Internal.effectOutput) => unknown
|
|
619
|
-
),
|
|
620
|
-
},
|
|
621
|
-
)
|
|
622
|
-
->ignore
|
|
623
|
-
context
|
|
624
|
-
->Utils.Object.definePropertyWithValue(
|
|
625
|
-
"runSync",
|
|
626
|
-
{
|
|
627
|
-
enumerable: true,
|
|
628
|
-
value: (
|
|
629
|
-
fn => {
|
|
630
|
-
let run = currentRunOrThrow(~prop="runSync")
|
|
631
|
-
params->runSyncRound(
|
|
632
|
-
{
|
|
633
|
-
run,
|
|
634
|
-
status: Active,
|
|
635
|
-
pending: [],
|
|
636
|
-
memo: Dict.make(),
|
|
637
|
-
writes: SyncWrites.make(),
|
|
638
|
-
speculated: 0,
|
|
639
|
-
},
|
|
640
|
-
fn,
|
|
641
|
-
~round=1,
|
|
642
|
-
)
|
|
643
|
-
}
|
|
644
|
-
)->(Utils.magic: ((unit => unit) => promise<unit>) => unknown),
|
|
645
|
-
},
|
|
646
|
-
)
|
|
647
|
-
->ignore
|
|
648
|
-
}
|
|
649
285
|
define("isPreload", run => run.isPreload->(Utils.magic: bool => unknown))
|
|
650
286
|
define("chain", run =>
|
|
651
287
|
params.chains
|