envio 3.0.2 → 3.1.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -1
- package/evm.schema.json +15 -8
- package/fuel.schema.json +19 -12
- package/index.d.ts +0 -2
- package/package.json +6 -7
- package/rescript.json +1 -1
- package/src/Batch.res +4 -214
- package/src/Batch.res.mjs +6 -165
- package/src/ChainFetcher.res +12 -28
- package/src/ChainFetcher.res.mjs +8 -17
- package/src/ChainManager.res +10 -9
- package/src/ChainManager.res.mjs +6 -10
- package/src/Config.res +9 -25
- package/src/Config.res.mjs +17 -27
- package/src/Core.res +7 -0
- package/src/Ctx.res +1 -0
- package/src/Env.res +0 -8
- package/src/Env.res.mjs +0 -6
- package/src/EventConfigBuilder.res +13 -123
- package/src/EventConfigBuilder.res.mjs +6 -73
- package/src/EventProcessing.res +5 -29
- package/src/EventProcessing.res.mjs +11 -20
- package/src/EventUtils.res +0 -27
- package/src/EventUtils.res.mjs +0 -24
- package/src/FetchState.res +2 -15
- package/src/FetchState.res.mjs +3 -18
- package/src/GlobalState.res +26 -39
- package/src/GlobalState.res.mjs +12 -40
- package/src/HandlerLoader.res +6 -5
- package/src/HandlerLoader.res.mjs +27 -9
- package/src/HandlerRegister.res +1 -12
- package/src/HandlerRegister.res.mjs +1 -6
- package/src/HandlerRegister.resi +1 -1
- package/src/Hasura.res +96 -32
- package/src/Hasura.res.mjs +93 -38
- package/src/InMemoryStore.res +205 -45
- package/src/InMemoryStore.res.mjs +157 -40
- package/src/InMemoryTable.res +165 -249
- package/src/InMemoryTable.res.mjs +156 -227
- package/src/Internal.res +10 -34
- package/src/Internal.res.mjs +9 -3
- package/src/LoadLayer.res +5 -5
- package/src/LoadLayer.res.mjs +5 -5
- package/src/LogSelection.res +15 -19
- package/src/LogSelection.res.mjs +5 -6
- package/src/Main.res +4 -6
- package/src/Main.res.mjs +26 -15
- package/src/Persistence.res +7 -132
- package/src/Persistence.res.mjs +1 -102
- package/src/PgStorage.res +57 -40
- package/src/PgStorage.res.mjs +60 -34
- package/src/ReorgDetection.res +35 -58
- package/src/ReorgDetection.res.mjs +21 -29
- package/src/SimulateItems.res.mjs +21 -3
- package/src/Sink.res +2 -2
- package/src/Sink.res.mjs +1 -1
- package/src/TableIndices.res +9 -2
- package/src/TableIndices.res.mjs +7 -1
- package/src/TestIndexer.res +53 -60
- package/src/TestIndexer.res.mjs +77 -63
- package/src/TestIndexerProxyStorage.res +4 -14
- package/src/TestIndexerProxyStorage.res.mjs +1 -5
- package/src/UserContext.res +2 -4
- package/src/UserContext.res.mjs +4 -5
- package/src/Utils.res +0 -2
- package/src/Utils.res.mjs +0 -3
- package/src/bindings/ClickHouse.res +45 -38
- package/src/bindings/ClickHouse.res.mjs +16 -17
- package/src/bindings/Vitest.res +3 -0
- package/src/db/InternalTable.res +59 -18
- package/src/db/InternalTable.res.mjs +82 -51
- package/src/db/Table.res +9 -2
- package/src/db/Table.res.mjs +10 -7
- package/src/sources/EnvioApiClient.res +15 -0
- package/src/sources/EnvioApiClient.res.mjs +24 -0
- package/src/sources/EvmChain.res +32 -10
- package/src/sources/EvmChain.res.mjs +31 -5
- package/src/sources/HyperFuelSource.res +15 -58
- package/src/sources/HyperFuelSource.res.mjs +20 -39
- package/src/sources/HyperSync.res +54 -100
- package/src/sources/HyperSync.res.mjs +67 -96
- package/src/sources/HyperSync.resi +4 -22
- package/src/sources/HyperSyncClient.res +70 -247
- package/src/sources/HyperSyncClient.res.mjs +47 -46
- package/src/sources/HyperSyncSource.res +94 -166
- package/src/sources/HyperSyncSource.res.mjs +100 -127
- package/src/sources/RpcSource.res +43 -22
- package/src/sources/RpcSource.res.mjs +50 -35
- package/src/sources/SimulateSource.res +1 -7
- package/src/sources/SimulateSource.res.mjs +1 -7
- package/src/sources/Source.res +10 -1
- package/src/sources/Source.res.mjs +3 -0
- package/src/sources/SourceManager.res +177 -8
- package/src/sources/SourceManager.res.mjs +141 -3
- package/src/sources/SourceManager.resi +19 -0
- package/src/tui/Tui.res +44 -6
- package/src/tui/Tui.res.mjs +56 -8
- package/src/tui/components/TuiData.res +3 -0
- package/svm.schema.json +11 -4
- package/src/sources/HyperSyncJsonApi.res +0 -390
- package/src/sources/HyperSyncJsonApi.res.mjs +0 -237
package/src/Hasura.res
CHANGED
|
@@ -110,7 +110,13 @@ let clearHasuraMetadata = async (~endpoint, ~auth) => {
|
|
|
110
110
|
}
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
|
|
113
|
+
type trackTableConfig = {
|
|
114
|
+
tableName: string,
|
|
115
|
+
description: option<string>,
|
|
116
|
+
columnDescriptions: dict<string>,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
let trackTables = async (~endpoint, ~auth, ~pgSchema, ~tableConfigs: array<trackTableConfig>) => {
|
|
114
120
|
try {
|
|
115
121
|
let result = await trackTablesRoute->Rest.fetch(
|
|
116
122
|
{
|
|
@@ -118,17 +124,33 @@ let trackTables = async (~endpoint, ~auth, ~pgSchema, ~tableNames: array<string>
|
|
|
118
124
|
"args": {
|
|
119
125
|
// If set to false, any warnings will cause the API call to fail and no new tables to be tracked. Otherwise tables that fail to track will be raised as warnings. (default: true)
|
|
120
126
|
"allow_warnings": false,
|
|
121
|
-
"tables":
|
|
127
|
+
"tables": tableConfigs->Array.map(({tableName, description, columnDescriptions}) => {
|
|
128
|
+
let configuration = dict{
|
|
129
|
+
"custom_name": tableName->(Utils.magic: string => JSON.t),
|
|
130
|
+
}
|
|
131
|
+
switch description {
|
|
132
|
+
| Some(d) => configuration->Dict.set("comment", d->(Utils.magic: string => JSON.t))
|
|
133
|
+
| None => ()
|
|
134
|
+
}
|
|
135
|
+
let columnConfigEntries = columnDescriptions->Dict.toArray
|
|
136
|
+
if columnConfigEntries->Array.length > 0 {
|
|
137
|
+
let columnConfig = dict{}
|
|
138
|
+
columnConfigEntries->Array.forEach(((column, comment)) =>
|
|
139
|
+
columnConfig->Dict.set(column, {"comment": comment}->(Utils.magic: {..} => JSON.t))
|
|
140
|
+
)
|
|
141
|
+
configuration->Dict.set(
|
|
142
|
+
"column_config",
|
|
143
|
+
columnConfig->(Utils.magic: dict<JSON.t> => JSON.t),
|
|
144
|
+
)
|
|
145
|
+
}
|
|
122
146
|
{
|
|
123
147
|
"table": {
|
|
124
148
|
"name": tableName,
|
|
125
149
|
"schema": pgSchema,
|
|
126
150
|
},
|
|
127
|
-
"configuration":
|
|
128
|
-
"custom_name": tableName,
|
|
129
|
-
},
|
|
151
|
+
"configuration": configuration,
|
|
130
152
|
}
|
|
131
|
-
),
|
|
153
|
+
}),
|
|
132
154
|
}->(Utils.magic: 'a => JSON.t),
|
|
133
155
|
},
|
|
134
156
|
~client=Rest.client(endpoint),
|
|
@@ -139,13 +161,13 @@ let trackTables = async (~endpoint, ~auth, ~pgSchema, ~tableNames: array<string>
|
|
|
139
161
|
}
|
|
140
162
|
Logging.trace({
|
|
141
163
|
"msg": msg,
|
|
142
|
-
"tableNames":
|
|
164
|
+
"tableNames": tableConfigs->Array.map(c => c.tableName),
|
|
143
165
|
})
|
|
144
166
|
} catch {
|
|
145
167
|
| exn =>
|
|
146
168
|
Logging.error({
|
|
147
169
|
"msg": `There was an issue tracking tables in hasura - indexing may still work - but you may have issues querying the data in hasura.`,
|
|
148
|
-
"tableNames":
|
|
170
|
+
"tableNames": tableConfigs->Array.map(c => c.tableName),
|
|
149
171
|
"err": exn->Utils.prettifyExn,
|
|
150
172
|
})
|
|
151
173
|
}
|
|
@@ -192,31 +214,41 @@ let createEntityRelationship = async (
|
|
|
192
214
|
~objectName: string,
|
|
193
215
|
~mappedEntity: string,
|
|
194
216
|
~isDerivedFrom: bool,
|
|
217
|
+
~comment: option<string>=?,
|
|
195
218
|
) => {
|
|
196
219
|
let derivedFromTo = isDerivedFrom ? `"id": "${relationalKey}"` : `"${relationalKey}_id" : "id"`
|
|
197
220
|
|
|
221
|
+
let tableJson = {
|
|
222
|
+
"schema": pgSchema,
|
|
223
|
+
"name": tableName,
|
|
224
|
+
}->(Utils.magic: {..} => JSON.t)
|
|
225
|
+
let usingJson = {
|
|
226
|
+
"manual_configuration": {
|
|
227
|
+
"remote_table": {
|
|
228
|
+
"schema": pgSchema,
|
|
229
|
+
"name": mappedEntity,
|
|
230
|
+
},
|
|
231
|
+
"column_mapping": JSON.parseOrThrow(`{${derivedFromTo}}`),
|
|
232
|
+
},
|
|
233
|
+
}->(Utils.magic: {..} => JSON.t)
|
|
234
|
+
|
|
235
|
+
let args = dict{
|
|
236
|
+
"table": tableJson,
|
|
237
|
+
"name": objectName->(Utils.magic: string => JSON.t),
|
|
238
|
+
"source": "default"->(Utils.magic: string => JSON.t),
|
|
239
|
+
"using": usingJson,
|
|
240
|
+
}
|
|
241
|
+
switch comment {
|
|
242
|
+
| Some(c) => args->Dict.set("comment", c->(Utils.magic: string => JSON.t))
|
|
243
|
+
| None => ()
|
|
244
|
+
}
|
|
245
|
+
|
|
198
246
|
await sendOperation(
|
|
199
247
|
~endpoint,
|
|
200
248
|
~auth,
|
|
201
249
|
~operation={
|
|
202
250
|
"type": `pg_create_${relationshipType}_relationship`,
|
|
203
|
-
"args":
|
|
204
|
-
"table": {
|
|
205
|
-
"schema": pgSchema,
|
|
206
|
-
"name": tableName,
|
|
207
|
-
},
|
|
208
|
-
"name": objectName,
|
|
209
|
-
"source": "default",
|
|
210
|
-
"using": {
|
|
211
|
-
"manual_configuration": {
|
|
212
|
-
"remote_table": {
|
|
213
|
-
"schema": pgSchema,
|
|
214
|
-
"name": mappedEntity,
|
|
215
|
-
},
|
|
216
|
-
"column_mapping": JSON.parseOrThrow(`{${derivedFromTo}}`),
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
},
|
|
251
|
+
"args": args,
|
|
220
252
|
}->(Utils.magic: 'a => JSON.t),
|
|
221
253
|
)
|
|
222
254
|
}
|
|
@@ -230,19 +262,49 @@ let trackDatabase = async (
|
|
|
230
262
|
~responseLimit,
|
|
231
263
|
~schema,
|
|
232
264
|
) => {
|
|
233
|
-
let
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
265
|
+
let exposedInternalTableConfigs = [
|
|
266
|
+
{
|
|
267
|
+
tableName: InternalTable.RawEvents.table.tableName,
|
|
268
|
+
description: None,
|
|
269
|
+
columnDescriptions: dict{},
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
tableName: InternalTable.Views.metaViewName,
|
|
273
|
+
description: None,
|
|
274
|
+
columnDescriptions: dict{},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
tableName: InternalTable.Views.chainMetadataViewName,
|
|
278
|
+
description: None,
|
|
279
|
+
columnDescriptions: dict{},
|
|
280
|
+
},
|
|
237
281
|
]
|
|
238
|
-
let
|
|
239
|
-
|
|
282
|
+
let userTableConfigs = userEntities->Array.map(entity => {
|
|
283
|
+
let columnDescriptions = dict{}
|
|
284
|
+
entity.table.fields->Array.forEach(fieldOrDerived =>
|
|
285
|
+
switch fieldOrDerived {
|
|
286
|
+
| Table.Field(field) =>
|
|
287
|
+
switch field.description {
|
|
288
|
+
| Some(d) => columnDescriptions->Dict.set(field->Table.getDbFieldName, d)
|
|
289
|
+
| None => ()
|
|
290
|
+
}
|
|
291
|
+
| Table.DerivedFrom(_) => ()
|
|
292
|
+
}
|
|
293
|
+
)
|
|
294
|
+
{
|
|
295
|
+
tableName: entity.table.tableName,
|
|
296
|
+
description: entity.table.description,
|
|
297
|
+
columnDescriptions,
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
let tableConfigs = [exposedInternalTableConfigs, userTableConfigs]->Belt.Array.concatMany
|
|
301
|
+
let tableNames = tableConfigs->Array.map(c => c.tableName)
|
|
240
302
|
|
|
241
303
|
Logging.info("Tracking tables in Hasura")
|
|
242
304
|
|
|
243
305
|
let _ = await clearHasuraMetadata(~endpoint, ~auth)
|
|
244
306
|
|
|
245
|
-
await trackTables(~endpoint, ~auth, ~pgSchema, ~
|
|
307
|
+
await trackTables(~endpoint, ~auth, ~pgSchema, ~tableConfigs)
|
|
246
308
|
|
|
247
309
|
for i in 0 to tableNames->Array.length - 1 {
|
|
248
310
|
let tableName = tableNames->Array.getUnsafe(i)
|
|
@@ -278,6 +340,7 @@ let trackDatabase = async (
|
|
|
278
340
|
~objectName=derivedFromField.fieldName,
|
|
279
341
|
~relationalKey=relationalFieldName,
|
|
280
342
|
~mappedEntity=derivedFromField.derivedFromEntity,
|
|
343
|
+
~comment=?derivedFromField.description,
|
|
281
344
|
)
|
|
282
345
|
}
|
|
283
346
|
|
|
@@ -295,6 +358,7 @@ let trackDatabase = async (
|
|
|
295
358
|
~objectName=field.fieldName,
|
|
296
359
|
~relationalKey=field.fieldName,
|
|
297
360
|
~mappedEntity=linkedEntityName,
|
|
361
|
+
~comment=?field.description,
|
|
298
362
|
)
|
|
299
363
|
}
|
|
300
364
|
}
|
package/src/Hasura.res.mjs
CHANGED
|
@@ -112,34 +112,52 @@ async function clearHasuraMetadata(endpoint, auth) {
|
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
async function trackTables(endpoint, auth, pgSchema,
|
|
115
|
+
async function trackTables(endpoint, auth, pgSchema, tableConfigs) {
|
|
116
116
|
try {
|
|
117
117
|
let result = await Rest.fetch(trackTablesRoute, {
|
|
118
118
|
auth: auth,
|
|
119
119
|
args: {
|
|
120
120
|
allow_warnings: false,
|
|
121
|
-
tables:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
},
|
|
126
|
-
configuration: {
|
|
121
|
+
tables: tableConfigs.map(param => {
|
|
122
|
+
let description = param.description;
|
|
123
|
+
let tableName = param.tableName;
|
|
124
|
+
let configuration = {
|
|
127
125
|
custom_name: tableName
|
|
126
|
+
};
|
|
127
|
+
if (description !== undefined) {
|
|
128
|
+
configuration["comment"] = description;
|
|
128
129
|
}
|
|
129
|
-
|
|
130
|
+
let columnConfigEntries = Object.entries(param.columnDescriptions);
|
|
131
|
+
if (columnConfigEntries.length !== 0) {
|
|
132
|
+
let columnConfig = {};
|
|
133
|
+
columnConfigEntries.forEach(param => {
|
|
134
|
+
columnConfig[param[0]] = {
|
|
135
|
+
comment: param[1]
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
configuration["column_config"] = columnConfig;
|
|
139
|
+
}
|
|
140
|
+
return {
|
|
141
|
+
table: {
|
|
142
|
+
name: tableName,
|
|
143
|
+
schema: pgSchema
|
|
144
|
+
},
|
|
145
|
+
configuration: configuration
|
|
146
|
+
};
|
|
147
|
+
})
|
|
130
148
|
}
|
|
131
149
|
}, Rest.client(endpoint, undefined));
|
|
132
150
|
let msg;
|
|
133
151
|
msg = result === "QuerySucceeded" ? "Hasura finished tracking tables" : "Hasura tables already tracked";
|
|
134
152
|
return Logging.trace({
|
|
135
153
|
msg: msg,
|
|
136
|
-
tableNames:
|
|
154
|
+
tableNames: tableConfigs.map(c => c.tableName)
|
|
137
155
|
});
|
|
138
156
|
} catch (raw_exn) {
|
|
139
157
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
140
158
|
return Logging.error({
|
|
141
159
|
msg: `There was an issue tracking tables in hasura - indexing may still work - but you may have issues querying the data in hasura.`,
|
|
142
|
-
tableNames:
|
|
160
|
+
tableNames: tableConfigs.map(c => c.tableName),
|
|
143
161
|
err: Utils.prettifyExn(exn)
|
|
144
162
|
});
|
|
145
163
|
}
|
|
@@ -165,44 +183,81 @@ async function createSelectPermission(endpoint, auth, tableName, pgSchema, respo
|
|
|
165
183
|
});
|
|
166
184
|
}
|
|
167
185
|
|
|
168
|
-
async function createEntityRelationship(endpoint, auth, pgSchema, tableName, relationshipType, relationalKey, objectName, mappedEntity, isDerivedFrom) {
|
|
186
|
+
async function createEntityRelationship(endpoint, auth, pgSchema, tableName, relationshipType, relationalKey, objectName, mappedEntity, isDerivedFrom, comment) {
|
|
169
187
|
let derivedFromTo = isDerivedFrom ? `"id": "` + relationalKey + `"` : `"` + relationalKey + `_id" : "id"`;
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
188
|
+
let tableJson = {
|
|
189
|
+
schema: pgSchema,
|
|
190
|
+
name: tableName
|
|
191
|
+
};
|
|
192
|
+
let usingJson = {
|
|
193
|
+
manual_configuration: {
|
|
194
|
+
remote_table: {
|
|
174
195
|
schema: pgSchema,
|
|
175
|
-
name:
|
|
196
|
+
name: mappedEntity
|
|
176
197
|
},
|
|
177
|
-
|
|
178
|
-
source: "default",
|
|
179
|
-
using: {
|
|
180
|
-
manual_configuration: {
|
|
181
|
-
remote_table: {
|
|
182
|
-
schema: pgSchema,
|
|
183
|
-
name: mappedEntity
|
|
184
|
-
},
|
|
185
|
-
column_mapping: JSON.parse(`{` + derivedFromTo + `}`)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
198
|
+
column_mapping: JSON.parse(`{` + derivedFromTo + `}`)
|
|
188
199
|
}
|
|
200
|
+
};
|
|
201
|
+
let args = {
|
|
202
|
+
table: tableJson,
|
|
203
|
+
name: objectName,
|
|
204
|
+
source: "default",
|
|
205
|
+
using: usingJson
|
|
206
|
+
};
|
|
207
|
+
if (comment !== undefined) {
|
|
208
|
+
args["comment"] = comment;
|
|
209
|
+
}
|
|
210
|
+
return await sendOperation(endpoint, auth, {
|
|
211
|
+
type: `pg_create_` + relationshipType + `_relationship`,
|
|
212
|
+
args: args
|
|
189
213
|
});
|
|
190
214
|
}
|
|
191
215
|
|
|
192
216
|
async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEntities, responseLimit, schema) {
|
|
193
|
-
let
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
217
|
+
let exposedInternalTableConfigs = [
|
|
218
|
+
{
|
|
219
|
+
tableName: InternalTable.RawEvents.table.tableName,
|
|
220
|
+
description: undefined,
|
|
221
|
+
columnDescriptions: {}
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
tableName: InternalTable.Views.metaViewName,
|
|
225
|
+
description: undefined,
|
|
226
|
+
columnDescriptions: {}
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
tableName: InternalTable.Views.chainMetadataViewName,
|
|
230
|
+
description: undefined,
|
|
231
|
+
columnDescriptions: {}
|
|
232
|
+
}
|
|
197
233
|
];
|
|
198
|
-
let
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
234
|
+
let userTableConfigs = userEntities.map(entity => {
|
|
235
|
+
let columnDescriptions = {};
|
|
236
|
+
entity.table.fields.forEach(fieldOrDerived => {
|
|
237
|
+
if (fieldOrDerived.TAG !== "Field") {
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
let field = fieldOrDerived._0;
|
|
241
|
+
let d = field.description;
|
|
242
|
+
if (d !== undefined) {
|
|
243
|
+
columnDescriptions[Table.getDbFieldName(field)] = d;
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return {
|
|
248
|
+
tableName: entity.table.tableName,
|
|
249
|
+
description: entity.table.description,
|
|
250
|
+
columnDescriptions: columnDescriptions
|
|
251
|
+
};
|
|
252
|
+
});
|
|
253
|
+
let tableConfigs = Belt_Array.concatMany([
|
|
254
|
+
exposedInternalTableConfigs,
|
|
255
|
+
userTableConfigs
|
|
202
256
|
]);
|
|
257
|
+
let tableNames = tableConfigs.map(c => c.tableName);
|
|
203
258
|
Logging.info("Tracking tables in Hasura");
|
|
204
259
|
await clearHasuraMetadata(endpoint, auth);
|
|
205
|
-
await trackTables(endpoint, auth, pgSchema,
|
|
260
|
+
await trackTables(endpoint, auth, pgSchema, tableConfigs);
|
|
206
261
|
for (let i = 0, i_finish = tableNames.length; i < i_finish; ++i) {
|
|
207
262
|
let tableName = tableNames[i];
|
|
208
263
|
await createSelectPermission(endpoint, auth, tableName, pgSchema, responseLimit, aggregateEntities);
|
|
@@ -215,13 +270,13 @@ async function trackDatabase(endpoint, auth, pgSchema, userEntities, aggregateEn
|
|
|
215
270
|
for (let j = 0, j_finish = derivedFromFields.length; j < j_finish; ++j) {
|
|
216
271
|
let derivedFromField = derivedFromFields[j];
|
|
217
272
|
let relationalFieldName = Utils.unwrapResultExn(Schema.getDerivedFromFieldName(schema, derivedFromField));
|
|
218
|
-
await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "array", relationalFieldName, derivedFromField.fieldName, derivedFromField.derivedFromEntity, true);
|
|
273
|
+
await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "array", relationalFieldName, derivedFromField.fieldName, derivedFromField.derivedFromEntity, true, derivedFromField.description);
|
|
219
274
|
}
|
|
220
275
|
let linkedEntityFields = Table.getLinkedEntityFields(entityConfig.table);
|
|
221
276
|
for (let j$1 = 0, j_finish$1 = linkedEntityFields.length; j$1 < j_finish$1; ++j$1) {
|
|
222
277
|
let match$1 = linkedEntityFields[j$1];
|
|
223
278
|
let field = match$1[0];
|
|
224
|
-
await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "object", field.fieldName, field.fieldName, match$1[1], false);
|
|
279
|
+
await createEntityRelationship(endpoint, auth, pgSchema, tableName$1, "object", field.fieldName, field.fieldName, match$1[1], false, field.description);
|
|
225
280
|
}
|
|
226
281
|
}
|
|
227
282
|
return Logging.info("Hasura configuration completed");
|
package/src/InMemoryStore.res
CHANGED
|
@@ -1,42 +1,23 @@
|
|
|
1
|
-
type rawEventsKey = {
|
|
2
|
-
chainId: int,
|
|
3
|
-
eventId: string,
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
let hashRawEventsKey = (key: rawEventsKey) =>
|
|
7
|
-
EventUtils.getEventIdKeyString(~chainId=key.chainId, ~eventId=key.eventId)
|
|
8
|
-
|
|
9
1
|
module EntityTables = {
|
|
10
|
-
type t = dict<InMemoryTable.Entity.t
|
|
2
|
+
type t = dict<InMemoryTable.Entity.t>
|
|
11
3
|
exception UndefinedEntity({entityName: string})
|
|
12
4
|
let make = (entities: array<Internal.entityConfig>): t => {
|
|
13
5
|
let init = Dict.make()
|
|
14
|
-
entities->
|
|
6
|
+
entities->Array.forEach(entityConfig => {
|
|
15
7
|
init->Dict.set((entityConfig.name :> string), InMemoryTable.Entity.make())
|
|
16
8
|
})
|
|
17
9
|
init
|
|
18
10
|
}
|
|
19
11
|
|
|
20
|
-
let get = (
|
|
12
|
+
let get = (self: t, ~entityName: string) => {
|
|
21
13
|
switch self->Utils.Dict.dangerouslyGetNonOption(entityName) {
|
|
22
|
-
| Some(table) =>
|
|
23
|
-
table->(
|
|
24
|
-
Utils.magic: InMemoryTable.Entity.t<Internal.entity> => InMemoryTable.Entity.t<entity>
|
|
25
|
-
)
|
|
26
|
-
|
|
14
|
+
| Some(table) => table
|
|
27
15
|
| None =>
|
|
28
16
|
UndefinedEntity({entityName: entityName})->ErrorHandling.mkLogAndRaise(
|
|
29
17
|
~msg="Unexpected, entity InMemoryTable is undefined",
|
|
30
18
|
)
|
|
31
19
|
}
|
|
32
20
|
}
|
|
33
|
-
|
|
34
|
-
let clone = (self: t) => {
|
|
35
|
-
self
|
|
36
|
-
->Dict.toArray
|
|
37
|
-
->Belt.Array.map(((k, v)) => (k, v->InMemoryTable.Entity.clone))
|
|
38
|
-
->Dict.fromArray
|
|
39
|
-
}
|
|
40
21
|
}
|
|
41
22
|
|
|
42
23
|
type effectCacheInMemTable = {
|
|
@@ -47,30 +28,29 @@ type effectCacheInMemTable = {
|
|
|
47
28
|
}
|
|
48
29
|
|
|
49
30
|
type t = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
31
|
+
allEntities: array<Internal.entityConfig>,
|
|
32
|
+
mutable rawEvents: array<InternalTable.RawEvents.t>,
|
|
33
|
+
mutable entities: dict<InMemoryTable.Entity.t>,
|
|
34
|
+
mutable effects: dict<effectCacheInMemTable>,
|
|
35
|
+
mutable rollback: option<Persistence.rollback>,
|
|
36
|
+
mutable committedCheckpointId: Internal.checkpointId,
|
|
54
37
|
}
|
|
55
38
|
|
|
56
|
-
let make = (
|
|
57
|
-
|
|
39
|
+
let make = (
|
|
40
|
+
~entities: array<Internal.entityConfig>,
|
|
41
|
+
~committedCheckpointId=Internal.initialCheckpointId,
|
|
42
|
+
): t => {
|
|
43
|
+
allEntities: entities,
|
|
44
|
+
rawEvents: [],
|
|
58
45
|
entities: EntityTables.make(entities),
|
|
59
46
|
effects: Dict.make(),
|
|
60
|
-
|
|
47
|
+
rollback: None,
|
|
48
|
+
committedCheckpointId,
|
|
61
49
|
}
|
|
62
50
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
effects: Dict.mapValues(self.effects, table => {
|
|
67
|
-
idsToStore: table.idsToStore->Array.copy,
|
|
68
|
-
invalidationsCount: table.invalidationsCount,
|
|
69
|
-
dict: table.dict->Utils.Dict.shallowCopy,
|
|
70
|
-
effect: table.effect,
|
|
71
|
-
}),
|
|
72
|
-
rollbackTargetCheckpointId: self.rollbackTargetCheckpointId,
|
|
73
|
-
}
|
|
51
|
+
// Once the store holds this many entities across all tables, we drop them
|
|
52
|
+
// after a batch write so it doesn't grow unbounded on long running indexers.
|
|
53
|
+
let keepLatestChangesLimit = 50_000.
|
|
74
54
|
|
|
75
55
|
let getEffectInMemTable = (inMemoryStore: t, ~effect: Internal.effect) => {
|
|
76
56
|
let key = effect.name
|
|
@@ -91,13 +71,193 @@ let getEffectInMemTable = (inMemoryStore: t, ~effect: Internal.effect) => {
|
|
|
91
71
|
let getInMemTable = (
|
|
92
72
|
inMemoryStore: t,
|
|
93
73
|
~entityConfig: Internal.entityConfig,
|
|
94
|
-
): InMemoryTable.Entity.t
|
|
74
|
+
): InMemoryTable.Entity.t => {
|
|
95
75
|
inMemoryStore.entities->EntityTables.get(~entityName=entityConfig.name)
|
|
96
76
|
}
|
|
97
77
|
|
|
98
|
-
let isRollingBack = (inMemoryStore: t) => inMemoryStore.
|
|
78
|
+
let isRollingBack = (inMemoryStore: t) => inMemoryStore.rollback !== None
|
|
79
|
+
|
|
80
|
+
let writeBatch = async (
|
|
81
|
+
inMemoryStore: t,
|
|
82
|
+
~persistence: Persistence.t,
|
|
83
|
+
~batch,
|
|
84
|
+
~config,
|
|
85
|
+
~isInReorgThreshold,
|
|
86
|
+
) =>
|
|
87
|
+
switch persistence.storageStatus {
|
|
88
|
+
| Unknown
|
|
89
|
+
| Initializing(_) =>
|
|
90
|
+
JsError.throwWithMessage(`Failed to access the indexer storage. The Persistence layer is not initialized.`)
|
|
91
|
+
| Ready({cache}) =>
|
|
92
|
+
let committedCheckpointId = inMemoryStore.committedCheckpointId
|
|
93
|
+
// Decide before the keepMap below trims prevEntityChanges from changesCount,
|
|
94
|
+
// so the signal still reflects every change currently held in memory.
|
|
95
|
+
let keepLatestChanges = {
|
|
96
|
+
let totalChanges = ref(0.)
|
|
97
|
+
persistence.allEntities->Array.forEach(entityConfig => {
|
|
98
|
+
totalChanges :=
|
|
99
|
+
totalChanges.contents +. (inMemoryStore->getInMemTable(~entityConfig)).changesCount
|
|
100
|
+
})
|
|
101
|
+
totalChanges.contents < keepLatestChangesLimit
|
|
102
|
+
}
|
|
103
|
+
let updatedEntities = persistence.allEntities->Array.filterMap(entityConfig => {
|
|
104
|
+
let table = inMemoryStore->getInMemTable(~entityConfig)
|
|
105
|
+
|
|
106
|
+
// The reset below drops prevEntityChanges and we reuse the array as the
|
|
107
|
+
// write buffer here, so drop it from the count before appending to it.
|
|
108
|
+
table.changesCount = table.changesCount -. table.prevEntityChanges->Array.length->Int.toFloat
|
|
109
|
+
let changes = table.prevEntityChanges
|
|
110
|
+
table.latestEntityChangeById->Utils.Dict.forEach(change =>
|
|
111
|
+
if change->Change.getCheckpointId > committedCheckpointId {
|
|
112
|
+
changes->Array.push(change)
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
if changes->Utils.Array.isEmpty {
|
|
116
|
+
None
|
|
117
|
+
} else {
|
|
118
|
+
Some(({entityConfig, changes}: Persistence.updatedEntity))
|
|
119
|
+
}
|
|
120
|
+
})
|
|
121
|
+
await persistence.storage.writeBatch(
|
|
122
|
+
~batch,
|
|
123
|
+
~rawEvents=inMemoryStore.rawEvents,
|
|
124
|
+
~rollback=inMemoryStore.rollback,
|
|
125
|
+
~isInReorgThreshold,
|
|
126
|
+
~config,
|
|
127
|
+
~allEntities=persistence.allEntities,
|
|
128
|
+
~updatedEntities,
|
|
129
|
+
~updatedEffectsCache={
|
|
130
|
+
let acc = []
|
|
131
|
+
inMemoryStore.effects->Utils.Dict.forEach(inMemTable => {
|
|
132
|
+
let {idsToStore, dict, effect, invalidationsCount} = inMemTable
|
|
133
|
+
switch idsToStore {
|
|
134
|
+
| [] => ()
|
|
135
|
+
| ids =>
|
|
136
|
+
let items = ids->Array.map((id): Internal.effectCacheItem => {
|
|
137
|
+
id,
|
|
138
|
+
output: dict->Dict.getUnsafe(id),
|
|
139
|
+
})
|
|
140
|
+
let effectName = effect.name
|
|
141
|
+
let effectCacheRecord = switch cache->Utils.Dict.dangerouslyGetNonOption(effectName) {
|
|
142
|
+
| Some(c) => c
|
|
143
|
+
| None =>
|
|
144
|
+
let c: Persistence.effectCacheRecord = {effectName, count: 0}
|
|
145
|
+
cache->Dict.set(effectName, c)
|
|
146
|
+
c
|
|
147
|
+
}
|
|
148
|
+
let shouldInitialize = effectCacheRecord.count === 0
|
|
149
|
+
effectCacheRecord.count =
|
|
150
|
+
effectCacheRecord.count + items->Array.length - invalidationsCount
|
|
151
|
+
Prometheus.EffectCacheCount.set(~count=effectCacheRecord.count, ~effectName)
|
|
152
|
+
acc
|
|
153
|
+
->Array.push(({effect, items, shouldInitialize}: Persistence.updatedEffectCache))
|
|
154
|
+
->ignore
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
acc
|
|
158
|
+
},
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
inMemoryStore.rawEvents = []
|
|
162
|
+
inMemoryStore.effects = Dict.make()
|
|
163
|
+
inMemoryStore.rollback = None
|
|
164
|
+
inMemoryStore.committedCheckpointId = switch batch.checkpointIds->Utils.Array.last {
|
|
165
|
+
| Some(checkpointId) => checkpointId
|
|
166
|
+
| None => committedCheckpointId
|
|
167
|
+
}
|
|
168
|
+
if keepLatestChanges {
|
|
169
|
+
persistence.allEntities->Array.forEach(entityConfig => {
|
|
170
|
+
let table = inMemoryStore->getInMemTable(~entityConfig)
|
|
171
|
+
inMemoryStore.entities->Dict.set(
|
|
172
|
+
(entityConfig.name :> string),
|
|
173
|
+
table->InMemoryTable.Entity.resetButKeepLatestChanges,
|
|
174
|
+
)
|
|
175
|
+
})
|
|
176
|
+
} else {
|
|
177
|
+
// Over the limit: drop everything written in a batch and keep only the
|
|
178
|
+
// entities loaded from the db, so the next batch can still read them
|
|
179
|
+
// without hitting the database.
|
|
180
|
+
let loadedFromDbCount = ref(0.)
|
|
181
|
+
let resetTables = persistence.allEntities->Array.map(entityConfig => {
|
|
182
|
+
let resetTable =
|
|
183
|
+
inMemoryStore
|
|
184
|
+
->getInMemTable(~entityConfig)
|
|
185
|
+
->InMemoryTable.Entity.resetButKeepLoadedFromDbChanges
|
|
186
|
+
loadedFromDbCount := loadedFromDbCount.contents +. resetTable.changesCount
|
|
187
|
+
resetTable
|
|
188
|
+
})
|
|
189
|
+
// Even the loaded-from-db entities alone exceed the limit, so there's no
|
|
190
|
+
// point keeping them around - drop everything.
|
|
191
|
+
let dropEverything = loadedFromDbCount.contents >= keepLatestChangesLimit
|
|
192
|
+
persistence.allEntities->Array.forEachWithIndex((entityConfig, idx) => {
|
|
193
|
+
inMemoryStore.entities->Dict.set(
|
|
194
|
+
(entityConfig.name :> string),
|
|
195
|
+
dropEverything ? InMemoryTable.Entity.make() : resetTables->Array.getUnsafe(idx),
|
|
196
|
+
)
|
|
197
|
+
})
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let prepareRollbackDiff = async (
|
|
202
|
+
inMemoryStore: t,
|
|
203
|
+
~persistence: Persistence.t,
|
|
204
|
+
~rollbackTargetCheckpointId,
|
|
205
|
+
~rollbackDiffCheckpointId,
|
|
206
|
+
) => {
|
|
207
|
+
inMemoryStore.rawEvents = []
|
|
208
|
+
inMemoryStore.entities = EntityTables.make(inMemoryStore.allEntities)
|
|
209
|
+
inMemoryStore.effects = Dict.make()
|
|
210
|
+
inMemoryStore.rollback = Some({
|
|
211
|
+
targetCheckpointId: rollbackTargetCheckpointId,
|
|
212
|
+
diffCheckpointId: rollbackDiffCheckpointId,
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
let deletedEntities = Dict.make()
|
|
216
|
+
let setEntities = Dict.make()
|
|
217
|
+
|
|
218
|
+
let _ = await persistence.allEntities
|
|
219
|
+
->Array.map(async entityConfig => {
|
|
220
|
+
let entityTable = inMemoryStore->getInMemTable(~entityConfig)
|
|
221
|
+
|
|
222
|
+
let (removedIdsResult, restoredEntitiesResult) = await persistence.storage.getRollbackData(
|
|
223
|
+
~entityConfig,
|
|
224
|
+
~rollbackTargetCheckpointId,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
removedIdsResult->Array.forEach(data => {
|
|
228
|
+
deletedEntities->Utils.Dict.push(entityConfig.name, data["id"])
|
|
229
|
+
entityTable->InMemoryTable.Entity.set(
|
|
230
|
+
~committedCheckpointId=inMemoryStore.committedCheckpointId,
|
|
231
|
+
Delete({
|
|
232
|
+
entityId: data["id"],
|
|
233
|
+
checkpointId: rollbackDiffCheckpointId,
|
|
234
|
+
}),
|
|
235
|
+
)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
let restoredEntities = restoredEntitiesResult->S.parseOrThrow(entityConfig.rowsSchema)
|
|
239
|
+
|
|
240
|
+
restoredEntities->Array.forEach((entity: Internal.entity) => {
|
|
241
|
+
setEntities->Utils.Dict.push(entityConfig.name, entity.id)
|
|
242
|
+
entityTable->InMemoryTable.Entity.set(
|
|
243
|
+
~committedCheckpointId=inMemoryStore.committedCheckpointId,
|
|
244
|
+
Set({
|
|
245
|
+
entityId: entity.id,
|
|
246
|
+
checkpointId: rollbackDiffCheckpointId,
|
|
247
|
+
entity,
|
|
248
|
+
}),
|
|
249
|
+
)
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
->Promise.all
|
|
253
|
+
|
|
254
|
+
{
|
|
255
|
+
"deletedEntities": deletedEntities,
|
|
256
|
+
"setEntities": setEntities,
|
|
257
|
+
}
|
|
258
|
+
}
|
|
99
259
|
|
|
100
|
-
let setBatchDcs = (inMemoryStore: t, ~batch: Batch.t
|
|
260
|
+
let setBatchDcs = (inMemoryStore: t, ~batch: Batch.t) => {
|
|
101
261
|
let inMemTable =
|
|
102
262
|
inMemoryStore->getInMemTable(~entityConfig=InternalTable.EnvioAddresses.entityConfig)
|
|
103
263
|
|
|
@@ -126,12 +286,12 @@ let setBatchDcs = (inMemoryStore: t, ~batch: Batch.t, ~shouldSaveHistory) => {
|
|
|
126
286
|
}
|
|
127
287
|
|
|
128
288
|
inMemTable->InMemoryTable.Entity.set(
|
|
289
|
+
~committedCheckpointId=inMemoryStore.committedCheckpointId,
|
|
129
290
|
Set({
|
|
130
291
|
entityId: entity.id,
|
|
131
292
|
checkpointId,
|
|
132
293
|
entity: entity->InternalTable.EnvioAddresses.castToInternal,
|
|
133
294
|
}),
|
|
134
|
-
~shouldSaveHistory,
|
|
135
295
|
)
|
|
136
296
|
}
|
|
137
297
|
}
|