envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
1,209 lines (1,165 loc) • 52.9 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as Fs from "fs";
import * as Env from "./Env.res.mjs";
import * as Sink from "./Sink.res.mjs";
import * as Path from "path";
import * as Table from "./db/Table.res.mjs";
import * as Utils from "./Utils.res.mjs";
import * as Config from "./Config.res.mjs";
import * as Hasura from "./Hasura.res.mjs";
import * as Hrtime from "./bindings/Hrtime.res.mjs";
import * as Schema from "./db/Schema.res.mjs";
import * as Logging from "./Logging.res.mjs";
import * as Internal from "./Internal.res.mjs";
import Postgres from "postgres";
import * as EventUtils from "./EventUtils.res.mjs";
import * as Prometheus from "./Prometheus.res.mjs";
import * as Persistence from "./Persistence.res.mjs";
import * as ChainFetcher from "./ChainFetcher.res.mjs";
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
import * as EntityHistory from "./db/EntityHistory.res.mjs";
import * as InternalTable from "./db/InternalTable.res.mjs";
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
import * as Child_process from "child_process";
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
import * as Stdlib_Promise from "@rescript/runtime/lib/es6/Stdlib_Promise.js";
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
import * as Primitive_option from "@rescript/runtime/lib/es6/Primitive_option.js";
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
let getCacheRowCountFnName = "get_cache_row_count";
function makeClient() {
return Postgres({
host: Env.Db.host,
port: Env.Db.port,
database: Env.Db.database,
username: Env.Db.user,
password: Env.Db.password,
ssl: Env.Db.ssl,
max: Env.Db.maxConnections,
onnotice: Primitive_object.equal(Env.userLogLevel, "warn") || Primitive_object.equal(Env.userLogLevel, "error") ? undefined : _str => {},
transform: {
undefined: null
}
});
}
function makeCreateIndexQuery(tableName, indexFields, pgSchema) {
let indexName = tableName + "_" + indexFields.join("_");
let index = indexFields.map(idx => `"` + idx + `"`).join(", ");
return `CREATE INDEX IF NOT EXISTS "` + indexName + `" ON "` + pgSchema + `"."` + tableName + `"(` + index + `);`;
}
function directionToSql(direction) {
if (direction === "Asc") {
return "";
} else {
return " DESC";
}
}
function directionToIndexName(direction) {
if (direction === "Asc") {
return "";
} else {
return "_desc";
}
}
function makeCreateCompositeIndexQuery(tableName, indexFields, pgSchema) {
let indexName = tableName + "_" + indexFields.map(f => f.fieldName + directionToIndexName(f.direction)).join("_");
let index = indexFields.map(f => `"` + f.fieldName + `"` + directionToSql(f.direction)).join(", ");
return `CREATE INDEX IF NOT EXISTS "` + indexName + `" ON "` + pgSchema + `"."` + tableName + `"(` + index + `);`;
}
function makeCreateTableIndicesQuery(table, pgSchema) {
let tableName = table.tableName;
let createIndex = indexField => makeCreateIndexQuery(tableName, [indexField], pgSchema);
let createCompositeIndex = indexFields => makeCreateCompositeIndexQuery(tableName, indexFields, pgSchema);
let singleIndices = Table.getSingleIndices(table);
let compositeIndices = Table.getCompositeIndices(table);
return singleIndices.map(createIndex).join("\n") + compositeIndices.map(createCompositeIndex).join("\n");
}
function makeCreateTableQuery(table, pgSchema, isNumericArrayAsText) {
let fieldsMapped = Table.getFields(table).map(field => {
let defaultValue = field.defaultValue;
let isNullable = field.isNullable;
let fieldName = Table.getPgDbFieldName(field);
return `"` + fieldName + `" ` + Table.getPgFieldType(field.fieldType, pgSchema, field.isArray, isNumericArrayAsText, isNullable) + (
defaultValue !== undefined ? ` DEFAULT ` + defaultValue : (
isNullable ? `` : ` NOT NULL`
)
);
}).join(", ");
let primaryKeyFieldNames = Table.getPgPrimaryKeyFieldNames(table);
let primaryKey = primaryKeyFieldNames.map(field => `"` + field + `"`).join(", ");
return `CREATE TABLE IF NOT EXISTS "` + pgSchema + `"."` + table.tableName + `"(` + fieldsMapped + (
primaryKeyFieldNames.length !== 0 ? `, PRIMARY KEY(` + primaryKey + `)` : ""
) + `);`;
}
let entityHistoryCache = new WeakMap();
function getEntityHistory(entityConfig) {
let cache = entityHistoryCache.get(entityConfig);
if (cache !== undefined) {
return cache;
}
let dataFields = Stdlib_Array.filterMap(entityConfig.table.fields, field => {
if (field.TAG !== "Field") {
return;
}
let field$1 = field._0;
let match = field$1.fieldName;
if (match === "id") {
return {
TAG: "Field",
_0: {
fieldName: "id",
fieldType: field$1.fieldType,
fieldSchema: field$1.fieldSchema,
isArray: field$1.isArray,
isNullable: field$1.isNullable,
isPrimaryKey: true,
isIndex: field$1.isIndex,
linkedEntity: field$1.linkedEntity,
defaultValue: field$1.defaultValue,
description: field$1.description,
postgresDbName: field$1.postgresDbName,
clickhouseDbName: field$1.clickhouseDbName
}
};
} else {
return {
TAG: "Field",
_0: {
fieldName: field$1.fieldName,
fieldType: field$1.fieldType,
fieldSchema: field$1.fieldSchema,
isArray: field$1.isArray,
isNullable: true,
isPrimaryKey: field$1.isPrimaryKey,
isIndex: false,
linkedEntity: field$1.linkedEntity,
defaultValue: field$1.defaultValue,
description: field$1.description,
postgresDbName: field$1.postgresDbName,
clickhouseDbName: field$1.clickhouseDbName
}
};
}
});
let actionField = Table.mkField(EntityHistory.changeFieldName, EntityHistory.changeFieldType, S$RescriptSchema.never, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined);
let checkpointIdField = Table.mkField(EntityHistory.checkpointIdFieldName, EntityHistory.checkpointIdFieldType, EntityHistory.unsafeCheckpointIdSchema, undefined, undefined, undefined, true, undefined, undefined, undefined, undefined, undefined);
let entityTableName = entityConfig.table.tableName;
let historyTableName = EntityHistory.historyTableName(entityTableName, entityConfig.index);
let table = Table.mkTable(historyTableName, undefined, dataFields.concat([
checkpointIdField,
actionField
]), undefined);
let setChangeSchema = EntityHistory.makeSetUpdateSchema(entityConfig.schema);
let cache_setChangeSchemaRows = S$RescriptSchema.array(setChangeSchema);
let cache$1 = {
table: table,
setChangeSchema: setChangeSchema,
setChangeSchemaRows: cache_setChangeSchemaRows
};
entityHistoryCache.set(entityConfig, cache$1);
return cache$1;
}
function makeInitializeTransaction(pgSchema, pgUser, isHasuraEnabled, chainConfigsOpt, entitiesOpt, enumsOpt, isEmptyPgSchemaOpt) {
let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
let entities = entitiesOpt !== undefined ? entitiesOpt : [];
let enums = enumsOpt !== undefined ? enumsOpt : [];
let isEmptyPgSchema = isEmptyPgSchemaOpt !== undefined ? isEmptyPgSchemaOpt : false;
let generalTables = [
InternalTable.Chains.table,
InternalTable.EnvioInfo.table,
InternalTable.Checkpoints.table,
InternalTable.RawEvents.table
];
let allTables = generalTables.slice();
let allEntityTables = [];
entities.forEach(entityConfig => {
allEntityTables.push(entityConfig.table);
allTables.push(entityConfig.table);
allTables.push(getEntityHistory(entityConfig).table);
});
let derivedSchema = Schema.make(allEntityTables);
let query = {
contents: (
isEmptyPgSchema && pgSchema === "public" ? `CREATE SCHEMA IF NOT EXISTS "` + pgSchema + `";\n` : `DROP SCHEMA IF EXISTS "` + pgSchema + `" CASCADE;
CREATE SCHEMA "` + pgSchema + `";\n`
) + (`GRANT ALL ON SCHEMA "` + pgSchema + `" TO "` + pgUser + `";
GRANT ALL ON SCHEMA "` + pgSchema + `" TO public;`)
};
enums.forEach(enumConfig => {
let enumCreateQuery = `CREATE TYPE "` + pgSchema + `".` + enumConfig.name + ` AS ENUM(` + enumConfig.variants.map(v => `'` + v + `'`).join(", ") + `);`;
query.contents = query.contents + "\n" + enumCreateQuery;
});
allTables.forEach(table => {
query.contents = query.contents + "\n" + makeCreateTableQuery(table, pgSchema, isHasuraEnabled);
});
allTables.forEach(table => {
let indices = makeCreateTableIndicesQuery(table, pgSchema);
if (indices !== "") {
query.contents = query.contents + "\n" + indices;
return;
}
});
entities.forEach(entity => {
Table.getDerivedFromFields(entity.table).forEach(derivedFromField => {
let indexField = Utils.unwrapResultExn(Schema.getDerivedFromPgFieldName(derivedSchema, derivedFromField));
query.contents = query.contents + "\n" + makeCreateIndexQuery(derivedFromField.derivedFromEntity, [indexField], pgSchema);
});
});
query.contents = query.contents + "\n" + InternalTable.Views.makeMetaViewQuery(pgSchema);
query.contents = query.contents + "\n" + InternalTable.Views.makeChainMetadataViewQuery(pgSchema);
let initialChainsValuesQuery = InternalTable.Chains.makeInitialValuesQuery(pgSchema, chainConfigs);
if (initialChainsValuesQuery !== undefined) {
query.contents = query.contents + "\n" + initialChainsValuesQuery;
}
return [
query.contents,
`CREATE OR REPLACE FUNCTION ` + getCacheRowCountFnName + `(table_name text)
RETURNS integer AS $$
DECLARE
result integer;
BEGIN
EXECUTE format('SELECT COUNT(*) FROM "` + pgSchema + `".%I', table_name) INTO result;
RETURN result;
END;
$$ LANGUAGE plpgsql;`
];
}
function makeLoadQuery(pgSchema, tableName, condition) {
return `SELECT * FROM "` + pgSchema + `"."` + tableName + `" WHERE ` + condition + `;`;
}
function makeFilterCondition(filter, table, params) {
let getQueryFieldOrThrow = fieldName => {
let queryField = Table.queryFields(table)[fieldName];
if (queryField !== undefined) {
return queryField;
}
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed loading "` + table.tableName + `" from storage. The table doesn't have the field "` + fieldName + `".`,
reason: {
RE_EXN_ID: Table.NonExistingTableField,
_1: fieldName
},
Error: new Error()
};
};
let serializeParamOrThrow = (queryField, fieldName, fieldValue, isArray) => {
let param;
try {
param = S$RescriptSchema.reverseConvertToJsonOrThrow(fieldValue, isArray ? queryField.arrayFieldSchema : queryField.fieldSchema);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed loading "` + table.tableName + `" from storage by field "` + fieldName + `". Couldn't serialize provided value.`,
reason: exn,
Error: new Error()
};
}
params.push(param);
return `$` + params.length.toString();
};
let scalarCondition = (fieldName, fieldValue, op) => {
let queryField = getQueryFieldOrThrow(fieldName);
return `"` + queryField.pgDbFieldName + `" ` + op + ` ` + serializeParamOrThrow(queryField, fieldName, fieldValue, false);
};
switch (filter.operator) {
case "=" :
return scalarCondition(filter.fieldName, filter.fieldValue, "=");
case ">" :
return scalarCondition(filter.fieldName, filter.fieldValue, ">");
case "<" :
return scalarCondition(filter.fieldName, filter.fieldValue, "<");
case "in" :
let fieldName = filter.fieldName;
let queryField = getQueryFieldOrThrow(fieldName);
return `"` + queryField.pgDbFieldName + `" = ANY(` + serializeParamOrThrow(queryField, fieldName, filter.fieldValue, true) + `)`;
case "and" :
let filters = filter.filters;
if (filters.length !== 0) {
return `(` + filters.map(filter => makeFilterCondition(filter, table, params)).join(" AND ") + `)`;
}
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed loading "` + table.tableName + `" from storage. The "and" filter must contain at least one nested filter.`,
reason: new Error(`Empty "and" filter`),
Error: new Error()
};
}
}
function makeDeleteByIdQuery(pgSchema, tableName) {
return `DELETE FROM "` + pgSchema + `"."` + tableName + `" WHERE id = $1;`;
}
function makeDeleteByIdsQuery(pgSchema, tableName) {
return `DELETE FROM "` + pgSchema + `"."` + tableName + `" WHERE id = ANY($1::text[]);`;
}
function makeLoadAllQuery(pgSchema, tableName) {
return `SELECT * FROM "` + pgSchema + `"."` + tableName + `";`;
}
function makeInsertUnnestSetQuery(pgSchema, table, itemSchema, isRawEvents) {
let match = Table.toSqlParams(table, itemSchema, pgSchema);
let quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames;
let primaryKeyFieldNames = Table.getPgPrimaryKeyFieldNames(table);
return `INSERT INTO "` + pgSchema + `"."` + table.tableName + `" (` + match.quotedFieldNames.join(", ") + `)
SELECT * FROM unnest(` + match.arrayFieldTypes.map((arrayFieldType, idx) => `$` + (idx + 1 | 0).toString() + `::` + arrayFieldType).join(",") + `)` + (
isRawEvents || primaryKeyFieldNames.length === 0 ? `` : `ON CONFLICT(` + primaryKeyFieldNames.map(fieldName => `"` + fieldName + `"`).join(",") + `) DO ` + (
Utils.$$Array.isEmpty(quotedNonPrimaryFieldNames) ? `NOTHING` : `UPDATE SET ` + quotedNonPrimaryFieldNames.map(fieldName => fieldName + ` = EXCLUDED.` + fieldName).join(",")
)
) + ";";
}
function makeInsertValuesSetQuery(pgSchema, table, itemSchema, itemsCount) {
let match = Table.toSqlParams(table, itemSchema, pgSchema);
let quotedNonPrimaryFieldNames = match.quotedNonPrimaryFieldNames;
let quotedFieldNames = match.quotedFieldNames;
let primaryKeyFieldNames = Table.getPgPrimaryKeyFieldNames(table);
let fieldsCount = quotedFieldNames.length;
let placeholders = "";
for (let idx = 1; idx <= itemsCount; ++idx) {
if (idx > 1) {
placeholders = placeholders + ",";
}
placeholders = placeholders + "(";
for (let fieldIdx = 0; fieldIdx < fieldsCount; ++fieldIdx) {
if (fieldIdx > 0) {
placeholders = placeholders + ",";
}
placeholders = placeholders + (`$` + ((fieldIdx * itemsCount | 0) + idx | 0).toString());
}
placeholders = placeholders + ")";
}
return `INSERT INTO "` + pgSchema + `"."` + table.tableName + `" (` + quotedFieldNames.join(", ") + `)
VALUES` + placeholders + (
primaryKeyFieldNames.length !== 0 ? `ON CONFLICT(` + primaryKeyFieldNames.map(fieldName => `"` + fieldName + `"`).join(",") + `) DO ` + (
Utils.$$Array.isEmpty(quotedNonPrimaryFieldNames) ? `NOTHING` : `UPDATE SET ` + quotedNonPrimaryFieldNames.map(fieldName => fieldName + ` = EXCLUDED.` + fieldName).join(",")
) : ``
) + ";";
}
function makeTableBatchSetQuery(pgSchema, table, itemSchema) {
let match = Table.toSqlParams(table, itemSchema, pgSchema);
let isRawEvents = table.tableName === InternalTable.RawEvents.table.tableName;
let isHistoryUpdate = table.tableName.startsWith(EntityHistory.historyTablePrefix);
if ((isRawEvents || !match.hasArrayField) && !isHistoryUpdate) {
return {
query: makeInsertUnnestSetQuery(pgSchema, table, itemSchema, isRawEvents),
convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.unnest(match.dbSchema), "Output", "Input", "Sync", false),
isInsertValues: false
};
} else {
return {
query: makeInsertValuesSetQuery(pgSchema, table, itemSchema, 500),
convertOrThrow: S$RescriptSchema.compile(S$RescriptSchema.preprocess(S$RescriptSchema.unnest(itemSchema), param => ({
s: prim => prim.flat(1)
})), "Output", "Input", "Sync", false),
isInsertValues: true
};
}
}
function chunkArray(arr, chunkSize) {
let chunks = [];
let i = 0;
while (i < arr.length) {
let chunk = arr.slice(i, i + chunkSize | 0);
chunks.push(chunk);
i = i + chunkSize | 0;
};
return chunks;
}
function removeInvalidUtf8DeepInPlace(value) {
if (typeof value === "string") {
return value.replaceAll("\x00", "");
} else if (typeof value === "object" && value !== null) {
Utils.Dict.forEachWithKey(value, (v, k) => {
value[k] = removeInvalidUtf8DeepInPlace(v);
});
return value;
} else {
return value;
}
}
function removeInvalidUtf8InPlace(items) {
items.forEach(item => {
removeInvalidUtf8DeepInPlace(item);
});
}
let pgErrorMessageSchema = S$RescriptSchema.object(s => s.f("message", S$RescriptSchema.string));
let PgEncodingError = /* @__PURE__ */Primitive_exceptions.create("PgStorage.PgEncodingError");
function classifyWriteError(specificError, table, exn) {
let normalizedExn = Primitive_exceptions.internalToException(exn.RE_EXN_ID === "JsExn" || exn.RE_EXN_ID !== Persistence.StorageError ? exn : exn.reason);
if (normalizedExn.RE_EXN_ID === "JsExn") {
let val;
try {
val = S$RescriptSchema.parseOrThrow(normalizedExn._1, pgErrorMessageSchema);
} catch (exn$1) {
return;
}
switch (val) {
case "current transaction is aborted, commands ignored until end of transaction block" :
return;
case "invalid byte sequence for encoding \"UTF8\": 0x00" :
case "unsupported Unicode escape sequence" :
specificError.contents = {
RE_EXN_ID: PgEncodingError,
table: table
};
return;
default:
specificError.contents = Utils.prettifyExn(exn);
return;
}
} else {
if (normalizedExn.RE_EXN_ID !== S$RescriptSchema.Raised) {
return;
}
throw normalizedExn;
}
}
let setQueryCache = new WeakMap();
async function setOrThrow(sql, items, table, itemSchema, pgSchema) {
if (items.length === 0) {
return;
}
let cached = setQueryCache.get(table);
let data;
if (cached !== undefined) {
data = Primitive_option.valFromOption(cached);
} else {
let newQuery = makeTableBatchSetQuery(pgSchema, table, itemSchema);
setQueryCache.set(table, newQuery);
data = newQuery;
}
try {
if (!data.isInsertValues) {
return await sql.unsafe(data.query, data.convertOrThrow(items), {prepare: true});
}
let chunks = chunkArray(items, 500);
let responses = [];
chunks.forEach(chunk => {
let chunkSize = chunk.length;
let isFullChunk = chunkSize === 500;
let params = data.convertOrThrow(chunk);
let response = isFullChunk ? sql.unsafe(data.query, params, {prepare: true}) : sql.unsafe(makeInsertValuesSetQuery(pgSchema, table, itemSchema, chunkSize), params);
responses.push(response);
});
await Promise.all(responses);
return;
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
if (exn.RE_EXN_ID === S$RescriptSchema.Raised) {
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed to convert items for table "` + table.tableName + `"`,
reason: exn,
Error: new Error()
};
}
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed to insert items into table "` + table.tableName + `"`,
reason: Utils.prettifyExn(exn),
Error: new Error()
};
}
}
function makeSchemaTableNamesQuery(pgSchema) {
return `SELECT table_name FROM information_schema.tables WHERE table_schema = '` + pgSchema + `';`;
}
let cacheTablePrefixLength = Internal.cacheTablePrefix.length;
function makeSchemaCacheTableInfoQuery(pgSchema) {
return `SELECT
t.table_name,
` + getCacheRowCountFnName + `(t.table_name) as count
FROM information_schema.tables t
WHERE t.table_schema = '` + pgSchema + `'
AND t.table_name LIKE '` + Internal.cacheTablePrefix + `%';`;
}
let psqlExecState = {
contents: "Unknown"
};
async function getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort, containerName) {
let promise = psqlExecState.contents;
if (typeof promise === "object") {
if (promise.TAG === "Pending") {
return await promise._0;
} else {
return promise._0;
}
}
let promise$1 = new Promise((resolve, _reject) => {
let binary = "psql";
Child_process.exec(binary + ` --version`, (error, param, param$1) => {
if (error === null) {
return resolve({
TAG: "Ok",
_0: binary + ` -h ` + pgHost + ` -p ` + pgPort.toString() + ` -U ` + pgUser + ` -d ` + pgDatabase
});
}
let binary$1 = `docker exec -i -u ` + pgUser + ` ` + containerName + ` psql`;
Child_process.exec(binary$1 + ` --version`, (error, param, param$1) => {
if (error === null) {
return resolve({
TAG: "Ok",
_0: binary$1 + ` -h ` + pgHost + ` -p ` + (5432).toString() + ` -U ` + pgUser + ` -d ` + pgDatabase
});
} else {
return resolve({
TAG: "Error",
_0: `Please check if "psql" binary is installed or Docker container "` + containerName + `" is running.`
});
}
});
});
});
psqlExecState.contents = {
TAG: "Pending",
_0: promise$1
};
let result = await promise$1;
psqlExecState.contents = {
TAG: "Resolved",
_0: result
};
return result;
}
async function deleteByIdsOrThrow(sql, pgSchema, ids, table) {
try {
await (
ids.length !== 1 ? sql.unsafe(makeDeleteByIdsQuery(pgSchema, table.tableName), [ids], {prepare: true}) : sql.unsafe(makeDeleteByIdQuery(pgSchema, table.tableName), ids, {prepare: true})
);
return;
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed deleting "` + table.tableName + `" from storage by ids`,
reason: exn,
Error: new Error()
};
}
}
function makeInsertDeleteUpdatesQuery(entityConfig, pgSchema) {
let historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
let allHistoryFieldNames = Stdlib_Array.filterMap(entityConfig.table.fields, fieldOrDerived => {
if (fieldOrDerived.TAG === "Field") {
return Table.getPgDbFieldName(fieldOrDerived._0);
}
});
allHistoryFieldNames.push(EntityHistory.checkpointIdFieldName);
allHistoryFieldNames.push(EntityHistory.changeFieldName);
let allHistoryFieldNamesStr = allHistoryFieldNames.map(name => `"` + name + `"`).join(", ");
let selectParts = allHistoryFieldNames.map(fieldName => {
if (fieldName === Table.idFieldName) {
return `u.` + Table.idFieldName;
} else if (fieldName === EntityHistory.checkpointIdFieldName) {
return `u.` + EntityHistory.checkpointIdFieldName;
} else if (fieldName === EntityHistory.changeFieldName) {
return `'` + "DELETE" + `'`;
} else {
return "NULL";
}
});
let selectPartsStr = selectParts.join(", ");
let checkpointIdPgType = Table.getPgFieldType(EntityHistory.checkpointIdFieldType, pgSchema, false, false, false);
return `INSERT INTO "` + pgSchema + `"."` + historyTableName + `" (` + allHistoryFieldNamesStr + `)
SELECT ` + selectPartsStr + `
FROM UNNEST($1::text[], $2::` + checkpointIdPgType + `[]) AS u(` + Table.idFieldName + `, ` + EntityHistory.checkpointIdFieldName + `)`;
}
function executeSet(sql, items, dbFunction) {
if (items.length !== 0) {
return dbFunction(sql, items);
} else {
return Promise.resolve();
}
}
function convertFieldsToJson(fields) {
if (fields !== undefined) {
return Utils.Dict.mapValues(fields, value => {
if (typeof value === "bigint") {
return value.toString();
} else {
return value;
}
});
} else {
return {};
}
}
function makeRawEvent(eventItem, config) {
let event = eventItem.event;
let blockNumber = eventItem.blockNumber;
let eventConfig = eventItem.eventConfig;
let logIndex = event.logIndex;
let eventId = EventUtils.packEventIndex(blockNumber, logIndex);
let blockFields = convertFieldsToJson(event.block);
let transactionFields = convertFieldsToJson(event.transaction);
config.ecosystem.cleanUpRawEventFieldsInPlace(blockFields);
let params = S$RescriptSchema.reverseConvertOrThrow(event.params, eventConfig.paramsRawEventSchema);
let params$1 = params === null ? "null" : params;
return {
chain_id: eventItem.chain,
event_id: eventId,
event_name: eventConfig.name,
contract_name: eventConfig.contractName,
block_number: blockNumber,
log_index: logIndex,
src_address: event.srcAddress,
block_hash: eventItem.blockHash,
block_timestamp: eventItem.timestamp,
block_fields: blockFields,
transaction_fields: transactionFields,
params: params$1
};
}
async function writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, escapeTables) {
try {
let shouldSaveHistory = Config.shouldSaveHistory(config, isInReorgThreshold);
let specificError = {
contents: undefined
};
let rawEvents;
if (config.enableRawEvents) {
let rows = Stdlib_Array.filterMap(batch.items, item => {
if (item.kind === 0) {
return makeRawEvent(item, config);
}
});
if (escapeTables !== undefined && Primitive_option.valFromOption(escapeTables).has(InternalTable.RawEvents.table)) {
removeInvalidUtf8InPlace(rows);
}
rawEvents = rows;
} else {
rawEvents = [];
}
let setRawEvents = async sql => {
try {
return await executeSet(sql, rawEvents, (sql, items) => setOrThrow(sql, items, InternalTable.RawEvents.table, InternalTable.RawEvents.schema, pgSchema));
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
return classifyWriteError(specificError, InternalTable.RawEvents.table, exn);
}
};
let setEntities = updatedEntities.map(param => {
let entityConfig = param.entityConfig;
let entitiesToSet = [];
let idsToDelete = [];
let diffCheckpointId = Stdlib_Option.map(rollback, r => r.diffCheckpointId);
let batchSetUpdates = [];
let batchDeleteEntityIds = [];
let batchDeleteCheckpointIds = [];
let idsWithDiff = new Set();
let latestChangeById = {};
let orderedIds = [];
param.changes.forEach(change => {
let entityId = change.entityId;
if (Stdlib_Option.isNone(latestChangeById[entityId])) {
orderedIds.push(entityId);
}
latestChangeById[entityId] = change;
if (!shouldSaveHistory) {
return;
}
if (change.checkpointId === diffCheckpointId) {
idsWithDiff.add(entityId);
return;
}
if (change.type === "SET") {
batchSetUpdates.push(change);
return;
}
batchDeleteEntityIds.push(change.entityId);
batchDeleteCheckpointIds.push(change.checkpointId);
});
let backfillHistoryIds = new Set();
orderedIds.forEach(entityId => {
let match = latestChangeById[entityId];
if (match.type === "SET") {
entitiesToSet.push(match.entity);
} else {
idsToDelete.push(match.entityId);
}
if (shouldSaveHistory && !idsWithDiff.has(entityId)) {
backfillHistoryIds.add(entityId);
return;
}
});
let shouldRemoveInvalidUtf8 = escapeTables !== undefined ? Primitive_option.valFromOption(escapeTables).has(entityConfig.table) : false;
return async sql => {
try {
let promises = [];
if (shouldSaveHistory) {
if (backfillHistoryIds.size !== 0) {
await EntityHistory.backfillHistory(sql, pgSchema, entityConfig.name, entityConfig.index, Array.from(backfillHistoryIds));
}
if (Utils.$$Array.notEmpty(batchDeleteCheckpointIds)) {
promises.push(sql.unsafe(makeInsertDeleteUpdatesQuery(entityConfig, pgSchema), [
batchDeleteEntityIds,
Utils.$$BigInt.arrayToStringArray(batchDeleteCheckpointIds)
], {prepare: true}));
}
if (Utils.$$Array.notEmpty(batchSetUpdates)) {
if (shouldRemoveInvalidUtf8) {
removeInvalidUtf8InPlace(batchSetUpdates.map(batchSetUpdate => {
if (batchSetUpdate.type === "SET") {
return batchSetUpdate.entity;
} else {
return Stdlib_JsError.throwWithMessage("Expected Set action");
}
}));
}
let entityHistory = getEntityHistory(entityConfig);
promises.push(setOrThrow(sql, batchSetUpdates, entityHistory.table, entityHistory.setChangeSchema, pgSchema));
}
}
if (Utils.$$Array.notEmpty(entitiesToSet)) {
if (shouldRemoveInvalidUtf8) {
removeInvalidUtf8InPlace(entitiesToSet);
}
promises.push(setOrThrow(sql, entitiesToSet, entityConfig.table, entityConfig.schema, pgSchema));
}
if (Utils.$$Array.notEmpty(idsToDelete)) {
promises.push(deleteByIdsOrThrow(sql, pgSchema, idsToDelete, entityConfig.table));
}
await Promise.all(promises);
return;
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
return classifyWriteError(specificError, entityConfig.table, exn);
}
};
});
let rollbackTables;
if (rollback !== undefined) {
let rollbackTargetCheckpointId = rollback.targetCheckpointId;
rollbackTables = sql => {
let promises = allEntities.map(entityConfig => EntityHistory.rollback(sql, pgSchema, entityConfig.name, entityConfig.index, rollbackTargetCheckpointId));
promises.push(InternalTable.Checkpoints.rollback(sql, pgSchema, rollbackTargetCheckpointId));
return Promise.all(promises);
};
} else {
rollbackTables = undefined;
}
try {
await Promise.all([
sql.begin(async sql => {
if (rollbackTables !== undefined) {
await rollbackTables(sql);
}
let setOperations = [
sql => InternalTable.Chains.setProgressedChains(sql, pgSchema, Utils.Dict.mapValuesToArray(batch.progressedChainsById, chainAfterBatch => ({
chainId: chainAfterBatch.fetchState.chainId,
progressBlockNumber: chainAfterBatch.progressBlockNumber,
sourceBlockNumber: chainAfterBatch.sourceBlockNumber,
totalEventsProcessed: chainAfterBatch.totalEventsProcessed
}))),
setRawEvents
].concat(setEntities);
if (chainMetaData !== undefined) {
setOperations.push(sql => InternalTable.Chains.setMeta(sql, pgSchema, chainMetaData));
}
if (shouldSaveHistory) {
setOperations.push(sql => InternalTable.Checkpoints.insert(sql, pgSchema, batch.checkpointIds, batch.checkpointChainIds, batch.checkpointBlockNumbers, batch.checkpointBlockHashes, batch.checkpointEventsProcessed));
}
await Promise.all(setOperations.map(dbFunc => dbFunc(sql)));
if (sinkPromise === undefined) {
return;
}
let exn = await sinkPromise;
if (exn === undefined) {
return;
}
throw exn;
}),
Promise.all(updatedEffectsCache.map(param => setEffectCacheOrThrow(param.effect, param.items, param.shouldInitialize)))
]);
let specificError$1 = specificError.contents;
if (specificError$1 === undefined) {
return;
}
throw specificError$1;
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
let specificError$2 = specificError.contents;
throw specificError$2 !== undefined ? specificError$2 : exn;
}
} catch (raw_exn$1) {
let exn$1 = Primitive_exceptions.internalToException(raw_exn$1);
if (exn$1.RE_EXN_ID === PgEncodingError) {
let escapeTables$1 = escapeTables !== undefined ? Primitive_option.valFromOption(escapeTables) : new Set();
escapeTables$1.add(exn$1.table);
return await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, updatedEntities, sinkPromise, chainMetaData, Primitive_option.some(escapeTables$1));
}
throw exn$1;
}
}
function makeGetRollbackRestoredEntitiesQuery(entityConfig, pgSchema) {
let dataFieldNames = Stdlib_Array.filterMap(entityConfig.table.fields, fieldOrDerived => {
if (fieldOrDerived.TAG === "Field") {
return Table.getPgDbFieldName(fieldOrDerived._0);
}
});
let dataFieldsCommaSeparated = dataFieldNames.map(name => `"` + name + `"`).join(", ");
let historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
return `SELECT DISTINCT ON (` + Table.idFieldName + `) ` + dataFieldsCommaSeparated + `
FROM "` + pgSchema + `"."` + historyTableName + `"
WHERE "` + EntityHistory.checkpointIdFieldName + `" <= $1
AND EXISTS (
SELECT 1
FROM "` + pgSchema + `"."` + historyTableName + `" h
WHERE h.` + Table.idFieldName + ` = "` + historyTableName + `".` + Table.idFieldName + `
AND h."` + EntityHistory.checkpointIdFieldName + `" > $1
)
ORDER BY ` + Table.idFieldName + `, "` + EntityHistory.checkpointIdFieldName + `" DESC`;
}
function makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema) {
let historyTableName = EntityHistory.historyTableName(entityConfig.name, entityConfig.index);
return `SELECT DISTINCT ` + Table.idFieldName + `
FROM "` + pgSchema + `"."` + historyTableName + `"
WHERE "` + EntityHistory.checkpointIdFieldName + `" > $1
AND NOT EXISTS (
SELECT 1
FROM "` + pgSchema + `"."` + historyTableName + `" h
WHERE h.` + Table.idFieldName + ` = "` + historyTableName + `".` + Table.idFieldName + `
AND h."` + EntityHistory.checkpointIdFieldName + `" <= $1
)`;
}
function make(sql, pgHost, pgSchema, pgPort, pgUser, pgDatabase, pgPassword, isHasuraEnabled, sink, onInitialize, onNewTables) {
let containerName = "envio-postgres";
let psqlExecOptions_env = Object.fromEntries([
[
"PGPASSWORD",
pgPassword
],
[
"PATH",
process.env.PATH
]
]);
let psqlExecOptions = {
env: psqlExecOptions_env
};
let cacheDirPath = Path.resolve(".envio", "cache");
let isInitialized = async () => Utils.$$Array.notEmpty(await sql.unsafe(`SELECT table_schema FROM information_schema.tables WHERE table_schema = '` + pgSchema + `' AND (table_name = '` + "event_sync_state" + `' OR table_name = '` + InternalTable.Chains.table.tableName + `');`));
let restoreEffectCache = async withUpload => {
if (withUpload) {
let nothingToUploadErrorMessage = "Nothing to upload.";
let match = await Promise.all([
Stdlib_Promise.$$catch(Fs.promises.readdir(cacheDirPath).then(e => ({
TAG: "Ok",
_0: e
})), param => Promise.resolve({
TAG: "Error",
_0: nothingToUploadErrorMessage
})),
getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort, containerName)
]);
let exit = 0;
let message;
let entries = match[0];
if (entries.TAG === "Ok") {
let psqlExec = match[1];
if (psqlExec.TAG === "Ok") {
let psqlExec$1 = psqlExec._0;
let cacheFiles = entries._0.filter(entry => entry.endsWith(".tsv"));
await Promise.all(cacheFiles.map(entry => {
let effectName = entry.slice(0, -4);
let table = Internal.makeCacheTable(effectName);
return sql.unsafe(makeCreateTableQuery(table, pgSchema, false)).then(() => {
let inputFile = Path.join(cacheDirPath, entry);
let command = psqlExec$1 + ` -c 'COPY "` + pgSchema + `"."` + table.tableName + `" FROM STDIN WITH (FORMAT text, HEADER);' < ` + inputFile;
return new Promise((resolve, reject) => {
Child_process.exec(command, psqlExecOptions, (error, stdout, param) => {
if (error === null) {
return resolve(stdout);
} else {
return reject(error);
}
});
});
});
}));
Logging.info("Successfully uploaded cache.");
} else {
message = match[1]._0;
exit = 1;
}
} else {
message = entries._0;
exit = 1;
}
if (exit === 1) {
if (message === nothingToUploadErrorMessage) {
Logging.info("No cache found to upload.");
} else {
Logging.error(`Failed to upload cache, continuing without it. ` + message);
}
}
}
let cacheTableInfo = await sql.unsafe(makeSchemaCacheTableInfoQuery(pgSchema));
if (withUpload && Utils.$$Array.notEmpty(cacheTableInfo) && onNewTables !== undefined) {
await onNewTables(cacheTableInfo.map(info => info.table_name));
}
let cache = {};
cacheTableInfo.forEach(param => {
let effectName = param.table_name.slice(cacheTablePrefixLength);
cache[effectName] = {
effectName: effectName,
count: param.count
};
});
return cache;
};
let initialize = async (chainConfigsOpt, entitiesOpt, enumsOpt, envioInfo) => {
let chainConfigs = chainConfigsOpt !== undefined ? chainConfigsOpt : [];
let entities = entitiesOpt !== undefined ? entitiesOpt : [];
let enums = enumsOpt !== undefined ? enumsOpt : [];
let pgEntities = entities.filter(e => e.storage.postgres);
let chEntities = entities.filter(e => e.storage.clickhouse);
let schemaTableNames = await sql.unsafe(makeSchemaTableNamesQuery(pgSchema));
if (Utils.$$Array.notEmpty(schemaTableNames) && !schemaTableNames.some(table => table.table_name === InternalTable.Chains.table.tableName ? true : table.table_name === "event_sync_state")) {
Stdlib_JsError.throwWithMessage(`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_SCHEMA" environment variable\n3. Or manually drop the schema in your database if you're certain the data is not needed.`);
}
if (sink !== undefined) {
await sink.initialize(chainConfigs, chEntities, enums);
}
let queries = makeInitializeTransaction(pgSchema, pgUser, isHasuraEnabled, chainConfigs, pgEntities, enums, Utils.$$Array.isEmpty(schemaTableNames));
await sql.begin(async sql => {
await Promise.all(queries.map(query => sql.unsafe(query)));
return await InternalTable.EnvioInfo.write(sql, pgSchema, envioInfo);
});
let ids = [];
let addrChainIds = [];
let addrContractNames = [];
chainConfigs.forEach(chain => {
chain.contracts.forEach(contract => {
contract.addresses.forEach(address => {
ids.push(Config.EnvioAddresses.makeId(chain.id, address));
addrChainIds.push(chain.id);
addrContractNames.push(contract.name);
});
});
});
if (ids.length !== 0) {
await sql.unsafe(`INSERT INTO "` + pgSchema + `"."` + Config.EnvioAddresses.table.tableName + `" ("id", "chain_id", "registration_block", "registration_log_index", "contract_name")
SELECT id, chain_id, -1, -1, contract_name FROM unnest($1::text[],$2::int[],$3::text[]) AS t(id, chain_id, contract_name);`, [
ids,
addrChainIds,
addrContractNames
]);
}
let cache = await restoreEffectCache(true);
if (onInitialize !== undefined) {
await onInitialize();
}
return {
cleanRun: true,
cache: cache,
chains: chainConfigs.map(chainConfig => ({
id: chainConfig.id,
startBlock: chainConfig.startBlock,
endBlock: chainConfig.endBlock,
maxReorgDepth: chainConfig.maxReorgDepth,
progressBlockNumber: -1,
numEventsProcessed: 0,
firstEventBlockNumber: undefined,
timestampCaughtUpToHeadOrEndblock: undefined,
indexingAddresses: ChainFetcher.configAddresses(chainConfig),
sourceBlockNumber: 0
})),
checkpointId: InternalTable.Checkpoints.initialCheckpointId,
reorgCheckpoints: [],
envioInfo: envioInfo
};
};
let loadOrThrow = async (filter, table) => {
let params = [];
let condition = makeFilterCondition(filter, table, params);
let rows;
try {
rows = await sql.unsafe(makeLoadQuery(pgSchema, table.tableName, condition), params, {prepare: true});
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed loading "` + table.tableName + `" from storage by condition: ` + condition,
reason: exn,
Error: new Error()
};
}
try {
return S$RescriptSchema.parseOrThrow(rows, Table.pgRowsSchema(table));
} catch (raw_exn$1) {
let exn$1 = Primitive_exceptions.internalToException(raw_exn$1);
throw {
RE_EXN_ID: Persistence.StorageError,
message: `Failed to parse "` + table.tableName + `" loaded from storage by condition: ` + condition,
reason: exn$1,
Error: new Error()
};
}
};
let setOrThrow$1 = (items, table, itemSchema) => setOrThrow(sql, items, table, itemSchema, pgSchema);
let setEffectCacheOrThrow = async (effect, items, initialize) => {
let match = effect.storageMeta;
let table = match.table;
if (initialize) {
await sql.unsafe(makeCreateTableQuery(table, pgSchema, false));
if (onNewTables !== undefined) {
await onNewTables([table.tableName]);
}
}
return await setOrThrow$1(items, table, match.itemSchema);
};
let dumpEffectCache = async () => {
try {
let cacheTableInfo = (await sql.unsafe(makeSchemaCacheTableInfoQuery(pgSchema))).filter(i => i.count > 0);
if (!Utils.$$Array.notEmpty(cacheTableInfo)) {
return;
}
try {
await Fs.promises.access(cacheDirPath);
} catch (exn) {
await Fs.promises.mkdir(cacheDirPath, {
recursive: true
});
}
let psqlExec = await getConnectedPsqlExec(pgUser, pgHost, pgDatabase, pgPort, containerName);
if (psqlExec.TAG !== "Ok") {
return Logging.error(`Failed to dump cache. ` + psqlExec._0);
}
let psqlExec$1 = psqlExec._0;
Logging.info(`Dumping cache: ` + cacheTableInfo.map(param => param.table_name + " (" + param.count.toString() + " rows)").join(", "));
let promises = cacheTableInfo.map(async param => {
let tableName = param.table_name;
let cacheName = tableName.slice(cacheTablePrefixLength);
let outputFile = Path.join(cacheDirPath, cacheName + ".tsv");
let command = psqlExec$1 + ` -c 'COPY "` + pgSchema + `"."` + tableName + `" TO STDOUT WITH (FORMAT text, HEADER);' > ` + outputFile;
return new Promise((resolve, reject) => {
Child_process.exec(command, psqlExecOptions, (error, stdout, param) => {
if (error === null) {
return resolve(stdout);
} else {
return reject(error);
}
});
});
});
await Promise.all(promises);
return Logging.info(`Successfully dumped cache to ` + cacheDirPath);
} catch (raw_exn) {
let exn$1 = Primitive_exceptions.internalToException(raw_exn);
return Logging.errorWithExn(Utils.prettifyExn(exn$1), `Failed to dump cache.`);
}
};
let resumeInitialState = async () => {
let match = await Promise.all([
restoreEffectCache(false),
InternalTable.Chains.getInitialState(sql, pgSchema).then(rawInitialStates => rawInitialStates.map(rawInitialState => ({
id: rawInitialState.id,
startBlock: rawInitialState.startBlock,
endBlock: Primitive_option.fromNull(rawInitialState.endBlock),
maxReorgDepth: rawInitialState.maxReorgDepth,
progressBlockNumber: rawInitialState.progressBlockNumber,
numEventsProcessed: rawInitialState.numEventsProcessed,
firstEventBlockNumber: Primitive_option.fromNull(rawInitialState.firstEventBlockNumber),
timestampCaughtUpToHeadOrEndblock: Primitive_option.fromNull(rawInitialState.timestampCaughtUpToHeadOrEndblock),
indexingAddresses: rawInitialState.indexingAddresses,
sourceBlockNumber: rawInitialState.sourceBlockNumber
}))),
sql.unsafe(InternalTable.Checkpoints.makeCommitedCheckpointIdQuery(pgSchema)),
sql.unsafe(InternalTable.Checkpoints.makeGetReorgCheckpointsQuery(pgSchema)),
InternalTable.EnvioInfo.read(sql, pgSchema)
]);
let checkpointId = BigInt(match[2][0].id);
let reorgCheckpoints = match[3].map(raw => ({
id: BigInt(raw.id),
chain_id: raw.chain_id,
block_number: raw.block_number,
block_hash: raw.block_hash
}));
if (sink !== undefined) {
await sink.resume(checkpointId);
}
return {
cleanRun: false,
cache: match[0],
chains: match[1],
checkpointId: checkpointId,
reorgCheckpoints: reorgCheckpoints,
envioInfo: match[4]
};
};
let reset = async () => {
let query = `DROP SCHEMA IF EXISTS "` + pgSchema + `" CASCADE;`;
return await sql.unsafe(query);
};
let setChainMeta = chainsData => InternalTable.Chains.setMeta(sql, pgSchema, chainsData).then(param => (undefined));
let pruneStaleCheckpoints = safeCheckpointId => InternalTable.Checkpoints.pruneStaleCheckpoints(sql, pgSchema, safeCheckpointId);
let pruneStaleEntityHistory = (entityName, entityIndex, safeCheckpointId) => EntityHistory.pruneStaleEntityHistory(sql, entityName, entityIndex, pgSchema, safeCheckpointId);
let getRollbackTargetCheckpoint = (reorgChainId, lastKnownValidBlockNumber) => InternalTable.Checkpoints.getRollbackTargetCheckpoint(sql, pgSchema, reorgChainId, lastKnownValidBlockNumber);
let getRollbackProgressDiff = rollbackTargetCheckpointId => InternalTable.Checkpoints.getRollbackProgressDiff(sql, pgSchema, rollbackTargetCheckpointId);
let getRollbackData = async (entityConfig, rollbackTargetCheckpointId) => await Promise.all([
sql.unsafe(makeGetRollbackRemovedIdsQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId.toString()], {prepare: true}),
sql.unsafe(makeGetRollbackRestoredEntitiesQuery(entityConfig, pgSchema), [rollbackTargetCheckpointId.toString()], {prepare: true})
]);
let writeBatchMethod = async (batch, rollback, isInReorgThreshold, config, allEntities, updatedEffectsCache, updatedEntities, chainMetaData) => {
let pgUpdates = [];
let chUpdates = [];
for (let i = 0, i_finish = updatedEntities.length; i < i_finish; ++i) {
let update = updatedEntities[i];
let entityConfig = update.entityConfig;
if (entityConfig.storage.postgres) {
pgUpdates.push(update);
}
if (entityConfig.storage.clickhouse) {
chUpdates.push(update);
}
}
let sinkPromise;
if (sink !== undefined) {
let timerRef = Hrtime.makeTimer();
sinkPromise = sink.writeBatch(batch, chUpdates).then(() => {
Prometheus.StorageWrite.increment(sink.name, Hrtime.toSecondsFloat(Hrtime.timeSince(timerRef)));
}).catch(exn => exn);
} else {
sinkPromise = undefined;
}
let primaryTimerRef = Hrtime.makeTimer();
await writeBatch(sql, batch, pgSchema, rollback, isInReorgThreshold, config, allEntities, setEffectCacheOrThrow, updatedEffectsCache, pgUpdates, sinkPromise, chainMetaData, undefined);
return Prometheus.StorageWrite.increment("postgres", Hrtime.toSecondsFloat(Hrtime.timeSince(primaryTimerRef)));
};
let close = () => sql.end();
return {
name: "postgres",
isInitialized: isInitialized,
initialize: initialize,
resumeInitialState: resumeInitialState,
loadOrThrow: loadOrThrow,
dumpEffectCache: dumpEffectCache,
reset: reset,
setChainMeta: setChainMeta,
pruneStaleCheckpoints: pruneStaleCheckpoints,
pruneStaleEntityHistory: pruneStaleEntityHistory,
getRollbackTargetCheckpoint: getRollbackTargetCheckpoint,
getRollbackProgressDiff: getRollbackProgressDiff,
getRollbackData: getRollbackData,
writeBatch: writeBatchMethod,
close: close
};
}
function makeStorageFromEnv(config, sqlOpt, pgSchemaOpt, isHasuraEnabledOpt) {
let sql = sqlOpt !== undefined ? Primitive_option.valFromOption(sqlOpt) : makeClient();
let pgSchema = pgSchemaOpt !== undefined ? pgSchemaOpt : Env.Db.publicSchema;
let isHasuraEnabled = isHasuraEnabledOpt !== undefined ? isHasuraEnabledOpt : Env.Hasura.enabled;
let tmp;
if (config.storage.clickhouse) {
let host = Env.ClickHouse.host();
let username = Env.ClickHouse.username();
let password = Env.ClickHouse.password();
let database = Env.ClickHouse.database();
let missing = [];
let checkEnv = (opt, name) => {
if (opt !== undefined) {
return;
} else {
missing.push(name);
return;