envio 3.0.0-alpha.2 → 3.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +164 -30
- package/bin.mjs +49 -0
- package/evm.schema.json +79 -169
- package/fuel.schema.json +50 -21
- package/index.d.ts +578 -1
- package/index.js +4 -0
- package/package.json +47 -31
- package/rescript.json +4 -1
- package/src/Batch.res +11 -8
- package/src/Batch.res.mjs +11 -9
- package/src/ChainFetcher.res +531 -0
- package/src/ChainFetcher.res.mjs +339 -0
- package/src/ChainManager.res +190 -0
- package/src/ChainManager.res.mjs +166 -0
- package/src/Change.res +3 -3
- package/src/Config.gen.ts +19 -0
- package/src/Config.res +725 -25
- package/src/Config.res.mjs +692 -26
- package/src/{Indexer.res → Ctx.res} +1 -1
- package/src/Ecosystem.res +9 -124
- package/src/Ecosystem.res.mjs +19 -160
- package/src/Env.res +33 -73
- package/src/Env.res.mjs +29 -85
- package/src/Envio.gen.ts +3 -1
- package/src/Envio.res +77 -9
- package/src/Envio.res.mjs +39 -1
- package/src/EventConfigBuilder.res +408 -0
- package/src/EventConfigBuilder.res.mjs +376 -0
- package/src/EventProcessing.res +469 -0
- package/src/EventProcessing.res.mjs +337 -0
- package/src/EvmTypes.gen.ts +6 -0
- package/src/EvmTypes.res +1 -0
- package/src/FetchState.res +1256 -639
- package/src/FetchState.res.mjs +1135 -612
- package/src/GlobalState.res +1224 -0
- package/src/GlobalState.res.mjs +1291 -0
- package/src/GlobalStateManager.res +68 -0
- package/src/GlobalStateManager.res.mjs +75 -0
- package/src/GlobalStateManager.resi +7 -0
- package/src/HandlerLoader.res +89 -0
- package/src/HandlerLoader.res.mjs +79 -0
- package/src/HandlerRegister.res +357 -0
- package/src/HandlerRegister.res.mjs +299 -0
- package/src/HandlerRegister.resi +30 -0
- package/src/Hasura.res +111 -175
- package/src/Hasura.res.mjs +88 -150
- package/src/InMemoryStore.res +1 -1
- package/src/InMemoryStore.res.mjs +3 -3
- package/src/InMemoryTable.res +1 -1
- package/src/InMemoryTable.res.mjs +1 -1
- package/src/Internal.gen.ts +6 -0
- package/src/Internal.res +265 -12
- package/src/Internal.res.mjs +115 -1
- package/src/LoadLayer.res +444 -0
- package/src/LoadLayer.res.mjs +296 -0
- package/src/LoadLayer.resi +32 -0
- package/src/LogSelection.res +33 -27
- package/src/LogSelection.res.mjs +6 -0
- package/src/Logging.res +21 -7
- package/src/Logging.res.mjs +16 -8
- package/src/Main.res +390 -0
- package/src/Main.res.mjs +341 -0
- package/src/Persistence.res +7 -21
- package/src/Persistence.res.mjs +3 -3
- package/src/PgStorage.gen.ts +10 -0
- package/src/PgStorage.res +116 -69
- package/src/PgStorage.res.d.mts +5 -0
- package/src/PgStorage.res.mjs +93 -50
- package/src/Prometheus.res +294 -224
- package/src/Prometheus.res.mjs +353 -340
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +2 -2
- package/src/SimulateItems.res +353 -0
- package/src/SimulateItems.res.mjs +335 -0
- package/src/Sink.res +4 -2
- package/src/Sink.res.mjs +2 -1
- package/src/TableIndices.res +0 -1
- package/src/TestIndexer.res +913 -0
- package/src/TestIndexer.res.mjs +698 -0
- package/src/TestIndexerProxyStorage.res +205 -0
- package/src/TestIndexerProxyStorage.res.mjs +151 -0
- package/src/TopicFilter.res +1 -1
- package/src/Types.ts +1 -1
- package/src/UserContext.res +424 -0
- package/src/UserContext.res.mjs +279 -0
- package/src/Utils.res +97 -26
- package/src/Utils.res.mjs +91 -44
- package/src/bindings/BigInt.res +10 -0
- package/src/bindings/BigInt.res.mjs +15 -0
- package/src/bindings/ClickHouse.res +120 -23
- package/src/bindings/ClickHouse.res.mjs +118 -28
- package/src/bindings/DateFns.res +74 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/EventSource.res +11 -2
- package/src/bindings/EventSource.res.mjs +8 -1
- package/src/bindings/Express.res +1 -0
- package/src/bindings/Hrtime.res +14 -1
- package/src/bindings/Hrtime.res.mjs +22 -2
- package/src/bindings/Hrtime.resi +4 -0
- package/src/bindings/Lodash.res +0 -1
- package/src/bindings/NodeJs.res +49 -3
- package/src/bindings/NodeJs.res.mjs +11 -3
- package/src/bindings/Pino.res +24 -10
- package/src/bindings/Pino.res.mjs +14 -8
- package/src/bindings/Postgres.gen.ts +8 -0
- package/src/bindings/Postgres.res +5 -1
- package/src/bindings/Postgres.res.d.mts +5 -0
- package/src/bindings/PromClient.res +0 -10
- package/src/bindings/PromClient.res.mjs +0 -3
- package/src/bindings/Vitest.res +144 -0
- package/src/bindings/Vitest.res.mjs +9 -0
- package/src/bindings/WebSocket.res +27 -0
- package/src/bindings/WebSocket.res.mjs +2 -0
- package/src/bindings/Yargs.res +8 -0
- package/src/bindings/Yargs.res.mjs +2 -0
- package/src/db/EntityHistory.res +7 -7
- package/src/db/EntityHistory.res.mjs +9 -9
- package/src/db/InternalTable.res +59 -111
- package/src/db/InternalTable.res.mjs +73 -104
- package/src/db/Table.res +27 -8
- package/src/db/Table.res.mjs +25 -14
- package/src/sources/Evm.res +84 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +94 -0
- package/src/sources/EvmChain.res.mjs +60 -0
- package/src/sources/Fuel.res +19 -34
- package/src/sources/Fuel.res.mjs +34 -16
- package/src/sources/FuelSDK.res +38 -0
- package/src/sources/FuelSDK.res.mjs +29 -0
- package/src/sources/HyperFuel.res +2 -2
- package/src/sources/HyperFuel.resi +1 -1
- package/src/sources/HyperFuelClient.res +2 -2
- package/src/sources/HyperFuelSource.res +35 -13
- package/src/sources/HyperFuelSource.res.mjs +26 -16
- package/src/sources/HyperSync.res +61 -60
- package/src/sources/HyperSync.res.mjs +53 -67
- package/src/sources/HyperSync.resi +6 -4
- package/src/sources/HyperSyncClient.res +29 -2
- package/src/sources/HyperSyncClient.res.mjs +9 -0
- package/src/sources/HyperSyncHeightStream.res +76 -118
- package/src/sources/HyperSyncHeightStream.res.mjs +68 -75
- package/src/sources/HyperSyncSource.res +122 -143
- package/src/sources/HyperSyncSource.res.mjs +106 -121
- package/src/sources/Rpc.res +86 -14
- package/src/sources/Rpc.res.mjs +101 -9
- package/src/sources/RpcSource.res +731 -364
- package/src/sources/RpcSource.res.mjs +845 -410
- package/src/sources/RpcWebSocketHeightStream.res +181 -0
- package/src/sources/RpcWebSocketHeightStream.res.mjs +196 -0
- package/src/sources/SimulateSource.res +59 -0
- package/src/sources/SimulateSource.res.mjs +50 -0
- package/src/sources/Source.res +7 -5
- package/src/sources/SourceManager.res +358 -221
- package/src/sources/SourceManager.res.mjs +346 -171
- package/src/sources/SourceManager.resi +17 -6
- package/src/sources/Svm.res +81 -0
- package/src/sources/Svm.res.mjs +90 -0
- package/src/tui/Tui.res +247 -0
- package/src/tui/Tui.res.mjs +337 -0
- package/src/tui/bindings/Ink.res +371 -0
- package/src/tui/bindings/Ink.res.mjs +72 -0
- package/src/tui/bindings/Style.res +123 -0
- package/src/tui/bindings/Style.res.mjs +2 -0
- package/src/tui/components/BufferedProgressBar.res +40 -0
- package/src/tui/components/BufferedProgressBar.res.mjs +57 -0
- package/src/tui/components/CustomHooks.res +122 -0
- package/src/tui/components/CustomHooks.res.mjs +179 -0
- package/src/tui/components/Messages.res +41 -0
- package/src/tui/components/Messages.res.mjs +75 -0
- package/src/tui/components/SyncETA.res +174 -0
- package/src/tui/components/SyncETA.res.mjs +263 -0
- package/src/tui/components/TuiData.res +47 -0
- package/src/tui/components/TuiData.res.mjs +34 -0
- package/svm.schema.json +112 -0
- package/bin.js +0 -48
- package/src/EventRegister.res +0 -241
- package/src/EventRegister.res.mjs +0 -240
- package/src/EventRegister.resi +0 -30
- package/src/bindings/Ethers.gen.ts +0 -14
- package/src/bindings/Ethers.res +0 -204
- package/src/bindings/Ethers.res.mjs +0 -130
- /package/src/{Indexer.res.mjs → Ctx.res.mjs} +0 -0
|
@@ -0,0 +1,444 @@
|
|
|
1
|
+
open Belt
|
|
2
|
+
|
|
3
|
+
let loadById = (
|
|
4
|
+
~loadManager,
|
|
5
|
+
~persistence: Persistence.t,
|
|
6
|
+
~entityConfig: Internal.entityConfig,
|
|
7
|
+
~inMemoryStore,
|
|
8
|
+
~shouldGroup,
|
|
9
|
+
~item,
|
|
10
|
+
~entityId,
|
|
11
|
+
) => {
|
|
12
|
+
let key = `${entityConfig.name}.get`
|
|
13
|
+
let inMemTable = inMemoryStore->InMemoryStore.getInMemTable(~entityConfig)
|
|
14
|
+
|
|
15
|
+
let load = async (idsToLoad, ~onError as _) => {
|
|
16
|
+
let timerRef = Prometheus.StorageLoad.startOperation(~operation=key)
|
|
17
|
+
|
|
18
|
+
// Since LoadManager.call prevents registerign entities already existing in the inMemoryStore,
|
|
19
|
+
// we can be sure that we load only the new ones.
|
|
20
|
+
let dbEntities = try {
|
|
21
|
+
await (persistence->Persistence.getInitializedStorageOrThrow).loadByIdsOrThrow(
|
|
22
|
+
~table=entityConfig.table,
|
|
23
|
+
~rowsSchema=entityConfig.rowsSchema,
|
|
24
|
+
~ids=idsToLoad,
|
|
25
|
+
)
|
|
26
|
+
} catch {
|
|
27
|
+
| Persistence.StorageError({message, reason}) =>
|
|
28
|
+
reason->ErrorHandling.mkLogAndRaise(~logger=item->Logging.getItemLogger, ~msg=message)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let entitiesMap = Js.Dict.empty()
|
|
32
|
+
for idx in 0 to dbEntities->Array.length - 1 {
|
|
33
|
+
let entity = dbEntities->Js.Array2.unsafe_get(idx)
|
|
34
|
+
entitiesMap->Js.Dict.set(entity.id, entity)
|
|
35
|
+
}
|
|
36
|
+
idsToLoad->Js.Array2.forEach(entityId => {
|
|
37
|
+
// Set the entity in the in memory store
|
|
38
|
+
// without overwriting existing values
|
|
39
|
+
// which might be newer than what we got from db
|
|
40
|
+
inMemTable->InMemoryTable.Entity.initValue(
|
|
41
|
+
~allowOverWriteEntity=false,
|
|
42
|
+
~key=entityId,
|
|
43
|
+
~entity=entitiesMap->Utils.Dict.dangerouslyGetNonOption(entityId),
|
|
44
|
+
)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
timerRef->Prometheus.StorageLoad.endOperation(
|
|
48
|
+
~operation=key,
|
|
49
|
+
~whereSize=idsToLoad->Array.length,
|
|
50
|
+
~size=dbEntities->Array.length,
|
|
51
|
+
)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
loadManager->LoadManager.call(
|
|
55
|
+
~key,
|
|
56
|
+
~load,
|
|
57
|
+
~shouldGroup,
|
|
58
|
+
~hasher=LoadManager.noopHasher,
|
|
59
|
+
~getUnsafeInMemory=inMemTable->InMemoryTable.Entity.getUnsafe,
|
|
60
|
+
~hasInMemory=hash => inMemTable.table->InMemoryTable.hasByHash(hash),
|
|
61
|
+
~input=entityId,
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let callEffect = (
|
|
66
|
+
~effect: Internal.effect,
|
|
67
|
+
~arg: Internal.effectArgs,
|
|
68
|
+
~inMemTable: InMemoryStore.effectCacheInMemTable,
|
|
69
|
+
~timerRef,
|
|
70
|
+
~onError,
|
|
71
|
+
) => {
|
|
72
|
+
let effectName = effect.name
|
|
73
|
+
let hadActiveCalls = effect.activeCallsCount > 0
|
|
74
|
+
effect.activeCallsCount = effect.activeCallsCount + 1
|
|
75
|
+
Prometheus.EffectCalls.activeCallsCount->Prometheus.SafeGauge.handleInt(
|
|
76
|
+
~labels=effectName,
|
|
77
|
+
~value=effect.activeCallsCount,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if hadActiveCalls {
|
|
81
|
+
let elapsed = Hrtime.secondsBetween(~from=effect.prevCallStartTimerRef, ~to=timerRef)
|
|
82
|
+
if elapsed > 0. {
|
|
83
|
+
Prometheus.EffectCalls.timeCounter->Prometheus.SafeCounter.handleFloat(
|
|
84
|
+
~labels=effectName,
|
|
85
|
+
~value=elapsed,
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
effect.prevCallStartTimerRef = timerRef
|
|
90
|
+
|
|
91
|
+
effect.handler(arg)
|
|
92
|
+
->Promise.thenResolve(output => {
|
|
93
|
+
inMemTable.dict->Js.Dict.set(arg.cacheKey, output)
|
|
94
|
+
if arg.context.cache {
|
|
95
|
+
inMemTable.idsToStore->Array.push(arg.cacheKey)->ignore
|
|
96
|
+
}
|
|
97
|
+
})
|
|
98
|
+
->Promise.catchResolve(exn => {
|
|
99
|
+
onError(~inputKey=arg.cacheKey, ~exn)
|
|
100
|
+
})
|
|
101
|
+
->Promise.finally(() => {
|
|
102
|
+
effect.activeCallsCount = effect.activeCallsCount - 1
|
|
103
|
+
Prometheus.EffectCalls.activeCallsCount->Prometheus.SafeGauge.handleInt(
|
|
104
|
+
~labels=effectName,
|
|
105
|
+
~value=effect.activeCallsCount,
|
|
106
|
+
)
|
|
107
|
+
let newTimer = Hrtime.makeTimer()
|
|
108
|
+
Prometheus.EffectCalls.timeCounter->Prometheus.SafeCounter.handleFloat(
|
|
109
|
+
~labels=effectName,
|
|
110
|
+
~value=Hrtime.secondsBetween(~from=effect.prevCallStartTimerRef, ~to=newTimer),
|
|
111
|
+
)
|
|
112
|
+
effect.prevCallStartTimerRef = newTimer
|
|
113
|
+
|
|
114
|
+
Prometheus.EffectCalls.totalCallsCount->Prometheus.SafeCounter.increment(~labels=effectName)
|
|
115
|
+
Prometheus.EffectCalls.sumTimeCounter->Prometheus.SafeCounter.handleFloat(
|
|
116
|
+
~labels=effectName,
|
|
117
|
+
~value=timerRef->Hrtime.timeSince->Hrtime.toSecondsFloat,
|
|
118
|
+
)
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
let rec executeWithRateLimit = (
|
|
123
|
+
~effect: Internal.effect,
|
|
124
|
+
~effectArgs: array<Internal.effectArgs>,
|
|
125
|
+
~inMemTable,
|
|
126
|
+
~onError,
|
|
127
|
+
~isFromQueue: bool,
|
|
128
|
+
) => {
|
|
129
|
+
let effectName = effect.name
|
|
130
|
+
|
|
131
|
+
let timerRef = Hrtime.makeTimer()
|
|
132
|
+
let promises = []
|
|
133
|
+
|
|
134
|
+
switch effect.rateLimit {
|
|
135
|
+
| None =>
|
|
136
|
+
// No rate limiting - execute all immediately
|
|
137
|
+
for idx in 0 to effectArgs->Array.length - 1 {
|
|
138
|
+
promises
|
|
139
|
+
->Array.push(
|
|
140
|
+
callEffect(
|
|
141
|
+
~effect,
|
|
142
|
+
~arg=effectArgs->Array.getUnsafe(idx),
|
|
143
|
+
~inMemTable,
|
|
144
|
+
~timerRef,
|
|
145
|
+
~onError,
|
|
146
|
+
)->Promise.ignoreValue,
|
|
147
|
+
)
|
|
148
|
+
->ignore
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
| Some(state) =>
|
|
152
|
+
let now = Js.Date.now()
|
|
153
|
+
|
|
154
|
+
// Check if we need to reset the window
|
|
155
|
+
if now >= state.windowStartTime +. state.durationMs->Int.toFloat {
|
|
156
|
+
state.availableCalls = state.callsPerDuration
|
|
157
|
+
state.windowStartTime = now
|
|
158
|
+
state.nextWindowPromise = None
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Split into immediate and queued
|
|
162
|
+
let immediateCount = Js.Math.min_int(state.availableCalls, effectArgs->Array.length)
|
|
163
|
+
let immediateArgs = effectArgs->Array.slice(~offset=0, ~len=immediateCount)
|
|
164
|
+
let queuedArgs = effectArgs->Array.sliceToEnd(immediateCount)
|
|
165
|
+
|
|
166
|
+
// Update available calls
|
|
167
|
+
state.availableCalls = state.availableCalls - immediateCount
|
|
168
|
+
|
|
169
|
+
// Call immediate effects
|
|
170
|
+
for idx in 0 to immediateArgs->Array.length - 1 {
|
|
171
|
+
promises
|
|
172
|
+
->Array.push(
|
|
173
|
+
callEffect(
|
|
174
|
+
~effect,
|
|
175
|
+
~arg=immediateArgs->Array.getUnsafe(idx),
|
|
176
|
+
~inMemTable,
|
|
177
|
+
~timerRef,
|
|
178
|
+
~onError,
|
|
179
|
+
)->Promise.ignoreValue,
|
|
180
|
+
)
|
|
181
|
+
->ignore
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if immediateCount > 0 && isFromQueue {
|
|
185
|
+
// Update queue count metric
|
|
186
|
+
state.queueCount = state.queueCount - immediateCount
|
|
187
|
+
Prometheus.EffectQueueCount.set(~count=state.queueCount, ~effectName)
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Handle queued items
|
|
191
|
+
if queuedArgs->Utils.Array.notEmpty {
|
|
192
|
+
if !isFromQueue {
|
|
193
|
+
// Update queue count metric
|
|
194
|
+
state.queueCount = state.queueCount + queuedArgs->Array.length
|
|
195
|
+
Prometheus.EffectQueueCount.set(~count=state.queueCount, ~effectName)
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let millisUntilReset = ref(0)
|
|
199
|
+
let nextWindowPromise = switch state.nextWindowPromise {
|
|
200
|
+
| Some(p) => p
|
|
201
|
+
| None =>
|
|
202
|
+
millisUntilReset :=
|
|
203
|
+
(state.windowStartTime +. state.durationMs->Int.toFloat -. now)->Float.toInt
|
|
204
|
+
let p = Utils.delay(millisUntilReset.contents)
|
|
205
|
+
state.nextWindowPromise = Some(p)
|
|
206
|
+
p
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Wait for next window and recursively process queue
|
|
210
|
+
promises
|
|
211
|
+
->Array.push(
|
|
212
|
+
nextWindowPromise
|
|
213
|
+
->Promise.then(() => {
|
|
214
|
+
if millisUntilReset.contents > 0 {
|
|
215
|
+
Prometheus.EffectQueueCount.timeCounter->Prometheus.SafeCounter.handleFloat(
|
|
216
|
+
~labels=effectName,
|
|
217
|
+
~value=millisUntilReset.contents->Int.toFloat /. 1000.,
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
executeWithRateLimit(
|
|
221
|
+
~effect,
|
|
222
|
+
~effectArgs=queuedArgs,
|
|
223
|
+
~inMemTable,
|
|
224
|
+
~onError,
|
|
225
|
+
~isFromQueue=true,
|
|
226
|
+
)
|
|
227
|
+
})
|
|
228
|
+
->Promise.ignoreValue,
|
|
229
|
+
)
|
|
230
|
+
->ignore
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Wait for all to complete
|
|
235
|
+
promises->Promise.all
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
let loadEffect = (
|
|
239
|
+
~loadManager,
|
|
240
|
+
~persistence: Persistence.t,
|
|
241
|
+
~effect: Internal.effect,
|
|
242
|
+
~effectArgs,
|
|
243
|
+
~inMemoryStore,
|
|
244
|
+
~shouldGroup,
|
|
245
|
+
~item,
|
|
246
|
+
) => {
|
|
247
|
+
let effectName = effect.name
|
|
248
|
+
let key = `${effectName}.effect`
|
|
249
|
+
let inMemTable = inMemoryStore->InMemoryStore.getEffectInMemTable(~effect)
|
|
250
|
+
|
|
251
|
+
let load = async (args, ~onError) => {
|
|
252
|
+
let idsToLoad = args->Js.Array2.map((arg: Internal.effectArgs) => arg.cacheKey)
|
|
253
|
+
let idsFromCache = Utils.Set.make()
|
|
254
|
+
|
|
255
|
+
if (
|
|
256
|
+
switch persistence.storageStatus {
|
|
257
|
+
| Ready({cache}) => cache->Utils.Dict.has(effectName)
|
|
258
|
+
| _ => false
|
|
259
|
+
}
|
|
260
|
+
) {
|
|
261
|
+
let timerRef = Prometheus.StorageLoad.startOperation(~operation=key)
|
|
262
|
+
let {table, outputSchema} = effect.storageMeta
|
|
263
|
+
|
|
264
|
+
let dbEntities = try {
|
|
265
|
+
await (persistence->Persistence.getInitializedStorageOrThrow).loadByIdsOrThrow(
|
|
266
|
+
~table,
|
|
267
|
+
~rowsSchema=Internal.effectCacheItemRowsSchema,
|
|
268
|
+
~ids=idsToLoad,
|
|
269
|
+
)
|
|
270
|
+
} catch {
|
|
271
|
+
| exn =>
|
|
272
|
+
item
|
|
273
|
+
->Logging.getItemLogger
|
|
274
|
+
->Logging.childWarn({
|
|
275
|
+
"msg": `Failed to load cache effect cache. The indexer will continue working, but the effect will not be able to use the cache.`,
|
|
276
|
+
"err": exn->Utils.prettifyExn,
|
|
277
|
+
"effect": effectName,
|
|
278
|
+
})
|
|
279
|
+
[]
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
dbEntities->Js.Array2.forEach(dbEntity => {
|
|
283
|
+
try {
|
|
284
|
+
let output = dbEntity.output->S.parseOrThrow(outputSchema)
|
|
285
|
+
idsFromCache->Utils.Set.add(dbEntity.id)->ignore
|
|
286
|
+
inMemTable.dict->Js.Dict.set(dbEntity.id, output)
|
|
287
|
+
} catch {
|
|
288
|
+
| S.Raised(error) =>
|
|
289
|
+
inMemTable.invalidationsCount = inMemTable.invalidationsCount + 1
|
|
290
|
+
Prometheus.EffectCacheInvalidationsCount.increment(~effectName)
|
|
291
|
+
item
|
|
292
|
+
->Logging.getItemLogger
|
|
293
|
+
->Logging.childTrace({
|
|
294
|
+
"msg": "Invalidated effect cache",
|
|
295
|
+
"input": dbEntity.id,
|
|
296
|
+
"effect": effectName,
|
|
297
|
+
"err": error->S.Error.message,
|
|
298
|
+
})
|
|
299
|
+
}
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
timerRef->Prometheus.StorageLoad.endOperation(
|
|
303
|
+
~operation=key,
|
|
304
|
+
~whereSize=idsToLoad->Array.length,
|
|
305
|
+
~size=dbEntities->Array.length,
|
|
306
|
+
)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
let remainingCallsCount = idsToLoad->Array.length - idsFromCache->Utils.Set.size
|
|
310
|
+
if remainingCallsCount > 0 {
|
|
311
|
+
let argsToCall = []
|
|
312
|
+
for idx in 0 to args->Array.length - 1 {
|
|
313
|
+
let arg = args->Array.getUnsafe(idx)
|
|
314
|
+
if !(idsFromCache->Utils.Set.has(arg.cacheKey)) {
|
|
315
|
+
argsToCall->Array.push(arg)->ignore
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
if argsToCall->Utils.Array.notEmpty {
|
|
320
|
+
await executeWithRateLimit(
|
|
321
|
+
~effect,
|
|
322
|
+
~effectArgs=argsToCall,
|
|
323
|
+
~inMemTable,
|
|
324
|
+
~onError,
|
|
325
|
+
~isFromQueue=false,
|
|
326
|
+
)->Promise.ignoreValue
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
loadManager->LoadManager.call(
|
|
332
|
+
~key,
|
|
333
|
+
~load,
|
|
334
|
+
~shouldGroup,
|
|
335
|
+
~hasher=args => args.cacheKey,
|
|
336
|
+
~getUnsafeInMemory=hash => inMemTable.dict->Js.Dict.unsafeGet(hash),
|
|
337
|
+
~hasInMemory=hash => inMemTable.dict->Utils.Dict.has(hash),
|
|
338
|
+
~input=effectArgs,
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
let loadByField = (
|
|
343
|
+
~loadManager,
|
|
344
|
+
~persistence: Persistence.t,
|
|
345
|
+
~operator: TableIndices.Operator.t,
|
|
346
|
+
~entityConfig: Internal.entityConfig,
|
|
347
|
+
~inMemoryStore,
|
|
348
|
+
~fieldName,
|
|
349
|
+
~fieldValueSchema,
|
|
350
|
+
~shouldGroup,
|
|
351
|
+
~item,
|
|
352
|
+
~fieldValue,
|
|
353
|
+
) => {
|
|
354
|
+
let operatorCallName = switch operator {
|
|
355
|
+
| Eq => "eq"
|
|
356
|
+
| Gt => "gt"
|
|
357
|
+
| Lt => "lt"
|
|
358
|
+
}
|
|
359
|
+
let key = `${entityConfig.name}.getWhere.${fieldName}.${operatorCallName}`
|
|
360
|
+
let inMemTable = inMemoryStore->InMemoryStore.getInMemTable(~entityConfig)
|
|
361
|
+
|
|
362
|
+
let load = async (fieldValues: array<'fieldValue>, ~onError as _) => {
|
|
363
|
+
let timerRef = Prometheus.StorageLoad.startOperation(~operation=key)
|
|
364
|
+
|
|
365
|
+
let size = ref(0)
|
|
366
|
+
|
|
367
|
+
let indiciesToLoad = fieldValues->Js.Array2.map((fieldValue): TableIndices.Index.t => {
|
|
368
|
+
Single({
|
|
369
|
+
fieldName,
|
|
370
|
+
fieldValue: TableIndices.FieldValue.castFrom(fieldValue),
|
|
371
|
+
operator,
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
let _ =
|
|
376
|
+
await indiciesToLoad
|
|
377
|
+
->Js.Array2.map(async index => {
|
|
378
|
+
inMemTable->InMemoryTable.Entity.addEmptyIndex(~index)
|
|
379
|
+
try {
|
|
380
|
+
let entities = await (
|
|
381
|
+
persistence->Persistence.getInitializedStorageOrThrow
|
|
382
|
+
).loadByFieldOrThrow(
|
|
383
|
+
~operator=switch index {
|
|
384
|
+
| Single({operator: Gt}) => #">"
|
|
385
|
+
| Single({operator: Eq}) => #"="
|
|
386
|
+
| Single({operator: Lt}) => #"<"
|
|
387
|
+
},
|
|
388
|
+
~table=entityConfig.table,
|
|
389
|
+
~rowsSchema=entityConfig.rowsSchema,
|
|
390
|
+
~fieldName=index->TableIndices.Index.getFieldName,
|
|
391
|
+
~fieldValue=switch index {
|
|
392
|
+
| Single({fieldValue}) => fieldValue
|
|
393
|
+
},
|
|
394
|
+
~fieldSchema=fieldValueSchema->(
|
|
395
|
+
Utils.magic: S.t<'fieldValue> => S.t<TableIndices.FieldValue.t>
|
|
396
|
+
),
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
entities->Array.forEach(entity => {
|
|
400
|
+
//Set the entity in the in memory store
|
|
401
|
+
inMemTable->InMemoryTable.Entity.initValue(
|
|
402
|
+
~allowOverWriteEntity=false,
|
|
403
|
+
~key=entity.id,
|
|
404
|
+
~entity=Some(entity),
|
|
405
|
+
)
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
size := size.contents + entities->Array.length
|
|
409
|
+
} catch {
|
|
410
|
+
| Persistence.StorageError({message, reason}) =>
|
|
411
|
+
reason->ErrorHandling.mkLogAndRaise(
|
|
412
|
+
~logger=Logging.createChildFrom(
|
|
413
|
+
~logger=item->Logging.getItemLogger,
|
|
414
|
+
~params={
|
|
415
|
+
"operator": operatorCallName,
|
|
416
|
+
"tableName": entityConfig.table.tableName,
|
|
417
|
+
"fieldName": fieldName,
|
|
418
|
+
"fieldValue": fieldValue,
|
|
419
|
+
},
|
|
420
|
+
),
|
|
421
|
+
~msg=message,
|
|
422
|
+
)
|
|
423
|
+
}
|
|
424
|
+
})
|
|
425
|
+
->Promise.all
|
|
426
|
+
|
|
427
|
+
timerRef->Prometheus.StorageLoad.endOperation(
|
|
428
|
+
~operation=key,
|
|
429
|
+
~whereSize=fieldValues->Array.length,
|
|
430
|
+
~size=size.contents,
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
loadManager->LoadManager.call(
|
|
435
|
+
~key,
|
|
436
|
+
~load,
|
|
437
|
+
~input=fieldValue,
|
|
438
|
+
~shouldGroup,
|
|
439
|
+
~hasher=fieldValue =>
|
|
440
|
+
fieldValue->TableIndices.FieldValue.castFrom->TableIndices.FieldValue.toString,
|
|
441
|
+
~getUnsafeInMemory=inMemTable->InMemoryTable.Entity.getUnsafeOnIndex(~fieldName, ~operator),
|
|
442
|
+
~hasInMemory=inMemTable->InMemoryTable.Entity.hasIndex(~fieldName, ~operator),
|
|
443
|
+
)
|
|
444
|
+
}
|