envio 3.15.0 → 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 +11 -2
- package/fuel.schema.json +10 -1
- package/index.d.ts +6 -0
- package/licenses/README.md +1 -1
- package/package.json +8 -8
- package/src/Batch.res.mjs +5 -5
- package/src/BatchProcessing.res +1 -0
- package/src/BatchProcessing.res.mjs +1 -0
- package/src/ChainState.res +48 -7
- package/src/ChainState.res.mjs +27 -11
- package/src/Clock.res +57 -0
- package/src/Clock.res.mjs +72 -0
- package/src/Config.res +10 -3
- package/src/Config.res.mjs +4 -2
- package/src/Core.res +4 -40
- 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/FetchState.res +157 -69
- package/src/FetchState.res.mjs +144 -49
- package/src/FinalizeBackfill.res +2 -4
- package/src/FinalizeBackfill.res.mjs +3 -4
- 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 -49
- package/src/Internal.res.mjs +1 -80
- package/src/LoadLayer.res +14 -8
- package/src/LoadLayer.res.mjs +8 -6
- package/src/Main.res.mjs +1 -1
- 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/SafeCheckpointTracking.res +4 -4
- package/src/SafeCheckpointTracking.res.mjs +1 -1
- 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 -948
- package/src/Storage.res.mjs +215 -707
- 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/Utils.res +0 -6
- package/src/Utils.res.mjs +0 -5
- 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 -174
- package/src/bindings/StorageClient.res.mjs +19 -8
- package/src/bindings/Vitest.res +0 -26
- package/src/bindings/Vitest.res.mjs +0 -3
- package/src/db/Table.res +24 -150
- package/src/db/Table.res.mjs +38 -272
- package/src/sources/ChainSources.res +2 -0
- package/src/sources/ChainSources.res.mjs +2 -2
- package/src/sources/EvmChain.res +3 -0
- package/src/sources/EvmChain.res.mjs +4 -2
- 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/HyperSyncSSE.res +4 -6
- package/src/sources/HyperSyncSSE.res.mjs +2 -2
- package/src/sources/RpcSource.res +10 -1
- package/src/sources/RpcSource.res.mjs +4 -3
- package/src/sources/Source.res +3 -0
- package/src/sources/SourceManager.res +84 -77
- package/src/sources/SourceManager.res.mjs +17 -20
- package/src/sources/SourceManager.resi +3 -4
- package/src/sources/StartBlockResolver.res +1 -0
- package/src/sources/StartBlockResolver.res.mjs +3 -2
- package/src/sources/SvmHyperSyncSource.res +2 -2
- package/src/sources/SvmHyperSyncSource.res.mjs +1 -1
- package/svm.schema.json +11 -2
- package/src/PgWriting.res +0 -36
- package/src/PgWriting.res.mjs +0 -34
- package/src/db/IndexDefinition.res +0 -47
- package/src/db/IndexDefinition.res.mjs +0 -67
package/src/Storage.res.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as Fs from "fs";
|
|
4
3
|
import * as Env from "./Env.res.mjs";
|
|
5
4
|
import * as Sql from "./db/Sql.res.mjs";
|
|
6
5
|
import * as Path from "path";
|
|
@@ -12,16 +11,16 @@ import * as Logger from "./Logger.res.mjs";
|
|
|
12
11
|
import * as Schema from "./db/Schema.res.mjs";
|
|
13
12
|
import * as ChainId from "./ChainId.res.mjs";
|
|
14
13
|
import * as Staging from "./Staging.res.mjs";
|
|
15
|
-
import * as EntityId from "./EntityId.res.mjs";
|
|
16
14
|
import * as Frontier from "./db/Frontier.res.mjs";
|
|
17
15
|
import * as Internal from "./Internal.res.mjs";
|
|
18
|
-
import * as PgWriting from "./PgWriting.res.mjs";
|
|
19
16
|
import * as ChainState from "./ChainState.res.mjs";
|
|
20
17
|
import * as ClickHouse from "./bindings/ClickHouse.res.mjs";
|
|
21
18
|
import * as LoadFilter from "./db/LoadFilter.res.mjs";
|
|
19
|
+
import * as StagedRows from "./StagedRows.res.mjs";
|
|
22
20
|
import * as AddressRows from "./AddressRows.res.mjs";
|
|
23
21
|
import * as Performance from "./bindings/Performance.res.mjs";
|
|
24
22
|
import * as Persistence from "./Persistence.res.mjs";
|
|
23
|
+
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
|
|
25
24
|
import * as EntityFilter from "./db/EntityFilter.res.mjs";
|
|
26
25
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
27
26
|
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
@@ -30,21 +29,12 @@ import * as HistoryPolicy from "./HistoryPolicy.res.mjs";
|
|
|
30
29
|
import * as InternalTable from "./db/InternalTable.res.mjs";
|
|
31
30
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
32
31
|
import * as StorageClient from "./bindings/StorageClient.res.mjs";
|
|
33
|
-
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
|
|
34
32
|
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
|
|
35
33
|
import * as ContractMapping from "./ContractMapping.res.mjs";
|
|
36
|
-
import * as IndexDefinition from "./db/IndexDefinition.res.mjs";
|
|
37
34
|
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
|
|
38
35
|
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
|
|
39
|
-
import * as CheckpointSequence from "./db/CheckpointSequence.res.mjs";
|
|
40
36
|
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
41
37
|
|
|
42
|
-
function formatSeconds(seconds) {
|
|
43
|
-
return (Math.round(seconds * 100) / 100).toString();
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let slowOnLargeDatabaseNotice = "This can take a long time on a large database.";
|
|
47
|
-
|
|
48
38
|
function storeName(store) {
|
|
49
39
|
if (store === "Postgres") {
|
|
50
40
|
return "postgres";
|
|
@@ -55,83 +45,11 @@ function storeName(store) {
|
|
|
55
45
|
|
|
56
46
|
let postgresName = "postgres";
|
|
57
47
|
|
|
58
|
-
function
|
|
59
|
-
switch (event.kind) {
|
|
60
|
-
case "Invalid" :
|
|
61
|
-
return Logger.warn(logger, `Ignoring invalid PostgreSQL indexes in schema "` + pgSchema + `". They can't serve queries, so the indexer builds its own alongside them.`, {
|
|
62
|
-
storage: postgresName,
|
|
63
|
-
indexes: event.names
|
|
64
|
-
});
|
|
65
|
-
case "Planned" :
|
|
66
|
-
let rebuilt = event.rebuilt;
|
|
67
|
-
let missing = event.missing;
|
|
68
|
-
if (Utils.$$Array.notEmpty(rebuilt)) {
|
|
69
|
-
Logger.warn(logger, `PostgreSQL reports ` + rebuilt.length.toString() + ` of the indexer's own indexes as invalid, so they can't serve queries. Rebuilding them.`, {
|
|
70
|
-
storage: postgresName,
|
|
71
|
-
indexes: rebuilt
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
if (missing.length !== 0) {
|
|
75
|
-
return Logger.info(logger, `Creating the ` + missing.length.toString() + ` remaining schema indexes before the indexer reports ready. Writes are paused until they are committed. ` + slowOnLargeDatabaseNotice, {
|
|
76
|
-
storage: postgresName,
|
|
77
|
-
indexes: missing
|
|
78
|
-
});
|
|
79
|
-
} else {
|
|
80
|
-
return Logger.info(logger, `All ` + event.declared.toString() + ` schema indexes are already in place. Marking the indexer ready.`, {
|
|
81
|
-
storage: postgresName
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
case "Building" :
|
|
85
|
-
if (event.purpose === "Query") {
|
|
86
|
-
return Logger.info(logger, (
|
|
87
|
-
event.isRebuild ? "Rebuilding unusable index" : "Creating index"
|
|
88
|
-
) + ` "` + event.name + `" to serve a getWhere query on "` + event.tableName + `". Writes to the table are paused until it completes. ` + slowOnLargeDatabaseNotice, {
|
|
89
|
-
storage: postgresName
|
|
90
|
-
});
|
|
91
|
-
} else {
|
|
92
|
-
return Logger.info(logger, (
|
|
93
|
-
event.isRebuild ? "Rebuilding unusable index" : "Creating missing index"
|
|
94
|
-
) + ` "` + event.name + `" the schema promises but the database no longer has. Writes to the table are paused until it completes. ` + slowOnLargeDatabaseNotice, {
|
|
95
|
-
storage: postgresName
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
case "Built" :
|
|
99
|
-
let tmp;
|
|
100
|
-
tmp = event.purpose === "Query" ? " Resuming indexing." : "";
|
|
101
|
-
return Logger.info(logger, `Index "` + event.name + `" is ready after ` + formatSeconds(event.seconds) + `s.` + tmp, {
|
|
102
|
-
storage: postgresName
|
|
103
|
-
});
|
|
104
|
-
case "Failed" :
|
|
105
|
-
if (event.purpose === "Query") {
|
|
106
|
-
return Logger.warn(logger, `Failed to create an index on "` + event.tableName + `"(` + event.columns.map(column => `"` + column + `"`).join(", ") + `) for a getWhere query. The query runs without it.`, {
|
|
107
|
-
storage: postgresName,
|
|
108
|
-
err: event.error,
|
|
109
|
-
code: event.code
|
|
110
|
-
});
|
|
111
|
-
} else {
|
|
112
|
-
return Logger.warn(logger, `Failed to restore the schema index "` + event.name + `". Queries relying on it run unindexed until the next restart.`, {
|
|
113
|
-
storage: postgresName,
|
|
114
|
-
err: event.error,
|
|
115
|
-
code: event.code
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
case "ResyncFailed" :
|
|
119
|
-
return Logger.trace(logger, `Could not re-read the index "` + event.name + `" after a failed build. The next attempt reads it again.`, {
|
|
120
|
-
storage: postgresName,
|
|
121
|
-
err: event.error,
|
|
122
|
-
code: event.code
|
|
123
|
-
});
|
|
124
|
-
case "Committed" :
|
|
125
|
-
return Logger.info(logger, `Committed ` + event.count.toString() + ` schema indexes and the ready timestamp in ` + formatSeconds(event.seconds) + `s.`, {
|
|
126
|
-
storage: postgresName
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt) {
|
|
48
|
+
function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt, isolatedOpt) {
|
|
132
49
|
let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
|
|
133
50
|
let isHasuraEnabled = isHasuraEnabledOpt !== undefined ? isHasuraEnabledOpt : false;
|
|
134
51
|
let maxConnections = maxConnectionsOpt !== undefined ? maxConnectionsOpt : Env.Db.maxConnections;
|
|
52
|
+
let isolated = isolatedOpt !== undefined ? isolatedOpt : false;
|
|
135
53
|
return {
|
|
136
54
|
host: Env.Db.host,
|
|
137
55
|
port: Env.Db.port,
|
|
@@ -142,288 +60,23 @@ function postgresOptions(pgSchemaOpt, isHasuraEnabledOpt, maxConnectionsOpt) {
|
|
|
142
60
|
maxConnections: maxConnections,
|
|
143
61
|
applicationName: `hyperindex/` + Utils.EnvioPackage.value.version,
|
|
144
62
|
pgSchema: pgSchema,
|
|
145
|
-
isHasuraEnabled: isHasuraEnabled
|
|
63
|
+
isHasuraEnabled: isHasuraEnabled,
|
|
64
|
+
isolated: isolated
|
|
146
65
|
};
|
|
147
66
|
}
|
|
148
67
|
|
|
149
|
-
function makeClient(logger,
|
|
150
|
-
let chainIdMode = chainIdModeOpt !== undefined ? chainIdModeOpt : "int32";
|
|
68
|
+
function makeClient(logger, config, postgres, clickhouse) {
|
|
151
69
|
return StorageClient.make({
|
|
152
|
-
chainIdMode: chainIdMode,
|
|
153
70
|
postgres: postgres,
|
|
154
71
|
clickhouse: clickhouse
|
|
155
|
-
},
|
|
156
|
-
storage: "clickhouse"
|
|
157
|
-
}));
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function partitionTableName(entityConfig, chainId) {
|
|
161
|
-
let chainIdStr = ChainId.toString(chainId);
|
|
162
|
-
return Table.fitPgTableName(entityConfig.table.tableName + `$` + chainIdStr, `$` + entityConfig.index.toString() + `$` + chainIdStr);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function indexTableNames(entityConfig, partitionChainIds) {
|
|
166
|
-
let match = Table.getChainIdField(entityConfig.table);
|
|
167
|
-
if (match !== undefined) {
|
|
168
|
-
if (partitionChainIds !== undefined) {
|
|
169
|
-
return partitionChainIds.map(chainId => partitionTableName(entityConfig, chainId));
|
|
170
|
-
} else {
|
|
171
|
-
return [entityConfig.table.tableName];
|
|
172
|
-
}
|
|
173
|
-
} else {
|
|
174
|
-
return [entityConfig.table.tableName];
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
function getSchemaIndexes(entities, partitionChainIds) {
|
|
179
|
-
let derivedSchema = Schema.make(entities.map(e => e.table));
|
|
180
|
-
let all = [];
|
|
181
|
-
entities.forEach(param => {
|
|
182
|
-
let table = param.table;
|
|
183
|
-
Table.getSingleIndexes(table).forEach(column => {
|
|
184
|
-
all.push(IndexDefinition.single(table.tableName, column));
|
|
185
|
-
});
|
|
186
|
-
Table.getCompositeIndexes(table).forEach(indexFields => {
|
|
187
|
-
all.push(IndexDefinition.fromIndexFields(table.tableName, indexFields));
|
|
188
|
-
});
|
|
189
|
-
});
|
|
190
|
-
entities.forEach(param => {
|
|
191
|
-
Table.getDerivedFromFields(param.table).forEach(derivedFromField => {
|
|
192
|
-
let column = Utils.unwrapResultExn(Schema.getDerivedFromPgFieldName(derivedSchema, derivedFromField));
|
|
193
|
-
all.push(IndexDefinition.single(derivedFromField.derivedFromEntity, column));
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
let seen = new Set();
|
|
197
|
-
let entityByTableName = {};
|
|
198
|
-
entities.forEach(entityConfig => {
|
|
199
|
-
entityByTableName[entityConfig.table.tableName] = entityConfig;
|
|
200
|
-
});
|
|
201
|
-
return all.filter(definition => {
|
|
202
|
-
let identity = IndexDefinition.describe(definition);
|
|
203
|
-
if (seen.has(identity)) {
|
|
204
|
-
return false;
|
|
205
|
-
} else {
|
|
206
|
-
seen.add(identity);
|
|
207
|
-
return true;
|
|
208
|
-
}
|
|
209
|
-
}).flatMap(definition => indexTableNames(Stdlib_Option.getOrThrow(entityByTableName[definition.tableName], undefined), partitionChainIds).map(tableName => ({
|
|
210
|
-
tableName: tableName,
|
|
211
|
-
columns: definition.columns,
|
|
212
|
-
method: definition.method
|
|
213
|
-
})));
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function pgColumnInput(field) {
|
|
217
|
-
let match = Table.pgFieldTypeParts(field.fieldType);
|
|
218
|
-
return {
|
|
219
|
-
name: Table.getPgDbFieldName(field),
|
|
220
|
-
fieldType: match[0],
|
|
221
|
-
isArray: field.isArray,
|
|
222
|
-
isNullable: field.isNullable,
|
|
223
|
-
isPrimaryKey: field.isPrimaryKey,
|
|
224
|
-
defaultValue: field.defaultValue,
|
|
225
|
-
precision: match[1],
|
|
226
|
-
scale: match[2],
|
|
227
|
-
enumName: match[3]
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
let rowSchemaCache = new WeakMap();
|
|
232
|
-
|
|
233
|
-
function getRowSchema(entityConfig) {
|
|
234
|
-
let cached = rowSchemaCache.get(entityConfig);
|
|
235
|
-
if (cached !== undefined) {
|
|
236
|
-
return Primitive_option.valFromOption(cached);
|
|
237
|
-
}
|
|
238
|
-
let chainIdField = Table.getChainIdField(entityConfig.table);
|
|
239
|
-
let schema = chainIdField !== undefined ? S$RescriptSchema.schema(s => {
|
|
240
|
-
let dict = {};
|
|
241
|
-
let match = entityConfig.schema.t;
|
|
242
|
-
let exit = 0;
|
|
243
|
-
if (typeof match !== "object" || match.TAG !== "object") {
|
|
244
|
-
exit = 1;
|
|
245
|
-
} else {
|
|
246
|
-
match.items.forEach(param => {
|
|
247
|
-
dict[param.location] = s.m(param.schema);
|
|
248
|
-
});
|
|
249
|
-
}
|
|
250
|
-
if (exit === 1) {
|
|
251
|
-
Stdlib_JsError.throwWithMessage(`Unexpected non-object schema for entity "` + entityConfig.name + `".`);
|
|
252
|
-
}
|
|
253
|
-
dict[chainIdField.fieldName] = s.m(ChainId.schema);
|
|
254
|
-
return dict;
|
|
255
|
-
}) : entityConfig.schema;
|
|
256
|
-
rowSchemaCache.set(entityConfig, schema);
|
|
257
|
-
return schema;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
function register(client, table, itemSchema, postgresOpt, clickhouse, appendOnlyOpt, history) {
|
|
261
|
-
let postgres = postgresOpt !== undefined ? postgresOpt : true;
|
|
262
|
-
let appendOnly = appendOnlyOpt !== undefined ? appendOnlyOpt : false;
|
|
263
|
-
let fields = Table.schemaOrderedFields(table, itemSchema);
|
|
264
|
-
let chainIdField = Table.getChainIdField(table);
|
|
265
|
-
let registered = client.registerTable({
|
|
266
|
-
name: table.tableName,
|
|
267
|
-
postgres: postgres ? ({
|
|
268
|
-
columns: Table.getFields(table).map(pgColumnInput),
|
|
269
|
-
writeColumns: fields.map(Table.getPgDbFieldName),
|
|
270
|
-
partitionByColumn: Stdlib_Option.map(chainIdField, Table.getPgDbFieldName),
|
|
271
|
-
appendOnly: appendOnly,
|
|
272
|
-
historyTable: history,
|
|
273
|
-
chainIdColumn: history !== undefined ? Stdlib_Option.map(chainIdField, Table.getPgDbFieldName) : undefined
|
|
274
|
-
}) : undefined,
|
|
275
|
-
clickhouse: clickhouse
|
|
276
|
-
});
|
|
277
|
-
return {
|
|
278
|
-
handle: registered.handle,
|
|
279
|
-
home: registered.home,
|
|
280
|
-
table: table,
|
|
281
|
-
fields: fields,
|
|
282
|
-
postgres: Stdlib_Option.map(registered.postgres, param => ({
|
|
283
|
-
staged: Stdlib_Option.map(param.kinds, kinds => PgWriting.columns(fields, kinds.map(Staging.kindOfOrdinal)))
|
|
284
|
-
})),
|
|
285
|
-
clickhouse: Stdlib_Option.map(registered.clickhouse, __x => ClickHouse.makeTable(registered.handle, Stdlib_Option.getOr(history, table.tableName), __x))
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
function registerEntity(client, entityConfig) {
|
|
290
|
-
return register(client, entityConfig.table, getRowSchema(entityConfig), entityConfig.storage.postgres, entityConfig.storage.clickhouse ? ClickHouse.entityTable(entityConfig) : undefined, undefined, EntityHistory.historyTableName(entityConfig.name, entityConfig.index));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function renderDocument(value, isNullable) {
|
|
294
|
-
if (value == null) {
|
|
295
|
-
if (value === null && !isNullable) {
|
|
296
|
-
return "null";
|
|
297
|
-
} else {
|
|
298
|
-
return value;
|
|
299
|
-
}
|
|
300
|
-
} else {
|
|
301
|
-
return Sql.stringifyDocument(value);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
let arenaConverters = new WeakMap();
|
|
306
|
-
|
|
307
|
-
let cellConverters = new WeakMap();
|
|
308
|
-
|
|
309
|
-
function converterOf(registered, forArena, itemSchema) {
|
|
310
|
-
let converters = forArena ? arenaConverters : cellConverters;
|
|
311
|
-
let convert = converters.get(itemSchema);
|
|
312
|
-
if (convert !== undefined) {
|
|
313
|
-
return convert;
|
|
314
|
-
}
|
|
315
|
-
let documents = Stdlib_Array.filterMapWithIndex(registered.fields, (field, index) => {
|
|
316
|
-
if (field.fieldType === "Json") {
|
|
317
|
-
return [
|
|
318
|
-
index,
|
|
319
|
-
field
|
|
320
|
-
];
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
let schema = forArena ? Table.toDbSchema(registered.table, itemSchema) : itemSchema;
|
|
324
|
-
let convert$1 = S$RescriptSchema.compile(S$RescriptSchema.preprocess(S$RescriptSchema.unnest(schema), param => ({
|
|
325
|
-
s: columns => {
|
|
326
|
-
documents.forEach(param => {
|
|
327
|
-
let field = param[1];
|
|
328
|
-
let values = columns[param[0]];
|
|
329
|
-
for (let row = 0, row_finish = values.length; row < row_finish; ++row) {
|
|
330
|
-
let value = values[row];
|
|
331
|
-
values[row] = !(value == null) && field.isArray ? value.map(document => renderDocument(document, true)) : renderDocument(value, field.isNullable);
|
|
332
|
-
}
|
|
333
|
-
});
|
|
334
|
-
return columns;
|
|
335
|
-
}
|
|
336
|
-
})), "Output", "Input", "Sync", false);
|
|
337
|
-
converters.set(itemSchema, convert$1);
|
|
338
|
-
return convert$1;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
function refuseNonFinite(values, fields) {
|
|
342
|
-
fields.forEach((field, index) => {
|
|
343
|
-
if (field.fieldType !== "Number") {
|
|
344
|
-
return;
|
|
345
|
-
}
|
|
346
|
-
let column = Table.getPgDbFieldName(field);
|
|
347
|
-
let check = value => {
|
|
348
|
-
if (value == null) {
|
|
349
|
-
return;
|
|
350
|
-
}
|
|
351
|
-
Staging.finiteOrThrow(value, column);
|
|
352
|
-
};
|
|
353
|
-
values[index].forEach(value => {
|
|
354
|
-
if (!field.isArray) {
|
|
355
|
-
return check(value);
|
|
356
|
-
}
|
|
357
|
-
if (value == null) {
|
|
358
|
-
return;
|
|
359
|
-
}
|
|
360
|
-
value.forEach(check);
|
|
361
|
-
});
|
|
362
|
-
});
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function stagedFor(staged, store, stage) {
|
|
366
|
-
let started = Performance.now();
|
|
367
|
-
let result = stage();
|
|
368
|
-
let name = storeName(store);
|
|
369
|
-
staged.seconds[name] = Stdlib_Option.getOr(staged.seconds[name], 0) + Performance.secondsSince(started);
|
|
370
|
-
return result;
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function rowsOrThrow(client, registered, itemSchema, items, staged) {
|
|
374
|
-
return stagedFor(staged, "Postgres", () => {
|
|
375
|
-
let rows = items.length;
|
|
376
|
-
try {
|
|
377
|
-
let match = registered.postgres;
|
|
378
|
-
if (match === undefined) {
|
|
379
|
-
return Stdlib_JsError.throwWithMessage(`Postgres doesn't keep "` + registered.table.tableName + `"`);
|
|
380
|
-
}
|
|
381
|
-
let columns = match.staged;
|
|
382
|
-
if (columns !== undefined) {
|
|
383
|
-
let values = converterOf(registered, true, itemSchema)(items);
|
|
384
|
-
let handle = PgWriting.stage(StorageClient.arena(client, "Postgres"), registered.handle, columns, values, rows);
|
|
385
|
-
staged.handles.push(handle);
|
|
386
|
-
return {
|
|
387
|
-
staged: handle,
|
|
388
|
-
rows: rows
|
|
389
|
-
};
|
|
390
|
-
}
|
|
391
|
-
let values$1 = converterOf(registered, false, itemSchema)(items);
|
|
392
|
-
refuseNonFinite(values$1, registered.fields);
|
|
393
|
-
return {
|
|
394
|
-
cells: Sql.params(values$1.flat(1)),
|
|
395
|
-
rows: rows
|
|
396
|
-
};
|
|
397
|
-
} catch (raw_exn) {
|
|
398
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
399
|
-
if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
|
|
400
|
-
throw {
|
|
401
|
-
RE_EXN_ID: Persistence.StorageError,
|
|
402
|
-
message: `Failed to convert items for table "` + registered.table.tableName + `"`,
|
|
403
|
-
reason: exn,
|
|
404
|
-
Error: new Error()
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
throw {
|
|
408
|
-
RE_EXN_ID: Persistence.StorageError,
|
|
409
|
-
message: `Failed to insert items into table "` + registered.table.tableName + `"`,
|
|
410
|
-
reason: Utils.prettifyExn(exn),
|
|
411
|
-
Error: new Error()
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
function renderIds(table, ids) {
|
|
418
|
-
return Table.encodeIdsToJson(table, ids).map(Sql.render);
|
|
72
|
+
}, Stdlib_Option.map(config, config => JSON.stringify(config.envioInfo)), logger);
|
|
419
73
|
}
|
|
420
74
|
|
|
421
75
|
function bounds(param) {
|
|
422
|
-
let
|
|
76
|
+
let byChain = param.byChain;
|
|
423
77
|
return {
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
checkpointIds: match[1]
|
|
78
|
+
chainIds: Frontier.chainIds(byChain),
|
|
79
|
+
checkpointIds: Frontier.chainIds(byChain).map(chainId => Frontier.get(byChain, chainId))
|
|
427
80
|
};
|
|
428
81
|
}
|
|
429
82
|
|
|
@@ -474,149 +127,99 @@ function storageErrorOf(exn) {
|
|
|
474
127
|
}
|
|
475
128
|
}
|
|
476
129
|
|
|
477
|
-
let rollbackRowStateSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.object(s => [
|
|
478
|
-
s.f(Table.idFieldName, Table.getIdSchema(table)),
|
|
479
|
-
s.f(EntityHistory.changeFieldName, EntityHistory.RowAction.schema)
|
|
480
|
-
]));
|
|
481
|
-
|
|
482
130
|
let rollbackChainIdSchema = Utils.$$WeakMap.memoize(table => Stdlib_Option.map(Table.getChainIdField(table), field => S$RescriptSchema.object(s => s.f(Table.getPgDbFieldName(field), ChainId.schema))));
|
|
483
131
|
|
|
484
132
|
let rollbackRemovedIdSchema = Utils.$$WeakMap.memoize(table => S$RescriptSchema.object(s => s.f(Table.idFieldName, Table.getIdSchema(table))));
|
|
485
133
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
let
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
134
|
+
let changeColumns = [
|
|
135
|
+
{
|
|
136
|
+
name: EntityHistory.checkpointIdFieldName,
|
|
137
|
+
kind: 2,
|
|
138
|
+
isNullable: false
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
name: EntityHistory.changeFieldName,
|
|
142
|
+
kind: 0,
|
|
143
|
+
isNullable: false
|
|
144
|
+
}
|
|
145
|
+
];
|
|
146
|
+
|
|
147
|
+
function stageOrThrow(arena, columns, rows, fill) {
|
|
148
|
+
let stage = Staging.begin(arena, rows, columns);
|
|
149
|
+
try {
|
|
150
|
+
fill(stage);
|
|
151
|
+
return Staging.commit(stage);
|
|
152
|
+
} catch (exn) {
|
|
153
|
+
Staging.abort(stage);
|
|
154
|
+
throw exn;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function stageRowsOrThrow(arena, writers, items) {
|
|
159
|
+
return stageOrThrow(arena, StagedRows.columnsOf(writers), items.length, stage => {
|
|
160
|
+
items.forEach((item, row) => {
|
|
161
|
+
writers.forEach((param, column) => StagedRows.writeField(stage, writers, column, row, item[param.field.name]));
|
|
162
|
+
});
|
|
505
163
|
});
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
table: entityTable(entityConfig).handle,
|
|
524
|
-
chainId: chainId,
|
|
525
|
-
name: partitionTableName(entityConfig, chainId)
|
|
526
|
-
})));
|
|
527
|
-
let holding = () => client.holding();
|
|
528
|
-
let scanCacheDir = async () => {
|
|
529
|
-
let topEntries;
|
|
530
|
-
try {
|
|
531
|
-
topEntries = await Fs.promises.readdir(cacheDirPath);
|
|
532
|
-
} catch (exn) {
|
|
533
|
-
topEntries = [];
|
|
534
|
-
}
|
|
535
|
-
let result = [];
|
|
536
|
-
await Promise.all(topEntries.map(async entry => {
|
|
537
|
-
let entryPath = Path.join(cacheDirPath, entry);
|
|
538
|
-
let isDir = (await Fs.promises.stat(entryPath)).isDirectory();
|
|
539
|
-
if (isDir) {
|
|
540
|
-
let subEntries = await Fs.promises.readdir(entryPath);
|
|
541
|
-
let tsvs = subEntries.filter(sub => sub.endsWith(".tsv"));
|
|
542
|
-
let chainId = Internal.EffectCache.parseChainId(entry);
|
|
543
|
-
if (chainId === undefined) {
|
|
544
|
-
if (Utils.$$Array.notEmpty(tsvs)) {
|
|
545
|
-
return Stdlib_JsError.throwWithMessage(`Invalid effect cache directory ".envio/cache/` + entry + `". Chain cache directories must be named by a numeric chain id (e.g. "1"). Found cache files: ` + tsvs.join(", ") + `.`);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function stageChangesOrThrow(arena, writers, changes) {
|
|
167
|
+
let fields = writers.length;
|
|
168
|
+
return stageOrThrow(arena, StagedRows.columnsOf(writers).concat(changeColumns), changes.length, stage => {
|
|
169
|
+
changes.forEach((change, row) => {
|
|
170
|
+
let entityId = change.entityId;
|
|
171
|
+
if (change.type === "SET") {
|
|
172
|
+
let entity = change.entity;
|
|
173
|
+
writers.forEach((param, column) => {
|
|
174
|
+
let field = param.field;
|
|
175
|
+
StagedRows.writeField(stage, writers, column, row, field.name === Table.idFieldName ? entityId : entity[field.name]);
|
|
176
|
+
});
|
|
177
|
+
} else {
|
|
178
|
+
writers.forEach((param, column) => {
|
|
179
|
+
if (param.field.name === Table.idFieldName) {
|
|
180
|
+
return StagedRows.writeField(stage, writers, column, row, entityId);
|
|
546
181
|
} else {
|
|
547
|
-
return;
|
|
182
|
+
return Staging.writeDeletedValue(stage, column, row, undefined);
|
|
548
183
|
}
|
|
549
|
-
}
|
|
550
|
-
let chainId$1 = Primitive_option.valFromOption(chainId);
|
|
551
|
-
tsvs.forEach(sub => {
|
|
552
|
-
let effectName = sub.slice(0, -4);
|
|
553
|
-
let table = Internal.makeCacheTable(effectName, chainId$1);
|
|
554
|
-
result.push([
|
|
555
|
-
table,
|
|
556
|
-
Path.join(entryPath, sub)
|
|
557
|
-
]);
|
|
558
184
|
});
|
|
559
|
-
return;
|
|
560
185
|
}
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
};
|
|
186
|
+
Staging.writeValue(stage, fields, row, change.checkpointId);
|
|
187
|
+
let tmp;
|
|
188
|
+
tmp = change.type === "SET" ? 0 : 1;
|
|
189
|
+
Staging.writeValue(stage, fields + 1 | 0, row, tmp);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function make(client, cacheDir, ecosystem, onInitialize) {
|
|
195
|
+
let cacheDirPath = cacheDir !== undefined ? Primitive_option.valFromOption(cacheDir) : Path.resolve(".envio", "cache");
|
|
196
|
+
let layouts = client.layouts();
|
|
197
|
+
let entityIndexByName = {};
|
|
198
|
+
layouts.entities.forEach((layout, index) => {
|
|
199
|
+
entityIndexByName[layout.name] = index;
|
|
200
|
+
});
|
|
201
|
+
let entityWriters = layouts.entities.map(layout => StagedRows.writersOf(layout.fields));
|
|
202
|
+
let rawEventWriters = StagedRows.writersOf(layouts.rawEvents);
|
|
203
|
+
let effectCacheWriters = StagedRows.writersOf(layouts.effectCache);
|
|
204
|
+
let holding = () => client.holding();
|
|
573
205
|
let restoreEffectCache = async withUpload => {
|
|
574
|
-
if (withUpload) {
|
|
575
|
-
let entries = await scanCacheDir();
|
|
576
|
-
if (entries.length !== 0) {
|
|
577
|
-
try {
|
|
578
|
-
await client.uploadEffectCache(entries.map(param => ({
|
|
579
|
-
table: ownTable(param[0], Internal.cacheItemSchema, undefined).handle,
|
|
580
|
-
path: param[1]
|
|
581
|
-
})));
|
|
582
|
-
Logger.info(logger, "Successfully uploaded cache.", undefined);
|
|
583
|
-
} catch (raw_exn) {
|
|
584
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
585
|
-
Logger.errorWithExn(logger, "Failed to upload cache, continuing without it.", exn);
|
|
586
|
-
}
|
|
587
|
-
} else {
|
|
588
|
-
Logger.info(logger, "No saved effect cache to load from .envio/cache.", undefined);
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
206
|
let cache = {};
|
|
592
|
-
(await client.
|
|
207
|
+
(await client.restoreEffectCache(cacheDirPath, withUpload)).forEach(param => {
|
|
593
208
|
let tableName = param.tableName;
|
|
594
|
-
let
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
};
|
|
602
|
-
return;
|
|
603
|
-
}
|
|
209
|
+
let chainId = param.chainId;
|
|
210
|
+
cache[tableName] = {
|
|
211
|
+
effectName: param.effectName,
|
|
212
|
+
scope: chainId !== undefined ? Primitive_option.valFromOption(chainId) : "crossChain",
|
|
213
|
+
tableName: tableName,
|
|
214
|
+
count: param.rows
|
|
215
|
+
};
|
|
604
216
|
});
|
|
605
217
|
return cache;
|
|
606
218
|
};
|
|
607
|
-
let initialize = async (chainConfigsOpt,
|
|
219
|
+
let initialize = async (chainConfigsOpt, contractMapping, envioInfo) => {
|
|
608
220
|
let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
|
|
609
|
-
let entities = entitiesOpt !== undefined ? entitiesOpt : [];
|
|
610
|
-
let enums = enumsOpt !== undefined ? enumsOpt : [];
|
|
611
221
|
let rowsByChain = chainConfigs.map(chainConfig => ChainState.configStorageRows(chainConfig, ecosystem, contractMapping));
|
|
612
222
|
await client.initialize({
|
|
613
|
-
sequence: CheckpointSequence.fromEntities(entities),
|
|
614
|
-
tables: [rawEventsTable().handle].concat(entities.map(entityConfig => entityTable(entityConfig).handle)),
|
|
615
|
-
partitions: partitions(entities, chainConfigs.map(chainConfig => chainConfig.id)),
|
|
616
|
-
enums: enums.map(enumConfig => ({
|
|
617
|
-
name: enumConfig.name,
|
|
618
|
-
variants: enumConfig.variants
|
|
619
|
-
})),
|
|
620
223
|
chains: chainConfigs.map(toChainConfig),
|
|
621
224
|
envioInfo: JSON.stringify(envioInfo),
|
|
622
225
|
contractNames: ContractMapping.names(contractMapping),
|
|
@@ -649,9 +252,11 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
649
252
|
};
|
|
650
253
|
let loadOrThrow = async (filter, table) => {
|
|
651
254
|
let conditions = LoadFilter.make(filter, table, field => field.pgDbFieldName);
|
|
255
|
+
let entity = entityIndexByName[table.tableName];
|
|
256
|
+
let loading = entity !== undefined ? StorageClient.loadWhere(client, entity, conditions) : StorageClient.loadEffectCache(client, table.tableName, conditions);
|
|
652
257
|
let rows;
|
|
653
258
|
try {
|
|
654
|
-
rows = await
|
|
259
|
+
rows = await loading;
|
|
655
260
|
} catch (raw_exn) {
|
|
656
261
|
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
657
262
|
throw {
|
|
@@ -691,50 +296,17 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
691
296
|
}));
|
|
692
297
|
return columns;
|
|
693
298
|
};
|
|
694
|
-
let partitionChainIds = chainIds => {
|
|
695
|
-
if (isolated) {
|
|
696
|
-
return Primitive_option.some(chainIds);
|
|
697
|
-
}
|
|
698
|
-
};
|
|
699
|
-
let queryTableName = (entityConfig, scope) => {
|
|
700
|
-
if (scope === "crossChain") {
|
|
701
|
-
return entityConfig.table.tableName;
|
|
702
|
-
} else {
|
|
703
|
-
return indexTableNames(entityConfig, partitionChainIds([scope]))[0];
|
|
704
|
-
}
|
|
705
|
-
};
|
|
706
299
|
let ensureQueryIndexes = async (entityConfig, scope, filters) => {
|
|
707
|
-
let building = client.ensureQueryIndexes(
|
|
300
|
+
let building = client.ensureQueryIndexes(entityConfig.index, Stdlib_Null.fromOption(Internal.chainScopeChainId(scope)), filterColumns(entityConfig.table, filters));
|
|
708
301
|
if (building === null) {
|
|
709
302
|
return;
|
|
710
303
|
} else {
|
|
711
304
|
return await building;
|
|
712
305
|
}
|
|
713
306
|
};
|
|
714
|
-
let
|
|
715
|
-
let
|
|
716
|
-
let
|
|
717
|
-
let dumpEffectCache = async () => {
|
|
718
|
-
try {
|
|
719
|
-
let tables = (await client.effectCacheTables()).filter(param => param.rows > 0);
|
|
720
|
-
if (Utils.$$Array.notEmpty(tables)) {
|
|
721
|
-
Logger.info(logger, `Dumping cache: ` + tables.map(param => param.tableName + " (" + param.rows.toString() + " rows)").join(", "), undefined);
|
|
722
|
-
await client.dumpEffectCache(Stdlib_Array.filterMap(tables, param => {
|
|
723
|
-
let tableName = param.tableName;
|
|
724
|
-
return Stdlib_Option.map(Internal.EffectCache.fromTableName(tableName), param => ({
|
|
725
|
-
tableName: tableName,
|
|
726
|
-
path: Path.join(cacheDirPath, Internal.EffectCache.toCachePath(param[0], param[1]))
|
|
727
|
-
}));
|
|
728
|
-
}));
|
|
729
|
-
return Logger.info(logger, `Successfully dumped cache to ` + cacheDirPath, undefined);
|
|
730
|
-
} else {
|
|
731
|
-
return;
|
|
732
|
-
}
|
|
733
|
-
} catch (raw_exn) {
|
|
734
|
-
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
735
|
-
return Logger.errorWithExn(logger, `Failed to dump cache.`, exn);
|
|
736
|
-
}
|
|
737
|
-
};
|
|
307
|
+
let ensureSchemaIndexes = chainIds => client.ensureSchemaIndexes(chainIds);
|
|
308
|
+
let finalizeBackfill = (chainIds, readyAt) => client.finalizeBackfill(chainIds, readyAt.getTime());
|
|
309
|
+
let dumpEffectCache = () => client.dumpEffectCache(cacheDirPath);
|
|
738
310
|
let readStoredConfig = async () => {
|
|
739
311
|
let stored = await client.readStoredConfig();
|
|
740
312
|
let configAddresses = StorageClient.read(client, stored.configAddresses);
|
|
@@ -772,19 +344,14 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
772
344
|
contractMapping: ContractMapping.fromStoredNames(match$1)
|
|
773
345
|
};
|
|
774
346
|
};
|
|
775
|
-
let addChain = (chainConfig,
|
|
347
|
+
let addChain = (chainConfig, contractMapping) => client.addChain({
|
|
776
348
|
chain: toChainConfig(chainConfig),
|
|
777
|
-
partitions: partitions(entities, [chainConfig.id]),
|
|
778
349
|
addresses: addressColumns(ChainState.configStorageRows(chainConfig, ecosystem, contractMapping))
|
|
779
350
|
});
|
|
780
|
-
let resumeInitialState = async (
|
|
351
|
+
let resumeInitialState = async (chainIds, contractMapping) => {
|
|
781
352
|
let match = await Promise.all([
|
|
782
353
|
restoreEffectCache(false),
|
|
783
|
-
client.resume(
|
|
784
|
-
sequence: CheckpointSequence.fromEntities(entities),
|
|
785
|
-
tables: entities.map(entityConfig => entityTable(entityConfig).handle),
|
|
786
|
-
chainIds: chainIds
|
|
787
|
-
})
|
|
354
|
+
client.resume(chainIds)
|
|
788
355
|
]);
|
|
789
356
|
let resumed = match[1];
|
|
790
357
|
let addressRowsByChainId = AddressRows.group(StorageClient.read(client, resumed.addresses));
|
|
@@ -811,10 +378,10 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
811
378
|
});
|
|
812
379
|
let checkpointFrontier = Frontier.fromEntries(stored.map(param => [
|
|
813
380
|
param[0],
|
|
814
|
-
|
|
381
|
+
param[1].checkpointId
|
|
815
382
|
]));
|
|
816
383
|
let reorgCheckpoints = resumed.reorgCheckpoints.map(checkpoint => ({
|
|
817
|
-
id:
|
|
384
|
+
id: checkpoint.id,
|
|
818
385
|
chain_id: ChainId.normalizeOrThrow(checkpoint.chainId),
|
|
819
386
|
block_number: checkpoint.blockNumber,
|
|
820
387
|
block_hash: checkpoint.blockHash
|
|
@@ -841,15 +408,15 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
841
408
|
});
|
|
842
409
|
let setChainMeta = chainsData => client.setChainMeta(chainMeta(chainsData)).then(() => (undefined));
|
|
843
410
|
let pruneStaleCheckpoints = safeCheckpoints => client.pruneCheckpoints(bounds(safeCheckpoints));
|
|
844
|
-
let pruneStaleEntityHistory = (entityConfig, safeCheckpoints) => client.pruneHistory(
|
|
845
|
-
let getRollbackTargetCheckpoint = async (reorgChainId, lastKnownValidBlockNumber
|
|
411
|
+
let pruneStaleEntityHistory = (entityConfig, safeCheckpoints) => client.pruneHistory(entityConfig.index, bounds(safeCheckpoints));
|
|
412
|
+
let getRollbackTargetCheckpoint = async (reorgChainId, lastKnownValidBlockNumber) => Primitive_option.fromNullable(await client.rollbackTargetCheckpoint(reorgChainId, lastKnownValidBlockNumber));
|
|
846
413
|
let getRollbackProgressDiff = async floors => (await client.rollbackProgressDiff(bounds(floors.checkpointBounds))).map(diff => ({
|
|
847
414
|
chain_id: ChainId.normalizeOrThrow(diff.chainId),
|
|
848
415
|
events_processed_diff: diff.eventsProcessed,
|
|
849
416
|
new_progress_block_number: diff.progressBlock
|
|
850
417
|
}));
|
|
851
418
|
let getRollbackData = async (entityConfig, floors) => {
|
|
852
|
-
let match = await client.rollbackData(
|
|
419
|
+
let match = await client.rollbackData(entityConfig.index, bounds(floors.checkpointBounds));
|
|
853
420
|
let restored = match.restored;
|
|
854
421
|
let removedIdRows;
|
|
855
422
|
try {
|
|
@@ -867,29 +434,28 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
867
434
|
return "crossChain";
|
|
868
435
|
}
|
|
869
436
|
};
|
|
870
|
-
let removals = removedIdRows.map(row => ({
|
|
871
|
-
entityId: S$RescriptSchema.parseOrThrow(row, rollbackRemovedIdSchema(entityConfig.table)),
|
|
872
|
-
scope: scopeOf(row)
|
|
873
|
-
}));
|
|
874
|
-
let restoredEntitiesResult = [];
|
|
875
|
-
rollbackRows.forEach(row => {
|
|
876
|
-
let match = S$RescriptSchema.parseOrThrow(row, rollbackRowStateSchema(entityConfig.table));
|
|
877
|
-
if (match[1] === "SET") {
|
|
878
|
-
restoredEntitiesResult.push(row);
|
|
879
|
-
return;
|
|
880
|
-
}
|
|
881
|
-
removals.push({
|
|
882
|
-
entityId: match[0],
|
|
883
|
-
scope: scopeOf(row)
|
|
884
|
-
});
|
|
885
|
-
});
|
|
886
437
|
return [
|
|
887
|
-
|
|
888
|
-
|
|
438
|
+
removedIdRows.map(row => ({
|
|
439
|
+
entityId: S$RescriptSchema.parseOrThrow(row, rollbackRemovedIdSchema(entityConfig.table)),
|
|
440
|
+
scope: scopeOf(row)
|
|
441
|
+
})),
|
|
442
|
+
S$RescriptSchema.parseOrThrow(rollbackRows, Table.pgRowsSchema(entityConfig.table))
|
|
889
443
|
];
|
|
890
444
|
};
|
|
891
|
-
let
|
|
892
|
-
|
|
445
|
+
let stageRows = (table, writers, items) => stageRowsOrThrow(StorageClient.arena(client, rows => client.beginRows(table, rows)), writers, items);
|
|
446
|
+
let stageChanges = param => {
|
|
447
|
+
let entityConfig = param.entityConfig;
|
|
448
|
+
return stageChangesOrThrow(StorageClient.arena(client, rows => client.beginChanges(entityConfig.index, rows)), entityWriters[entityConfig.index], param.changes);
|
|
449
|
+
};
|
|
450
|
+
let changesErrorOf = (entityConfig, exn) => ({
|
|
451
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
452
|
+
message: entityConfig.storage.postgres ? `Failed to insert items into table "` + entityConfig.table.tableName + `"` : `Failed to convert items for ClickHouse table "` + EntityHistory.historyTableName(entityConfig.name, entityConfig.index) + `"`,
|
|
453
|
+
reason: Utils.prettifyExn(exn)
|
|
454
|
+
});
|
|
455
|
+
let writeBatch = async (batch, rollback, config, updatedEffectsCache, updatedEntities, registeredAddresses, chainMetaData, onWrite) => {
|
|
456
|
+
let frontier = Persistence.writtenFrontier(batch, rollback);
|
|
457
|
+
let chainIds = Frontier.chainIds(frontier);
|
|
458
|
+
let frontierCheckpointIds = chainIds.map(chainId => Frontier.get(frontier, chainId));
|
|
893
459
|
let rawEvents;
|
|
894
460
|
if (config.enableRawEvents) {
|
|
895
461
|
let seenLogCoordinates = new Set();
|
|
@@ -908,21 +474,35 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
908
474
|
} else {
|
|
909
475
|
rawEvents = [];
|
|
910
476
|
}
|
|
911
|
-
let
|
|
912
|
-
let
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
477
|
+
let handles = [];
|
|
478
|
+
let staged = handle => {
|
|
479
|
+
handles.push(handle);
|
|
480
|
+
return handle;
|
|
481
|
+
};
|
|
482
|
+
let stagingStarted = Performance.now();
|
|
483
|
+
let clickHouseStagingSeconds = {
|
|
484
|
+
contents: 0
|
|
485
|
+
};
|
|
486
|
+
let clickHouseOnlyStagingSeconds = {
|
|
487
|
+
contents: 0
|
|
916
488
|
};
|
|
917
489
|
let input;
|
|
918
490
|
try {
|
|
919
491
|
let tmp;
|
|
920
492
|
if (Utils.$$Array.notEmpty(rawEvents)) {
|
|
921
|
-
let
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
493
|
+
let tmp$1;
|
|
494
|
+
try {
|
|
495
|
+
tmp$1 = staged(stageRows("RawEvents", rawEventWriters, rawEvents));
|
|
496
|
+
} catch (raw_exn) {
|
|
497
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
498
|
+
throw {
|
|
499
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
500
|
+
message: `Failed to insert items into table "` + InternalTable.RawEvents.table.tableName + `"`,
|
|
501
|
+
reason: Utils.prettifyExn(exn),
|
|
502
|
+
Error: new Error()
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
tmp = tmp$1;
|
|
926
506
|
} else {
|
|
927
507
|
tmp = undefined;
|
|
928
508
|
}
|
|
@@ -931,7 +511,6 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
931
511
|
let rolledBackAddresses = param.rolledBackAddresses;
|
|
932
512
|
return {
|
|
933
513
|
bounds: bounds(param.floors.checkpointBounds),
|
|
934
|
-
tables: allEntities.map(entityConfig => entityTable(entityConfig).handle),
|
|
935
514
|
progress: param.progressedChains.map(progress),
|
|
936
515
|
removedAddresses: {
|
|
937
516
|
chainIds: rolledBackAddresses.map(key => key.chainId),
|
|
@@ -940,7 +519,7 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
940
519
|
},
|
|
941
520
|
diffCheckpoints: param.diffCheckpoints.map(diff => ({
|
|
942
521
|
chainId: diff.chainId,
|
|
943
|
-
checkpointId: diff.checkpointId
|
|
522
|
+
checkpointId: diff.checkpointId,
|
|
944
523
|
blockNumber: diff.blockNumber
|
|
945
524
|
}))
|
|
946
525
|
};
|
|
@@ -953,124 +532,41 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
953
532
|
totalEventsProcessed: chainAfterBatch.totalEventsProcessed
|
|
954
533
|
})),
|
|
955
534
|
rawEvents: tmp,
|
|
956
|
-
|
|
957
|
-
let
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
let
|
|
962
|
-
let
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
type: "SET",
|
|
976
|
-
entityId: change.entityId,
|
|
977
|
-
entity: Internal.stampChainId(change.entity, field.fieldName, chainId),
|
|
978
|
-
checkpointId: change.checkpointId
|
|
979
|
-
};
|
|
980
|
-
} else {
|
|
981
|
-
return change;
|
|
982
|
-
}
|
|
983
|
-
});
|
|
984
|
-
} else {
|
|
985
|
-
stamped = changes;
|
|
986
|
-
}
|
|
987
|
-
let diffCheckpointId = Stdlib_Option.flatMap(rollback, r => CheckpointSequence.findForScope(config.checkpointSequence, r.diffFrontier, scope));
|
|
988
|
-
let historySets = [];
|
|
989
|
-
let historySetCheckpointIds = [];
|
|
990
|
-
let historyDeleteIds = [];
|
|
991
|
-
let historyDeleteCheckpointIds = [];
|
|
992
|
-
let idsWithDiff = new Set();
|
|
993
|
-
let latestChangeById = {};
|
|
994
|
-
let orderedIds = [];
|
|
995
|
-
stamped.forEach(change => {
|
|
996
|
-
let entityId = change.entityId;
|
|
997
|
-
let entityKey = EntityId.toKey(entityId);
|
|
998
|
-
if (Stdlib_Option.isNone(latestChangeById[entityKey])) {
|
|
999
|
-
orderedIds.push(entityId);
|
|
1000
|
-
}
|
|
1001
|
-
latestChangeById[entityKey] = change;
|
|
1002
|
-
if (!shouldSaveHistory) {
|
|
1003
|
-
return;
|
|
1004
|
-
}
|
|
1005
|
-
if (change.checkpointId === diffCheckpointId) {
|
|
1006
|
-
idsWithDiff.add(entityKey);
|
|
1007
|
-
return;
|
|
1008
|
-
}
|
|
1009
|
-
if (change.type === "SET") {
|
|
1010
|
-
historySets.push(change.entity);
|
|
1011
|
-
historySetCheckpointIds.push(change.checkpointId.toString());
|
|
1012
|
-
return;
|
|
1013
|
-
}
|
|
1014
|
-
historyDeleteIds.push(change.entityId);
|
|
1015
|
-
historyDeleteCheckpointIds.push(change.checkpointId.toString());
|
|
1016
|
-
});
|
|
1017
|
-
let sets = [];
|
|
1018
|
-
let deletes = [];
|
|
1019
|
-
let backfill = [];
|
|
1020
|
-
orderedIds.forEach(entityId => {
|
|
1021
|
-
let entityKey = EntityId.toKey(entityId);
|
|
1022
|
-
let match = latestChangeById[entityKey];
|
|
1023
|
-
if (match.type === "SET") {
|
|
1024
|
-
sets.push(match.entity);
|
|
1025
|
-
} else {
|
|
1026
|
-
deletes.push(match.entityId);
|
|
1027
|
-
}
|
|
1028
|
-
if (shouldSaveHistory && !idsWithDiff.has(entityKey)) {
|
|
1029
|
-
backfill.push(entityId);
|
|
1030
|
-
return;
|
|
1031
|
-
}
|
|
1032
|
-
});
|
|
1033
|
-
let rowsOf = entities => {
|
|
1034
|
-
if (Utils.$$Array.notEmpty(entities)) {
|
|
1035
|
-
return rowsOrThrow(client, table, getRowSchema(entityConfig), entities, staged);
|
|
1036
|
-
}
|
|
1037
|
-
};
|
|
1038
|
-
return {
|
|
1039
|
-
sets: rowsOf(sets),
|
|
1040
|
-
deletes: renderIds(entityConfig.table, deletes),
|
|
1041
|
-
history: shouldSaveHistory ? ({
|
|
1042
|
-
backfill: renderIds(entityConfig.table, backfill),
|
|
1043
|
-
sets: rowsOf(historySets),
|
|
1044
|
-
setCheckpointIds: historySetCheckpointIds,
|
|
1045
|
-
deleteIds: renderIds(entityConfig.table, historyDeleteIds),
|
|
1046
|
-
deleteCheckpointIds: historyDeleteCheckpointIds
|
|
1047
|
-
}) : undefined
|
|
1048
|
-
};
|
|
1049
|
-
};
|
|
1050
|
-
let match$1 = table.clickhouse;
|
|
1051
|
-
let tmp;
|
|
1052
|
-
if (changes.length !== 0 && match$1 !== undefined) {
|
|
1053
|
-
let handle = stagedFor(staged, "ClickHouse", () => ClickHouse.stageChangesOrThrow(StorageClient.arena(client, "ClickHouse"), match$1, entityConfig, scope, changes));
|
|
1054
|
-
staged_handles.push(handle);
|
|
1055
|
-
tmp = handle;
|
|
1056
|
-
} else {
|
|
1057
|
-
tmp = undefined;
|
|
535
|
+
changes: Stdlib_Array.filterMap(updatedEntities, update => {
|
|
536
|
+
let match = update.changes;
|
|
537
|
+
if (match.length === 0) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
let started = Performance.now();
|
|
541
|
+
let handle;
|
|
542
|
+
try {
|
|
543
|
+
handle = staged(stageChanges(update));
|
|
544
|
+
} catch (raw_exn) {
|
|
545
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
546
|
+
throw changesErrorOf(update.entityConfig, exn);
|
|
547
|
+
}
|
|
548
|
+
let seconds = Performance.secondsSince(started);
|
|
549
|
+
if (update.entityConfig.storage.clickhouse) {
|
|
550
|
+
clickHouseStagingSeconds.contents = clickHouseStagingSeconds.contents + seconds;
|
|
551
|
+
}
|
|
552
|
+
if (!update.entityConfig.storage.postgres) {
|
|
553
|
+
clickHouseOnlyStagingSeconds.contents = clickHouseOnlyStagingSeconds.contents + seconds;
|
|
1058
554
|
}
|
|
1059
555
|
return {
|
|
1060
|
-
|
|
1061
|
-
chainId:
|
|
1062
|
-
|
|
1063
|
-
|
|
556
|
+
entity: update.entityConfig.index,
|
|
557
|
+
chainId: Internal.chainScopeChainId(update.scope),
|
|
558
|
+
keepsHistory: update.shouldSaveHistory,
|
|
559
|
+
changes: handle
|
|
1064
560
|
};
|
|
1065
561
|
}),
|
|
1066
562
|
chainMeta: Stdlib_Option.mapOr(chainMetaData, [], chainMeta),
|
|
1067
563
|
addresses: addressColumns(registeredAddresses.map(staged => staged.row)),
|
|
1068
564
|
frontier: {
|
|
1069
|
-
chainIds:
|
|
1070
|
-
checkpointIds:
|
|
565
|
+
chainIds: chainIds,
|
|
566
|
+
checkpointIds: frontierCheckpointIds
|
|
1071
567
|
},
|
|
1072
568
|
checkpoints: {
|
|
1073
|
-
ids: batch.checkpointIds
|
|
569
|
+
ids: batch.checkpointIds,
|
|
1074
570
|
chainIds: batch.checkpointChainIds,
|
|
1075
571
|
blockNumbers: batch.checkpointBlockNumbers,
|
|
1076
572
|
blockHashes: batch.checkpointBlockHashes,
|
|
@@ -1078,35 +574,59 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
1078
574
|
keepsHistory: batch.checkpointChainIds.map(chainId => HistoryPolicy.forChain(batch.history, chainId))
|
|
1079
575
|
},
|
|
1080
576
|
effectCaches: updatedEffectsCache.map(param => {
|
|
1081
|
-
let
|
|
577
|
+
let itemSchema = param.itemSchema;
|
|
578
|
+
let table = param.table;
|
|
579
|
+
let tmp;
|
|
580
|
+
try {
|
|
581
|
+
tmp = staged(stageRows("EffectCache", effectCacheWriters, param.items.map(item => S$RescriptSchema.reverseConvertOrThrow(item, itemSchema))));
|
|
582
|
+
} catch (raw_exn) {
|
|
583
|
+
let exn = Primitive_exceptions.internalToException(raw_exn);
|
|
584
|
+
if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
|
|
585
|
+
throw {
|
|
586
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
587
|
+
message: `Failed to convert items for table "` + table.tableName + `"`,
|
|
588
|
+
reason: exn,
|
|
589
|
+
Error: new Error()
|
|
590
|
+
};
|
|
591
|
+
}
|
|
592
|
+
throw {
|
|
593
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
594
|
+
message: `Failed to insert items into table "` + table.tableName + `"`,
|
|
595
|
+
reason: Utils.prettifyExn(exn),
|
|
596
|
+
Error: new Error()
|
|
597
|
+
};
|
|
598
|
+
}
|
|
1082
599
|
return {
|
|
1083
|
-
table:
|
|
600
|
+
table: table.tableName,
|
|
1084
601
|
create: param.shouldInitialize,
|
|
1085
|
-
rows:
|
|
602
|
+
rows: tmp
|
|
1086
603
|
};
|
|
1087
604
|
})
|
|
1088
605
|
};
|
|
1089
|
-
} catch (exn) {
|
|
1090
|
-
client.discardStaged(
|
|
1091
|
-
throw exn;
|
|
606
|
+
} catch (exn$1) {
|
|
607
|
+
client.discardStaged(handles);
|
|
608
|
+
throw exn$1;
|
|
1092
609
|
}
|
|
610
|
+
let postgresStagingSeconds = Performance.secondsSince(stagingStarted) - clickHouseOnlyStagingSeconds.contents;
|
|
1093
611
|
let timings;
|
|
1094
612
|
try {
|
|
1095
613
|
timings = await client.writeBatch(input);
|
|
1096
|
-
} catch (raw_exn) {
|
|
1097
|
-
let exn$
|
|
1098
|
-
client.discardStaged(
|
|
1099
|
-
throw storageErrorOf(exn$
|
|
614
|
+
} catch (raw_exn$1) {
|
|
615
|
+
let exn$2 = Primitive_exceptions.internalToException(raw_exn$1);
|
|
616
|
+
client.discardStaged(handles);
|
|
617
|
+
throw storageErrorOf(exn$2);
|
|
1100
618
|
}
|
|
1101
619
|
timings.forEach(param => {
|
|
1102
|
-
let
|
|
1103
|
-
|
|
620
|
+
let store = param.store;
|
|
621
|
+
let tmp;
|
|
622
|
+
tmp = store === "Postgres" ? postgresStagingSeconds : clickHouseStagingSeconds.contents;
|
|
623
|
+
onWrite(storeName(store), param.seconds + tmp);
|
|
1104
624
|
});
|
|
1105
625
|
};
|
|
1106
626
|
let close = () => client.close();
|
|
1107
627
|
return {
|
|
1108
628
|
name: postgresName,
|
|
1109
|
-
entityStoreName: entityConfig =>
|
|
629
|
+
entityStoreName: entityConfig => Stdlib_Option.mapOr(layouts.entities[entityConfig.index].home, postgresName, storeName),
|
|
1110
630
|
holding: holding,
|
|
1111
631
|
initialize: initialize,
|
|
1112
632
|
readStoredConfig: readStoredConfig,
|
|
@@ -1131,13 +651,13 @@ function make(client, logger, entitiesOpt, cacheDir, ecosystem, isolatedOpt, onI
|
|
|
1131
651
|
|
|
1132
652
|
function clientFromEnv(config, logger, pgSchemaOpt, isHasuraEnabled, maxConnections) {
|
|
1133
653
|
let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
|
|
1134
|
-
return makeClient(logger, config
|
|
654
|
+
return makeClient(logger, config, postgresOptions(pgSchema, isHasuraEnabled, maxConnections, config.isolated), config.storage.clickhouse ? ClickHouse.optionsFromEnvOrThrow() : undefined);
|
|
1135
655
|
}
|
|
1136
656
|
|
|
1137
657
|
function makeStorageFromEnv(config, logger, pgSchemaOpt, isHasuraEnabledOpt, maxConnections, cacheDir) {
|
|
1138
658
|
let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
|
|
1139
659
|
let isHasuraEnabled = isHasuraEnabledOpt !== undefined ? isHasuraEnabledOpt : Env.Hasura.enabled;
|
|
1140
|
-
return make(clientFromEnv(config, logger, pgSchema, isHasuraEnabled, maxConnections),
|
|
660
|
+
return make(clientFromEnv(config, logger, pgSchema, isHasuraEnabled, maxConnections), cacheDir, config.ecosystem.name, isHasuraEnabled ? () => Stdlib_Promise.$$catch(Hasura.trackDatabase(logger, Env.Hasura.graphqlEndpoint, {
|
|
1141
661
|
role: Env.Hasura.role,
|
|
1142
662
|
secret: Env.Hasura.secret
|
|
1143
663
|
}, pgSchema, Config.getPgUserEntities(config), Env.Hasura.aggregateEntities, Env.Hasura.responseLimit, Schema.make(config.userEntities.map(e => e.table))), err => Promise.resolve(Logger.errorWithExn(logger, `Error tracking tables`, err))) : undefined);
|
|
@@ -1149,36 +669,24 @@ function makePersistenceFromConfig(config, logger, storageOpt) {
|
|
|
1149
669
|
}
|
|
1150
670
|
|
|
1151
671
|
export {
|
|
1152
|
-
formatSeconds,
|
|
1153
|
-
slowOnLargeDatabaseNotice,
|
|
1154
672
|
storeName,
|
|
1155
673
|
postgresName,
|
|
1156
|
-
logIndexEvent,
|
|
1157
674
|
postgresOptions,
|
|
1158
675
|
makeClient,
|
|
1159
|
-
partitionTableName,
|
|
1160
|
-
indexTableNames,
|
|
1161
|
-
getSchemaIndexes,
|
|
1162
|
-
pgColumnInput,
|
|
1163
|
-
rowSchemaCache,
|
|
1164
|
-
getRowSchema,
|
|
1165
|
-
registerEntity,
|
|
1166
|
-
arenaConverters,
|
|
1167
|
-
cellConverters,
|
|
1168
|
-
converterOf,
|
|
1169
|
-
stagedFor,
|
|
1170
|
-
renderIds,
|
|
1171
676
|
bounds,
|
|
1172
677
|
progress,
|
|
1173
678
|
addressColumns,
|
|
1174
679
|
toChainConfig,
|
|
1175
680
|
storageErrorOf,
|
|
1176
|
-
rollbackRowStateSchema,
|
|
1177
681
|
rollbackChainIdSchema,
|
|
1178
682
|
rollbackRemovedIdSchema,
|
|
683
|
+
changeColumns,
|
|
684
|
+
stageOrThrow,
|
|
685
|
+
stageRowsOrThrow,
|
|
686
|
+
stageChangesOrThrow,
|
|
1179
687
|
make,
|
|
1180
688
|
clientFromEnv,
|
|
1181
689
|
makeStorageFromEnv,
|
|
1182
690
|
makePersistenceFromConfig,
|
|
1183
691
|
}
|
|
1184
|
-
/*
|
|
692
|
+
/* rollbackChainIdSchema Not a pure module */
|