envio 3.0.0-alpha.1 → 3.0.0-alpha.3
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/evm.schema.json +63 -48
- package/fuel.schema.json +35 -31
- package/index.d.ts +1 -0
- package/package.json +15 -11
- package/rescript.json +1 -1
- package/src/Batch.res.mjs +1 -1
- package/src/Benchmark.res +394 -0
- package/src/Benchmark.res.mjs +398 -0
- package/src/ChainFetcher.res +459 -0
- package/src/ChainFetcher.res.mjs +281 -0
- package/src/ChainManager.res +179 -0
- package/src/ChainManager.res.mjs +139 -0
- package/src/Config.res +18 -7
- package/src/Config.res.mjs +28 -7
- package/src/Ecosystem.res +25 -0
- package/src/Ecosystem.res.mjs +29 -0
- package/src/Env.res +243 -0
- package/src/Env.res.mjs +270 -0
- package/src/Envio.gen.ts +9 -1
- package/src/Envio.res +12 -9
- package/src/EventProcessing.res +476 -0
- package/src/EventProcessing.res.mjs +341 -0
- package/src/EventRegister.res +4 -15
- package/src/EventRegister.res.mjs +3 -9
- package/src/EventRegister.resi +2 -8
- package/src/FetchState.res +54 -29
- package/src/FetchState.res.mjs +62 -35
- package/src/GlobalState.res +1169 -0
- package/src/GlobalState.res.mjs +1196 -0
- package/src/Internal.gen.ts +3 -14
- package/src/Internal.res +4 -12
- package/src/LoadLayer.res +444 -0
- package/src/LoadLayer.res.mjs +296 -0
- package/src/LoadLayer.resi +32 -0
- package/src/Prometheus.res +8 -8
- package/src/Prometheus.res.mjs +10 -10
- package/src/ReorgDetection.res +6 -10
- package/src/ReorgDetection.res.mjs +6 -6
- package/src/UserContext.res +356 -0
- package/src/UserContext.res.mjs +238 -0
- package/src/bindings/DateFns.res +71 -0
- package/src/bindings/DateFns.res.mjs +22 -0
- package/src/bindings/EventSource.res +13 -0
- package/src/bindings/EventSource.res.mjs +2 -0
- package/src/sources/Evm.res +87 -0
- package/src/sources/Evm.res.mjs +105 -0
- package/src/sources/EvmChain.res +95 -0
- package/src/sources/EvmChain.res.mjs +61 -0
- package/src/sources/Fuel.res +19 -34
- package/src/sources/Fuel.res.mjs +34 -16
- package/src/sources/FuelSDK.res +37 -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 +8 -8
- package/src/sources/HyperFuelSource.res.mjs +5 -5
- package/src/sources/HyperSyncHeightStream.res +179 -0
- package/src/sources/HyperSyncHeightStream.res.mjs +127 -0
- package/src/sources/HyperSyncSource.res +7 -65
- package/src/sources/HyperSyncSource.res.mjs +10 -66
- package/src/sources/RpcSource.res +4 -4
- package/src/sources/RpcSource.res.mjs +3 -3
- package/src/sources/Solana.res +59 -0
- package/src/sources/Solana.res.mjs +79 -0
- package/src/sources/Source.res +2 -2
- package/src/sources/SourceManager.res +24 -32
- package/src/sources/SourceManager.res.mjs +20 -20
- package/src/sources/SourceManager.resi +4 -5
- package/src/Platform.res +0 -140
- package/src/Platform.res.mjs +0 -170
package/src/Internal.gen.ts
CHANGED
|
@@ -26,11 +26,7 @@ export type genericContractRegisterArgs<event,context> = { readonly event: event
|
|
|
26
26
|
|
|
27
27
|
export type genericContractRegister<args> = $$genericContractRegister<args>;
|
|
28
28
|
|
|
29
|
-
export type genericHandlerArgs<event,context
|
|
30
|
-
readonly event: event;
|
|
31
|
-
readonly context: context;
|
|
32
|
-
readonly loaderReturn: loaderReturn
|
|
33
|
-
};
|
|
29
|
+
export type genericHandlerArgs<event,context> = { readonly event: event; readonly context: context };
|
|
34
30
|
|
|
35
31
|
export type genericHandler<args> = (_1:args) => Promise<void>;
|
|
36
32
|
|
|
@@ -46,17 +42,10 @@ export type genericHandlerWithLoader<loader,handler,eventFilters> = {
|
|
|
46
42
|
readonly loader: loader;
|
|
47
43
|
readonly handler: handler;
|
|
48
44
|
readonly wildcard?: boolean;
|
|
49
|
-
readonly eventFilters?: eventFilters
|
|
50
|
-
/** @deprecated The option is removed starting from v2.19 since we made the default mode even faster than pre-registration. */
|
|
51
|
-
readonly preRegisterDynamicContracts?: boolean
|
|
45
|
+
readonly eventFilters?: eventFilters
|
|
52
46
|
};
|
|
53
47
|
|
|
54
|
-
export type eventOptions<eventFilters> = {
|
|
55
|
-
readonly wildcard?: boolean;
|
|
56
|
-
readonly eventFilters?: eventFilters;
|
|
57
|
-
/** @deprecated The option is removed starting from v2.19 since we made the default mode even faster than pre-registration. */
|
|
58
|
-
readonly preRegisterDynamicContracts?: boolean
|
|
59
|
-
};
|
|
48
|
+
export type eventOptions<eventFilters> = { readonly wildcard?: boolean; readonly eventFilters?: eventFilters };
|
|
60
49
|
|
|
61
50
|
export type fuelSupplyParams = { readonly subId: string; readonly amount: bigint };
|
|
62
51
|
|
package/src/Internal.res
CHANGED
|
@@ -37,10 +37,9 @@ type contractRegisterArgs = genericContractRegisterArgs<event, contractRegisterC
|
|
|
37
37
|
type contractRegister = genericContractRegister<contractRegisterArgs>
|
|
38
38
|
|
|
39
39
|
@genType
|
|
40
|
-
type genericHandlerArgs<'event, 'context
|
|
40
|
+
type genericHandlerArgs<'event, 'context> = {
|
|
41
41
|
event: 'event,
|
|
42
42
|
context: 'context,
|
|
43
|
-
loaderReturn: 'loaderReturn,
|
|
44
43
|
}
|
|
45
44
|
@genType
|
|
46
45
|
type genericHandler<'args> = 'args => promise<unit>
|
|
@@ -81,10 +80,6 @@ type genericHandlerWithLoader<'loader, 'handler, 'eventFilters> = {
|
|
|
81
80
|
handler: 'handler,
|
|
82
81
|
wildcard?: bool,
|
|
83
82
|
eventFilters?: 'eventFilters,
|
|
84
|
-
/**
|
|
85
|
-
@deprecated The option is removed starting from v2.19 since we made the default mode even faster than pre-registration.
|
|
86
|
-
*/
|
|
87
|
-
preRegisterDynamicContracts?: bool,
|
|
88
83
|
}
|
|
89
84
|
|
|
90
85
|
// This is private so it's not manually constructed internally
|
|
@@ -170,11 +165,12 @@ type eventItem = private {
|
|
|
170
165
|
event: event,
|
|
171
166
|
}
|
|
172
167
|
|
|
173
|
-
// Opaque type to support both EVM and
|
|
168
|
+
// Opaque type to support both EVM and other ecosystems
|
|
174
169
|
type blockEvent
|
|
175
170
|
|
|
176
171
|
type onBlockArgs = {
|
|
177
|
-
|
|
172
|
+
slot?: int,
|
|
173
|
+
block?: blockEvent,
|
|
178
174
|
context: handlerContext,
|
|
179
175
|
}
|
|
180
176
|
|
|
@@ -225,10 +221,6 @@ external setItemDcs: (item, dcs) => unit = "dcs"
|
|
|
225
221
|
type eventOptions<'eventFilters> = {
|
|
226
222
|
wildcard?: bool,
|
|
227
223
|
eventFilters?: 'eventFilters,
|
|
228
|
-
/**
|
|
229
|
-
@deprecated The option is removed starting from v2.19 since we made the default mode even faster than pre-registration.
|
|
230
|
-
*/
|
|
231
|
-
preRegisterDynamicContracts?: bool,
|
|
232
224
|
}
|
|
233
225
|
|
|
234
226
|
@genType
|
|
@@ -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.millisBetween(~from=effect.prevCallStartTimerRef, ~to=timerRef)
|
|
82
|
+
if elapsed > 0 {
|
|
83
|
+
Prometheus.EffectCalls.timeCounter->Prometheus.SafeCounter.incrementMany(
|
|
84
|
+
~labels=effectName,
|
|
85
|
+
~value=Hrtime.millisBetween(~from=effect.prevCallStartTimerRef, ~to=timerRef),
|
|
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.incrementMany(
|
|
109
|
+
~labels=effectName,
|
|
110
|
+
~value=Hrtime.millisBetween(~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.incrementMany(
|
|
116
|
+
~labels=effectName,
|
|
117
|
+
~value=timerRef->Hrtime.timeSince->Hrtime.toMillis->Hrtime.intFromMillis,
|
|
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.incrementMany(
|
|
216
|
+
~labels=effectName,
|
|
217
|
+
~value=millisUntilReset.contents,
|
|
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
|
+
}
|