envio 2.26.0-rc.0 → 2.26.0-rc.2
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 +7 -0
- package/fuel.schema.json +7 -0
- package/index.d.ts +6 -22
- package/index.js +2 -1
- package/package.json +5 -5
- package/src/Envio.gen.ts +3 -1
- package/src/Envio.res +27 -0
- package/src/Envio.res.js +28 -1
- package/src/FetchState.res +1 -4
- package/src/Internal.res +20 -0
- package/src/Internal.res.js +12 -0
- package/src/Logging.res +8 -0
- package/src/Logging.res.js +29 -0
- package/src/Persistence.res +109 -47
- package/src/Persistence.res.js +62 -17
- package/src/PgStorage.res +488 -86
- package/src/PgStorage.res.js +365 -57
- package/src/Prometheus.res +12 -0
- package/src/Prometheus.res.js +12 -0
- package/src/Utils.res +39 -9
- package/src/Utils.res.js +17 -6
- package/src/bindings/BigInt.gen.ts +10 -0
- package/src/bindings/BigInt.res +1 -0
- package/src/bindings/NodeJs.res +27 -26
- package/src/bindings/NodeJs.res.js +5 -13
- package/src/db/EntityHistory.res +5 -28
- package/src/db/EntityHistory.res.js +4 -23
- package/src/db/Table.res +3 -61
- package/src/db/Table.res.js +3 -42
package/src/PgStorage.res.js
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
var Fs = require("fs");
|
|
5
|
+
var Path = require("path");
|
|
4
6
|
var $$Array = require("rescript/lib/js/array.js");
|
|
5
7
|
var Table = require("./db/Table.res.js");
|
|
6
8
|
var Utils = require("./Utils.res.js");
|
|
7
9
|
var Js_exn = require("rescript/lib/js/js_exn.js");
|
|
8
10
|
var Schema = require("./db/Schema.res.js");
|
|
11
|
+
var Js_dict = require("rescript/lib/js/js_dict.js");
|
|
12
|
+
var Logging = require("./Logging.res.js");
|
|
13
|
+
var $$Promise = require("./bindings/Promise.res.js");
|
|
14
|
+
var Internal = require("./Internal.res.js");
|
|
9
15
|
var Belt_Array = require("rescript/lib/js/belt_Array.js");
|
|
10
|
-
var
|
|
16
|
+
var Belt_Option = require("rescript/lib/js/belt_Option.js");
|
|
11
17
|
var Caml_option = require("rescript/lib/js/caml_option.js");
|
|
12
18
|
var Persistence = require("./Persistence.res.js");
|
|
19
|
+
var Child_process = require("child_process");
|
|
20
|
+
var Caml_exceptions = require("rescript/lib/js/caml_exceptions.js");
|
|
13
21
|
var S$RescriptSchema = require("rescript-schema/src/S.res.js");
|
|
14
22
|
var Caml_js_exceptions = require("rescript/lib/js/caml_js_exceptions.js");
|
|
15
23
|
|
|
16
|
-
function
|
|
24
|
+
function makeCreateIndexQuery(tableName, indexFields, pgSchema) {
|
|
17
25
|
var indexName = tableName + "_" + indexFields.join("_");
|
|
18
26
|
var index = Belt_Array.map(indexFields, (function (idx) {
|
|
19
27
|
return "\"" + idx + "\"";
|
|
@@ -21,20 +29,20 @@ function makeCreateIndexSql(tableName, indexFields, pgSchema) {
|
|
|
21
29
|
return "CREATE INDEX IF NOT EXISTS \"" + indexName + "\" ON \"" + pgSchema + "\".\"" + tableName + "\"(" + index + ");";
|
|
22
30
|
}
|
|
23
31
|
|
|
24
|
-
function
|
|
32
|
+
function makeCreateTableIndicesQuery(table, pgSchema) {
|
|
25
33
|
var tableName = table.tableName;
|
|
26
34
|
var createIndex = function (indexField) {
|
|
27
|
-
return
|
|
35
|
+
return makeCreateIndexQuery(tableName, [indexField], pgSchema);
|
|
28
36
|
};
|
|
29
37
|
var createCompositeIndex = function (indexFields) {
|
|
30
|
-
return
|
|
38
|
+
return makeCreateIndexQuery(tableName, indexFields, pgSchema);
|
|
31
39
|
};
|
|
32
40
|
var singleIndices = Table.getSingleIndices(table);
|
|
33
41
|
var compositeIndices = Table.getCompositeIndices(table);
|
|
34
42
|
return Belt_Array.map(singleIndices, createIndex).join("\n") + Belt_Array.map(compositeIndices, createCompositeIndex).join("\n");
|
|
35
43
|
}
|
|
36
44
|
|
|
37
|
-
function
|
|
45
|
+
function makeCreateTableQuery(table, pgSchema) {
|
|
38
46
|
var fieldsMapped = Belt_Array.map(Table.getFields(table), (function (field) {
|
|
39
47
|
var defaultValue = field.defaultValue;
|
|
40
48
|
var fieldType = field.fieldType;
|
|
@@ -58,11 +66,11 @@ function makeCreateTableSql(table, pgSchema) {
|
|
|
58
66
|
) + ");";
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
function makeInitializeTransaction(pgSchema, pgUser, generalTablesOpt, entitiesOpt, enumsOpt,
|
|
69
|
+
function makeInitializeTransaction(pgSchema, pgUser, generalTablesOpt, entitiesOpt, enumsOpt, isEmptyPgSchemaOpt) {
|
|
62
70
|
var generalTables = generalTablesOpt !== undefined ? generalTablesOpt : [];
|
|
63
71
|
var entities = entitiesOpt !== undefined ? entitiesOpt : [];
|
|
64
72
|
var enums = enumsOpt !== undefined ? enumsOpt : [];
|
|
65
|
-
var
|
|
73
|
+
var isEmptyPgSchema = isEmptyPgSchemaOpt !== undefined ? isEmptyPgSchemaOpt : false;
|
|
66
74
|
var allTables = $$Array.copy(generalTables);
|
|
67
75
|
var allEntityTables = [];
|
|
68
76
|
entities.forEach(function (entity) {
|
|
@@ -73,22 +81,20 @@ function makeInitializeTransaction(pgSchema, pgUser, generalTablesOpt, entitiesO
|
|
|
73
81
|
var derivedSchema = Schema.make(allEntityTables);
|
|
74
82
|
var query = {
|
|
75
83
|
contents: (
|
|
76
|
-
|
|
77
|
-
) + ("GRANT ALL ON SCHEMA \"" + pgSchema + "\" TO " + pgUser + ";\nGRANT ALL ON SCHEMA \"" + pgSchema + "\" TO public;")
|
|
84
|
+
isEmptyPgSchema && pgSchema === "public" ? "" : "DROP SCHEMA IF EXISTS \"" + pgSchema + "\" CASCADE;\nCREATE SCHEMA \"" + pgSchema + "\";\n"
|
|
85
|
+
) + ("GRANT ALL ON SCHEMA \"" + pgSchema + "\" TO \"" + pgUser + "\";\nGRANT ALL ON SCHEMA \"" + pgSchema + "\" TO public;")
|
|
78
86
|
};
|
|
79
87
|
enums.forEach(function (enumConfig) {
|
|
80
88
|
var enumCreateQuery = "CREATE TYPE \"" + pgSchema + "\"." + enumConfig.name + " AS ENUM(" + enumConfig.variants.map(function (v) {
|
|
81
89
|
return "'" + v + "'";
|
|
82
90
|
}).join(", ") + ");";
|
|
83
|
-
query.contents = query.contents + "\n" +
|
|
84
|
-
cleanRun ? enumCreateQuery : "IF NOT EXISTS (\n SELECT 1 FROM pg_type \n WHERE typname = '" + enumConfig.name.toLowerCase() + "' \n AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = '" + pgSchema + "')\n) THEN \n " + enumCreateQuery + "\nEND IF;"
|
|
85
|
-
);
|
|
91
|
+
query.contents = query.contents + "\n" + enumCreateQuery;
|
|
86
92
|
});
|
|
87
93
|
allTables.forEach(function (table) {
|
|
88
|
-
query.contents = query.contents + "\n" +
|
|
94
|
+
query.contents = query.contents + "\n" + makeCreateTableQuery(table, pgSchema);
|
|
89
95
|
});
|
|
90
96
|
allTables.forEach(function (table) {
|
|
91
|
-
var indices =
|
|
97
|
+
var indices = makeCreateTableIndicesQuery(table, pgSchema);
|
|
92
98
|
if (indices !== "") {
|
|
93
99
|
query.contents = query.contents + "\n" + indices;
|
|
94
100
|
return ;
|
|
@@ -102,22 +108,27 @@ function makeInitializeTransaction(pgSchema, pgUser, generalTablesOpt, entitiesO
|
|
|
102
108
|
functionsQuery.contents = functionsQuery.contents + "\n" + entity.entityHistory.createInsertFnQuery;
|
|
103
109
|
Table.getDerivedFromFields(entity.table).forEach(function (derivedFromField) {
|
|
104
110
|
var indexField = Utils.unwrapResultExn(Schema.getDerivedFromFieldName(derivedSchema, derivedFromField));
|
|
105
|
-
query.contents = query.contents + "\n" +
|
|
111
|
+
query.contents = query.contents + "\n" + makeCreateIndexQuery(derivedFromField.derivedFromEntity, [indexField], pgSchema);
|
|
106
112
|
});
|
|
107
113
|
});
|
|
108
|
-
|
|
114
|
+
functionsQuery.contents = functionsQuery.contents + "\n" + ("CREATE OR REPLACE FUNCTION get_cache_row_count(table_name text) \n RETURNS integer AS $$\n DECLARE\n result integer;\n BEGIN\n EXECUTE format('SELECT COUNT(*) FROM \"" + pgSchema + "\".%I', table_name) INTO result;\n RETURN result;\n END;\n $$ LANGUAGE plpgsql;");
|
|
115
|
+
return [query.contents].concat(functionsQuery.contents !== "" ? [functionsQuery.contents] : []);
|
|
109
116
|
}
|
|
110
117
|
|
|
111
|
-
function
|
|
118
|
+
function makeLoadByIdQuery(pgSchema, tableName) {
|
|
112
119
|
return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = $1 LIMIT 1;";
|
|
113
120
|
}
|
|
114
121
|
|
|
115
|
-
function
|
|
122
|
+
function makeLoadByFieldQuery(pgSchema, tableName, fieldName, operator) {
|
|
123
|
+
return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE \"" + fieldName + "\" " + operator + " $1;";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function makeLoadByIdsQuery(pgSchema, tableName) {
|
|
116
127
|
return "SELECT * FROM \"" + pgSchema + "\".\"" + tableName + "\" WHERE id = ANY($1::text[]);";
|
|
117
128
|
}
|
|
118
129
|
|
|
119
|
-
function
|
|
120
|
-
var match = Table.toSqlParams(table, itemSchema);
|
|
130
|
+
function makeInsertUnnestSetQuery(pgSchema, table, itemSchema, isRawEvents) {
|
|
131
|
+
var match = Table.toSqlParams(table, itemSchema, pgSchema);
|
|
121
132
|
var quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames;
|
|
122
133
|
var primaryKeyFieldNames = Table.getPrimaryKeyFieldNames(table);
|
|
123
134
|
return "INSERT INTO \"" + pgSchema + "\".\"" + table.tableName + "\" (" + match.quotedFieldNames.join(", ") + ")\nSELECT * FROM unnest(" + match.arrayFieldTypes.map(function (arrayFieldType, idx) {
|
|
@@ -133,8 +144,8 @@ function makeInsertUnnestSetSql(pgSchema, table, itemSchema, isRawEvents) {
|
|
|
133
144
|
) + ";";
|
|
134
145
|
}
|
|
135
146
|
|
|
136
|
-
function
|
|
137
|
-
var match = Table.toSqlParams(table, itemSchema);
|
|
147
|
+
function makeInsertValuesSetQuery(pgSchema, table, itemSchema, itemsCount) {
|
|
148
|
+
var match = Table.toSqlParams(table, itemSchema, pgSchema);
|
|
138
149
|
var quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames;
|
|
139
150
|
var quotedFieldNames = match.quotedFieldNames;
|
|
140
151
|
var primaryKeyFieldNames = Table.getPrimaryKeyFieldNames(table);
|
|
@@ -166,18 +177,20 @@ function makeInsertValuesSetSql(pgSchema, table, itemSchema, itemsCount) {
|
|
|
166
177
|
|
|
167
178
|
var rawEventsTableName = "raw_events";
|
|
168
179
|
|
|
180
|
+
var eventSyncStateTableName = "event_sync_state";
|
|
181
|
+
|
|
169
182
|
function makeTableBatchSetQuery(pgSchema, table, itemSchema) {
|
|
170
|
-
var match = Table.toSqlParams(table, itemSchema);
|
|
183
|
+
var match = Table.toSqlParams(table, itemSchema, pgSchema);
|
|
171
184
|
var isRawEvents = table.tableName === rawEventsTableName;
|
|
172
185
|
if (isRawEvents || !match.hasArrayField) {
|
|
173
186
|
return {
|
|
174
|
-
|
|
187
|
+
query: makeInsertUnnestSetQuery(pgSchema, table, itemSchema, isRawEvents),
|
|
175
188
|
convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.unnest(match.dbSchema), "Output", "Input", "Sync", false),
|
|
176
189
|
isInsertValues: false
|
|
177
190
|
};
|
|
178
191
|
} else {
|
|
179
192
|
return {
|
|
180
|
-
|
|
193
|
+
query: makeInsertValuesSetQuery(pgSchema, table, itemSchema, 500),
|
|
181
194
|
convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.preprocess(S$RescriptSchema.unnest(itemSchema), (function (param) {
|
|
182
195
|
return {
|
|
183
196
|
s: (function (prim) {
|
|
@@ -201,6 +214,24 @@ function chunkArray(arr, chunkSize) {
|
|
|
201
214
|
return chunks;
|
|
202
215
|
}
|
|
203
216
|
|
|
217
|
+
function removeInvalidUtf8InPlace(entities) {
|
|
218
|
+
entities.forEach(function (item) {
|
|
219
|
+
Utils.Dict.forEachWithKey(item, (function (key, value) {
|
|
220
|
+
if (typeof value === "string") {
|
|
221
|
+
item[key] = value.replaceAll("\x00", "");
|
|
222
|
+
return ;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
}));
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
var pgEncodingErrorSchema = S$RescriptSchema.object(function (s) {
|
|
230
|
+
s.tag("message", "invalid byte sequence for encoding \"UTF8\": 0x00");
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
var PgEncodingError = /* @__PURE__ */Caml_exceptions.create("PgStorage.PgEncodingError");
|
|
234
|
+
|
|
204
235
|
var setQueryCache = new WeakMap();
|
|
205
236
|
|
|
206
237
|
async function setOrThrow(sql, items, table, itemSchema, pgSchema) {
|
|
@@ -208,30 +239,24 @@ async function setOrThrow(sql, items, table, itemSchema, pgSchema) {
|
|
|
208
239
|
return ;
|
|
209
240
|
}
|
|
210
241
|
var cached = setQueryCache.get(table);
|
|
211
|
-
var
|
|
242
|
+
var data;
|
|
212
243
|
if (cached !== undefined) {
|
|
213
|
-
|
|
244
|
+
data = Caml_option.valFromOption(cached);
|
|
214
245
|
} else {
|
|
215
246
|
var newQuery = makeTableBatchSetQuery(pgSchema, table, itemSchema);
|
|
216
247
|
setQueryCache.set(table, newQuery);
|
|
217
|
-
|
|
248
|
+
data = newQuery;
|
|
218
249
|
}
|
|
219
|
-
var sqlQuery = query.sql;
|
|
220
250
|
try {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return await sql.unsafe(sqlQuery, payload, {prepare: true});
|
|
251
|
+
if (!data.isInsertValues) {
|
|
252
|
+
return await sql.unsafe(data.query, data.convertOrThrow(items), {prepare: true});
|
|
224
253
|
}
|
|
225
|
-
var
|
|
226
|
-
var fieldsCount;
|
|
227
|
-
fieldsCount = typeof match !== "object" || match.TAG !== "object" ? Js_exn.raiseError("Expected an object schema for table") : match.items.length;
|
|
228
|
-
var maxChunkSize = Math.imul(500, fieldsCount);
|
|
229
|
-
var chunks = chunkArray(payload, maxChunkSize);
|
|
254
|
+
var chunks = chunkArray(items, 500);
|
|
230
255
|
var responses = [];
|
|
231
256
|
chunks.forEach(function (chunk) {
|
|
232
257
|
var chunkSize = chunk.length;
|
|
233
|
-
var isFullChunk = chunkSize ===
|
|
234
|
-
var response = sql.unsafe(isFullChunk ?
|
|
258
|
+
var isFullChunk = chunkSize === 500;
|
|
259
|
+
var response = sql.unsafe(isFullChunk ? data.query : makeInsertValuesSetQuery(pgSchema, table, itemSchema, chunkSize), data.convertOrThrow(chunk), {prepare: true});
|
|
235
260
|
responses.push(response);
|
|
236
261
|
});
|
|
237
262
|
await Promise.all(responses);
|
|
@@ -250,34 +275,128 @@ async function setOrThrow(sql, items, table, itemSchema, pgSchema) {
|
|
|
250
275
|
throw {
|
|
251
276
|
RE_EXN_ID: Persistence.StorageError,
|
|
252
277
|
message: "Failed to insert items into table \"" + table.tableName + "\"",
|
|
253
|
-
reason: exn,
|
|
278
|
+
reason: Internal.prettifyExn(exn),
|
|
254
279
|
Error: new Error()
|
|
255
280
|
};
|
|
256
281
|
}
|
|
257
282
|
}
|
|
258
283
|
|
|
259
|
-
function
|
|
284
|
+
function setEntityHistoryOrThrow(sql, entityHistory, rows, shouldCopyCurrentEntity, shouldRemoveInvalidUtf8Opt) {
|
|
285
|
+
var shouldRemoveInvalidUtf8 = shouldRemoveInvalidUtf8Opt !== undefined ? shouldRemoveInvalidUtf8Opt : false;
|
|
286
|
+
return Promise.all(Belt_Array.map(rows, (function (historyRow) {
|
|
287
|
+
var row = S$RescriptSchema.reverseConvertToJsonOrThrow(historyRow, entityHistory.schema);
|
|
288
|
+
if (shouldRemoveInvalidUtf8) {
|
|
289
|
+
removeInvalidUtf8InPlace([row]);
|
|
290
|
+
}
|
|
291
|
+
return entityHistory.insertFn(sql, row, shouldCopyCurrentEntity !== undefined ? shouldCopyCurrentEntity : !Belt_Option.getWithDefault(historyRow.containsRollbackDiffChange, false));
|
|
292
|
+
})));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function makeSchemaTableNamesQuery(pgSchema) {
|
|
296
|
+
return "SELECT table_name FROM information_schema.tables WHERE table_schema = '" + pgSchema + "';";
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
var cacheTablePrefixLength = Internal.cacheTablePrefix.length;
|
|
300
|
+
|
|
301
|
+
function makeSchemaCacheTableInfoQuery(pgSchema) {
|
|
302
|
+
return "SELECT \n t.table_name,\n get_cache_row_count(t.table_name) as count\n FROM information_schema.tables t\n WHERE t.table_schema = '" + pgSchema + "' \n AND t.table_name LIKE '" + Internal.cacheTablePrefix + "%';";
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
var psqlExecState = {
|
|
306
|
+
contents: "Unknown"
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
async function getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort) {
|
|
310
|
+
var promise = psqlExecState.contents;
|
|
311
|
+
if (typeof promise === "object") {
|
|
312
|
+
if (promise.TAG === "Pending") {
|
|
313
|
+
return await promise._0;
|
|
314
|
+
} else {
|
|
315
|
+
return promise._0;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
var promise$1 = new Promise((function (resolve, _reject) {
|
|
319
|
+
var binary = "psql";
|
|
320
|
+
Child_process.exec(binary + " --version", (function (error, param, param$1) {
|
|
321
|
+
if (error === null) {
|
|
322
|
+
return resolve({
|
|
323
|
+
TAG: "Ok",
|
|
324
|
+
_0: binary + " -h " + pgHost + " -p " + pgPort.toString() + " -U " + pgUser + " -d " + pgDatabase
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
var binary$1 = "docker-compose exec -T -u " + pgUser + " envio-postgres psql";
|
|
328
|
+
Child_process.exec(binary$1 + " --version", (function (error, param, param$1) {
|
|
329
|
+
if (error === null) {
|
|
330
|
+
return resolve({
|
|
331
|
+
TAG: "Ok",
|
|
332
|
+
_0: binary$1 + " -h " + pgHost + " -p " + (5432).toString() + " -U " + pgUser + " -d " + pgDatabase
|
|
333
|
+
});
|
|
334
|
+
} else {
|
|
335
|
+
return resolve({
|
|
336
|
+
TAG: "Error",
|
|
337
|
+
_0: "Please check if \"psql\" binary is installed or docker-compose is running for the local indexer."
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
}));
|
|
341
|
+
}));
|
|
342
|
+
}));
|
|
343
|
+
psqlExecState.contents = {
|
|
344
|
+
TAG: "Pending",
|
|
345
|
+
_0: promise$1
|
|
346
|
+
};
|
|
347
|
+
var result = await promise$1;
|
|
348
|
+
psqlExecState.contents = {
|
|
349
|
+
TAG: "Resolved",
|
|
350
|
+
_0: result
|
|
351
|
+
};
|
|
352
|
+
return result;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, onInitialize, onNewTables) {
|
|
356
|
+
var psqlExecOptions_env = Js_dict.fromArray([
|
|
357
|
+
[
|
|
358
|
+
"PGPASSWORD",
|
|
359
|
+
pgPassword
|
|
360
|
+
],
|
|
361
|
+
[
|
|
362
|
+
"PATH",
|
|
363
|
+
process.env.PATH
|
|
364
|
+
]
|
|
365
|
+
]);
|
|
366
|
+
var psqlExecOptions = {
|
|
367
|
+
env: psqlExecOptions_env
|
|
368
|
+
};
|
|
369
|
+
var cacheDirPath = Path.resolve("..", ".envio", "cache");
|
|
260
370
|
var isInitialized = async function () {
|
|
261
|
-
var
|
|
262
|
-
return Utils.$$Array.notEmpty(
|
|
371
|
+
var envioTables = await sql.unsafe("SELECT table_schema FROM information_schema.tables WHERE table_schema = '" + pgSchema + "' AND table_name = '" + eventSyncStateTableName + "';");
|
|
372
|
+
return Utils.$$Array.notEmpty(envioTables);
|
|
263
373
|
};
|
|
264
|
-
var initialize = async function (entitiesOpt, generalTablesOpt, enumsOpt
|
|
374
|
+
var initialize = async function (entitiesOpt, generalTablesOpt, enumsOpt) {
|
|
265
375
|
var entities = entitiesOpt !== undefined ? entitiesOpt : [];
|
|
266
376
|
var generalTables = generalTablesOpt !== undefined ? generalTablesOpt : [];
|
|
267
377
|
var enums = enumsOpt !== undefined ? enumsOpt : [];
|
|
268
|
-
var
|
|
269
|
-
|
|
378
|
+
var schemaTableNames = await sql.unsafe(makeSchemaTableNamesQuery(pgSchema));
|
|
379
|
+
if (Utils.$$Array.notEmpty(schemaTableNames) && !schemaTableNames.some(function (table) {
|
|
380
|
+
return table.table_name === eventSyncStateTableName;
|
|
381
|
+
})) {
|
|
382
|
+
Js_exn.raiseError("Cannot run Envio migrations on PostgreSQL schema \"" + pgSchema + "\" because it contains non-Envio tables. Running migrations would delete all data in this schema.\n\nTo resolve this:\n1. If you want to use this schema, first backup any important data, then drop it with: \"pnpm envio local db-migrate down\"\n2. Or specify a different schema name by setting the \"ENVIO_PG_PUBLIC_SCHEMA\" environment variable\n3. Or manually drop the schema in your database if you're certain the data is not needed.");
|
|
383
|
+
}
|
|
384
|
+
var queries = makeInitializeTransaction(pgSchema, pgUser, generalTables, entities, enums, Utils.$$Array.isEmpty(schemaTableNames));
|
|
270
385
|
await sql.begin(function (sql) {
|
|
271
386
|
return queries.map(function (query) {
|
|
272
387
|
return sql.unsafe(query);
|
|
273
388
|
});
|
|
274
389
|
});
|
|
390
|
+
if (onInitialize !== undefined) {
|
|
391
|
+
return await onInitialize();
|
|
392
|
+
}
|
|
393
|
+
|
|
275
394
|
};
|
|
276
395
|
var loadByIdsOrThrow = async function (ids, table, rowsSchema) {
|
|
277
396
|
var rows;
|
|
278
397
|
try {
|
|
279
398
|
rows = await (
|
|
280
|
-
ids.length !== 1 ? sql.unsafe(
|
|
399
|
+
ids.length !== 1 ? sql.unsafe(makeLoadByIdsQuery(pgSchema, table.tableName), [ids], {prepare: true}) : sql.unsafe(makeLoadByIdQuery(pgSchema, table.tableName), ids, {prepare: true})
|
|
281
400
|
);
|
|
282
401
|
}
|
|
283
402
|
catch (raw_exn){
|
|
@@ -302,32 +421,221 @@ function make(sql, pgSchema, pgUser) {
|
|
|
302
421
|
};
|
|
303
422
|
}
|
|
304
423
|
};
|
|
424
|
+
var loadByFieldOrThrow = async function (fieldName, fieldSchema, fieldValue, operator, table, rowsSchema) {
|
|
425
|
+
var params;
|
|
426
|
+
try {
|
|
427
|
+
params = [S$RescriptSchema.reverseConvertToJsonOrThrow(fieldValue, fieldSchema)];
|
|
428
|
+
}
|
|
429
|
+
catch (raw_exn){
|
|
430
|
+
var exn = Caml_js_exceptions.internalToOCamlException(raw_exn);
|
|
431
|
+
throw {
|
|
432
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
433
|
+
message: "Failed loading \"" + table.tableName + "\" from storage by field \"" + fieldName + "\". Couldn't serialize provided value.",
|
|
434
|
+
reason: exn,
|
|
435
|
+
Error: new Error()
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
var rows;
|
|
439
|
+
try {
|
|
440
|
+
rows = await sql.unsafe(makeLoadByFieldQuery(pgSchema, table.tableName, fieldName, operator), params, {prepare: true});
|
|
441
|
+
}
|
|
442
|
+
catch (raw_exn$1){
|
|
443
|
+
var exn$1 = Caml_js_exceptions.internalToOCamlException(raw_exn$1);
|
|
444
|
+
throw {
|
|
445
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
446
|
+
message: "Failed loading \"" + table.tableName + "\" from storage by field \"" + fieldName + "\"",
|
|
447
|
+
reason: exn$1,
|
|
448
|
+
Error: new Error()
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
try {
|
|
452
|
+
return S$RescriptSchema.parseOrThrow(rows, rowsSchema);
|
|
453
|
+
}
|
|
454
|
+
catch (raw_exn$2){
|
|
455
|
+
var exn$2 = Caml_js_exceptions.internalToOCamlException(raw_exn$2);
|
|
456
|
+
throw {
|
|
457
|
+
RE_EXN_ID: Persistence.StorageError,
|
|
458
|
+
message: "Failed to parse \"" + table.tableName + "\" loaded from storage by ids",
|
|
459
|
+
reason: exn$2,
|
|
460
|
+
Error: new Error()
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
};
|
|
305
464
|
var setOrThrow$1 = function (items, table, itemSchema) {
|
|
306
465
|
return setOrThrow(sql, items, table, itemSchema, pgSchema);
|
|
307
466
|
};
|
|
467
|
+
var setEffectCacheOrThrow = async function (effect, items, initialize) {
|
|
468
|
+
var cacheMeta = effect.cache;
|
|
469
|
+
var match = cacheMeta !== undefined ? cacheMeta : Js_exn.raiseError("Failed to set effect cache for \"" + effect.name + "\". Effect has no cache enabled.");
|
|
470
|
+
var table = match.table;
|
|
471
|
+
if (initialize) {
|
|
472
|
+
await sql.unsafe(makeCreateTableQuery(table, pgSchema));
|
|
473
|
+
if (onNewTables !== undefined) {
|
|
474
|
+
await onNewTables([table.tableName]);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
}
|
|
478
|
+
return await setOrThrow$1(items, table, match.itemSchema);
|
|
479
|
+
};
|
|
480
|
+
var dumpEffectCache = async function () {
|
|
481
|
+
try {
|
|
482
|
+
var cacheTableInfo = (await sql.unsafe(makeSchemaCacheTableInfoQuery(pgSchema))).filter(function (i) {
|
|
483
|
+
return i.count > 0;
|
|
484
|
+
});
|
|
485
|
+
if (!Utils.$$Array.notEmpty(cacheTableInfo)) {
|
|
486
|
+
return ;
|
|
487
|
+
}
|
|
488
|
+
try {
|
|
489
|
+
await Fs.promises.access(cacheDirPath);
|
|
490
|
+
}
|
|
491
|
+
catch (exn){
|
|
492
|
+
await Fs.promises.mkdir(cacheDirPath, {
|
|
493
|
+
recursive: true
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
var psqlExec = await getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort);
|
|
497
|
+
if (psqlExec.TAG !== "Ok") {
|
|
498
|
+
return Logging.error("Failed to dump cache. " + psqlExec._0);
|
|
499
|
+
}
|
|
500
|
+
var psqlExec$1 = psqlExec._0;
|
|
501
|
+
Logging.info("Dumping cache: " + cacheTableInfo.map(function (param) {
|
|
502
|
+
return param.table_name + " (" + String(param.count) + " rows)";
|
|
503
|
+
}).join(", "));
|
|
504
|
+
var promises = cacheTableInfo.map(async function (param) {
|
|
505
|
+
var tableName = param.table_name;
|
|
506
|
+
var cacheName = tableName.slice(cacheTablePrefixLength);
|
|
507
|
+
var outputFile = Path.join(cacheDirPath, cacheName + ".tsv");
|
|
508
|
+
var command = psqlExec$1 + " -c 'COPY \"" + pgSchema + "\".\"" + tableName + "\" TO STDOUT WITH (FORMAT text, HEADER);' > " + outputFile;
|
|
509
|
+
return new Promise((function (resolve, reject) {
|
|
510
|
+
Child_process.exec(command, psqlExecOptions, (function (error, stdout, param) {
|
|
511
|
+
if (error === null) {
|
|
512
|
+
return resolve(stdout);
|
|
513
|
+
} else {
|
|
514
|
+
return reject(error);
|
|
515
|
+
}
|
|
516
|
+
}));
|
|
517
|
+
}));
|
|
518
|
+
});
|
|
519
|
+
await Promise.all(promises);
|
|
520
|
+
return Logging.info("Successfully dumped cache to " + cacheDirPath);
|
|
521
|
+
}
|
|
522
|
+
catch (raw_exn){
|
|
523
|
+
var exn$1 = Caml_js_exceptions.internalToOCamlException(raw_exn);
|
|
524
|
+
return Logging.errorWithExn(Internal.prettifyExn(exn$1), "Failed to dump cache.");
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
var restoreEffectCache = async function (withUpload) {
|
|
528
|
+
if (withUpload) {
|
|
529
|
+
var nothingToUploadErrorMessage = "Nothing to upload.";
|
|
530
|
+
var match = await Promise.all([
|
|
531
|
+
$$Promise.$$catch(Fs.promises.readdir(cacheDirPath).then(function (e) {
|
|
532
|
+
return {
|
|
533
|
+
TAG: "Ok",
|
|
534
|
+
_0: e
|
|
535
|
+
};
|
|
536
|
+
}), (function (param) {
|
|
537
|
+
return Promise.resolve({
|
|
538
|
+
TAG: "Error",
|
|
539
|
+
_0: nothingToUploadErrorMessage
|
|
540
|
+
});
|
|
541
|
+
})),
|
|
542
|
+
getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort)
|
|
543
|
+
]);
|
|
544
|
+
var exit = 0;
|
|
545
|
+
var message;
|
|
546
|
+
var entries = match[0];
|
|
547
|
+
if (entries.TAG === "Ok") {
|
|
548
|
+
var psqlExec = match[1];
|
|
549
|
+
if (psqlExec.TAG === "Ok") {
|
|
550
|
+
var psqlExec$1 = psqlExec._0;
|
|
551
|
+
var cacheFiles = entries._0.filter(function (entry) {
|
|
552
|
+
return entry.endsWith(".tsv");
|
|
553
|
+
});
|
|
554
|
+
await Promise.all(cacheFiles.map(function (entry) {
|
|
555
|
+
var effectName = entry.slice(0, -4);
|
|
556
|
+
var table = Internal.makeCacheTable(effectName);
|
|
557
|
+
return sql.unsafe(makeCreateTableQuery(table, pgSchema)).then(function () {
|
|
558
|
+
var inputFile = Path.join(cacheDirPath, entry);
|
|
559
|
+
var command = psqlExec$1 + " -c 'COPY \"" + pgSchema + "\".\"" + table.tableName + "\" FROM STDIN WITH (FORMAT text, HEADER);' < " + inputFile;
|
|
560
|
+
return new Promise((function (resolve, reject) {
|
|
561
|
+
Child_process.exec(command, psqlExecOptions, (function (error, stdout, param) {
|
|
562
|
+
if (error === null) {
|
|
563
|
+
return resolve(stdout);
|
|
564
|
+
} else {
|
|
565
|
+
return reject(error);
|
|
566
|
+
}
|
|
567
|
+
}));
|
|
568
|
+
}));
|
|
569
|
+
});
|
|
570
|
+
}));
|
|
571
|
+
Logging.info("Successfully uploaded cache.");
|
|
572
|
+
} else {
|
|
573
|
+
message = match[1]._0;
|
|
574
|
+
exit = 1;
|
|
575
|
+
}
|
|
576
|
+
} else {
|
|
577
|
+
message = entries._0;
|
|
578
|
+
exit = 1;
|
|
579
|
+
}
|
|
580
|
+
if (exit === 1) {
|
|
581
|
+
if (message === nothingToUploadErrorMessage) {
|
|
582
|
+
Logging.info("No cache found to upload.");
|
|
583
|
+
} else {
|
|
584
|
+
Logging.error("Failed to upload cache, continuing without it. " + message);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
}
|
|
589
|
+
var cacheTableInfo = await sql.unsafe(makeSchemaCacheTableInfoQuery(pgSchema));
|
|
590
|
+
if (withUpload && Utils.$$Array.notEmpty(cacheTableInfo) && onNewTables !== undefined) {
|
|
591
|
+
await onNewTables(cacheTableInfo.map(function (info) {
|
|
592
|
+
return info.table_name;
|
|
593
|
+
}));
|
|
594
|
+
}
|
|
595
|
+
return cacheTableInfo.map(function (info) {
|
|
596
|
+
return {
|
|
597
|
+
effectName: info.table_name.slice(cacheTablePrefixLength),
|
|
598
|
+
count: info.count
|
|
599
|
+
};
|
|
600
|
+
});
|
|
601
|
+
};
|
|
308
602
|
return {
|
|
309
603
|
isInitialized: isInitialized,
|
|
310
604
|
initialize: initialize,
|
|
311
605
|
loadByIdsOrThrow: loadByIdsOrThrow,
|
|
312
|
-
|
|
606
|
+
loadByFieldOrThrow: loadByFieldOrThrow,
|
|
607
|
+
setOrThrow: setOrThrow$1,
|
|
608
|
+
setEffectCacheOrThrow: setEffectCacheOrThrow,
|
|
609
|
+
dumpEffectCache: dumpEffectCache,
|
|
610
|
+
restoreEffectCache: restoreEffectCache
|
|
313
611
|
};
|
|
314
612
|
}
|
|
315
613
|
|
|
316
614
|
var maxItemsPerQuery = 500;
|
|
317
615
|
|
|
318
|
-
exports.
|
|
319
|
-
exports.
|
|
320
|
-
exports.
|
|
616
|
+
exports.makeCreateIndexQuery = makeCreateIndexQuery;
|
|
617
|
+
exports.makeCreateTableIndicesQuery = makeCreateTableIndicesQuery;
|
|
618
|
+
exports.makeCreateTableQuery = makeCreateTableQuery;
|
|
321
619
|
exports.makeInitializeTransaction = makeInitializeTransaction;
|
|
322
|
-
exports.
|
|
323
|
-
exports.
|
|
324
|
-
exports.
|
|
325
|
-
exports.
|
|
620
|
+
exports.makeLoadByIdQuery = makeLoadByIdQuery;
|
|
621
|
+
exports.makeLoadByFieldQuery = makeLoadByFieldQuery;
|
|
622
|
+
exports.makeLoadByIdsQuery = makeLoadByIdsQuery;
|
|
623
|
+
exports.makeInsertUnnestSetQuery = makeInsertUnnestSetQuery;
|
|
624
|
+
exports.makeInsertValuesSetQuery = makeInsertValuesSetQuery;
|
|
326
625
|
exports.rawEventsTableName = rawEventsTableName;
|
|
626
|
+
exports.eventSyncStateTableName = eventSyncStateTableName;
|
|
327
627
|
exports.maxItemsPerQuery = maxItemsPerQuery;
|
|
328
628
|
exports.makeTableBatchSetQuery = makeTableBatchSetQuery;
|
|
329
629
|
exports.chunkArray = chunkArray;
|
|
630
|
+
exports.removeInvalidUtf8InPlace = removeInvalidUtf8InPlace;
|
|
631
|
+
exports.pgEncodingErrorSchema = pgEncodingErrorSchema;
|
|
632
|
+
exports.PgEncodingError = PgEncodingError;
|
|
330
633
|
exports.setQueryCache = setQueryCache;
|
|
331
634
|
exports.setOrThrow = setOrThrow;
|
|
635
|
+
exports.setEntityHistoryOrThrow = setEntityHistoryOrThrow;
|
|
636
|
+
exports.makeSchemaTableNamesQuery = makeSchemaTableNamesQuery;
|
|
637
|
+
exports.cacheTablePrefixLength = cacheTablePrefixLength;
|
|
638
|
+
exports.makeSchemaCacheTableInfoQuery = makeSchemaCacheTableInfoQuery;
|
|
639
|
+
exports.getConnectedPsqlExec = getConnectedPsqlExec;
|
|
332
640
|
exports.make = make;
|
|
333
|
-
/*
|
|
641
|
+
/* pgEncodingErrorSchema Not a pure module */
|
package/src/Prometheus.res
CHANGED
|
@@ -590,3 +590,15 @@ module EffectCallsCount = {
|
|
|
590
590
|
gauge->SafeGauge.handleInt(~labels=effectName, ~value=callsCount)
|
|
591
591
|
}
|
|
592
592
|
}
|
|
593
|
+
|
|
594
|
+
module EffectCacheCount = {
|
|
595
|
+
let gauge = SafeGauge.makeOrThrow(
|
|
596
|
+
~name="envio_effect_cache_count",
|
|
597
|
+
~help="The number of items in the effect cache.",
|
|
598
|
+
~labelSchema=effectLabelsSchema,
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
let set = (~count, ~effectName) => {
|
|
602
|
+
gauge->SafeGauge.handleInt(~labels=effectName, ~value=count)
|
|
603
|
+
}
|
|
604
|
+
}
|
package/src/Prometheus.res.js
CHANGED
|
@@ -700,6 +700,17 @@ var EffectCallsCount = {
|
|
|
700
700
|
set: set$21
|
|
701
701
|
};
|
|
702
702
|
|
|
703
|
+
var gauge$22 = makeOrThrow$1("envio_effect_cache_count", "The number of items in the effect cache.", effectLabelsSchema);
|
|
704
|
+
|
|
705
|
+
function set$22(count, effectName) {
|
|
706
|
+
handleInt$1(gauge$22, effectName, count);
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
var EffectCacheCount = {
|
|
710
|
+
gauge: gauge$22,
|
|
711
|
+
set: set$22
|
|
712
|
+
};
|
|
713
|
+
|
|
703
714
|
exports.loadEntitiesDurationCounter = loadEntitiesDurationCounter;
|
|
704
715
|
exports.eventRouterDurationCounter = eventRouterDurationCounter;
|
|
705
716
|
exports.executeBatchDurationCounter = executeBatchDurationCounter;
|
|
@@ -747,4 +758,5 @@ exports.ProgressBlockNumber = ProgressBlockNumber;
|
|
|
747
758
|
exports.ProgressEventsCount = ProgressEventsCount;
|
|
748
759
|
exports.effectLabelsSchema = effectLabelsSchema;
|
|
749
760
|
exports.EffectCallsCount = EffectCallsCount;
|
|
761
|
+
exports.EffectCacheCount = EffectCacheCount;
|
|
750
762
|
/* loadEntitiesDurationCounter Not a pure module */
|