envio
Version:
A latency and sync speed optimized, developer friendly blockchain data indexer.
1,087 lines (1,067 loc) • 48.4 kB
JavaScript
// Generated by ReScript, PLEASE EDIT WITH CARE
import * as Env from "./Env.res.mjs";
import * as Utils from "./Utils.res.mjs";
import * as Config from "./Config.res.mjs";
import * as Hrtime from "./bindings/Hrtime.res.mjs";
import * as Logging from "./Logging.res.mjs";
import * as Process from "process";
import * as ChainMap from "./ChainMap.res.mjs";
import * as Throttler from "./Throttler.res.mjs";
import * as FetchState from "./FetchState.res.mjs";
import * as Prometheus from "./Prometheus.res.mjs";
import * as LoadManager from "./LoadManager.res.mjs";
import * as Stdlib_Null from "@rescript/runtime/lib/es6/Stdlib_Null.js";
import * as ChainFetcher from "./ChainFetcher.res.mjs";
import * as ChainManager from "./ChainManager.res.mjs";
import * as Stdlib_Float from "@rescript/runtime/lib/es6/Stdlib_Float.js";
import * as ErrorHandling from "./ErrorHandling.res.mjs";
import * as InMemoryStore from "./InMemoryStore.res.mjs";
import * as Primitive_int from "@rescript/runtime/lib/es6/Primitive_int.js";
import * as SourceManager from "./sources/SourceManager.res.mjs";
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
import * as ReorgDetection from "./ReorgDetection.res.mjs";
import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
import * as EventProcessing from "./EventProcessing.res.mjs";
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
import * as S$RescriptSchema from "rescript-schema/src/S.res.mjs";
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
import * as SafeCheckpointTracking from "./SafeCheckpointTracking.res.mjs";
function make() {
let intervalMillis = Env.ThrottleWrites.pruneStaleDataIntervalMillis;
let logger = Logging.createChild({
context: "Throttler for pruning stale entity history data",
intervalMillis: intervalMillis
});
let pruneStaleEntityHistory = Throttler.make(intervalMillis, logger);
return {
pruneStaleEntityHistory: pruneStaleEntityHistory
};
}
let WriteThrottlers = {
make: make
};
function make$1(ctx, chainManager, isDevelopmentModeOpt, shouldUseTuiOpt, exitAfterFirstEventBlockOpt, onError) {
let isDevelopmentMode = isDevelopmentModeOpt !== undefined ? isDevelopmentModeOpt : false;
let shouldUseTui = shouldUseTuiOpt !== undefined ? shouldUseTuiOpt : false;
let exitAfterFirstEventBlock = exitAfterFirstEventBlockOpt !== undefined ? exitAfterFirstEventBlockOpt : false;
return {
ctx: ctx,
chainManager: chainManager,
rollbackState: "NoRollback",
indexerStartTime: new Date(),
writeThrottlers: make(),
loadManager: LoadManager.make(),
keepProcessAlive: isDevelopmentMode || shouldUseTui,
exitAfterFirstEventBlock: exitAfterFirstEventBlock,
onError: onError,
id: 0
};
}
function getId(self) {
return self.id;
}
function setChainManager(self, chainManager) {
return {
ctx: self.ctx,
chainManager: chainManager,
rollbackState: self.rollbackState,
indexerStartTime: self.indexerStartTime,
writeThrottlers: self.writeThrottlers,
loadManager: self.loadManager,
keepProcessAlive: self.keepProcessAlive,
exitAfterFirstEventBlock: self.exitAfterFirstEventBlock,
onError: self.onError,
id: self.id
};
}
function isPreparingRollback(state) {
let match = state.rollbackState;
if (typeof match !== "object") {
return match !== "NoRollback";
} else {
return match.TAG !== "RollbackReady";
}
}
function updateChainMetadataTable(cm, inMemoryStore) {
let chainsData = {};
ChainMap.values(cm.chainFetchers).forEach(cf => {
chainsData[cf.chainConfig.id.toString()] = {
first_event_block: Stdlib_Null.fromOption(cf.fetchState.firstEventBlock),
buffer_block: FetchState.bufferBlockNumber(cf.fetchState),
ready_at: Stdlib_Null.fromOption(cf.timestampCaughtUpToHeadOrEndblock),
_is_hyper_sync: SourceManager.getActiveSource(cf.sourceManager).poweredByHyperSync
};
});
InMemoryStore.setChainMeta(inMemoryStore, chainsData);
}
function validatePartitionQueryResponse(state, partitionQueryResponse) {
let response = partitionQueryResponse.response;
let chain = partitionQueryResponse.chain;
let chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
let stats = response.stats;
let knownHeight = response.knownHeight;
if (knownHeight > chainFetcher.fetchState.knownHeight) {
Prometheus.SourceHeight.set(SourceManager.getActiveSource(chainFetcher.sourceManager).name, chainFetcher.chainConfig.id, knownHeight);
}
Prometheus.FetchingBlockRange.increment(chain, stats["total time elapsed (s)"], Stdlib_Option.getOr(stats["parsing time (s)"], 0), response.parsedQueueItems.length, (response.latestFetchedBlockNumber - response.fromBlockQueried | 0) + 1 | 0);
let match = ReorgDetection.registerReorgGuard(chainFetcher.reorgDetection, response.blockHashes, knownHeight);
let reorgResult = match[1];
let updatedChainFetcher_logger = chainFetcher.logger;
let updatedChainFetcher_fetchState = chainFetcher.fetchState;
let updatedChainFetcher_sourceManager = chainFetcher.sourceManager;
let updatedChainFetcher_chainConfig = chainFetcher.chainConfig;
let updatedChainFetcher_isProgressAtHead = chainFetcher.isProgressAtHead;
let updatedChainFetcher_timestampCaughtUpToHeadOrEndblock = chainFetcher.timestampCaughtUpToHeadOrEndblock;
let updatedChainFetcher_committedProgressBlockNumber = chainFetcher.committedProgressBlockNumber;
let updatedChainFetcher_numEventsProcessed = chainFetcher.numEventsProcessed;
let updatedChainFetcher_reorgDetection = match[0];
let updatedChainFetcher_safeCheckpointTracking = chainFetcher.safeCheckpointTracking;
let updatedChainFetcher = {
logger: updatedChainFetcher_logger,
fetchState: updatedChainFetcher_fetchState,
sourceManager: updatedChainFetcher_sourceManager,
chainConfig: updatedChainFetcher_chainConfig,
isProgressAtHead: updatedChainFetcher_isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: updatedChainFetcher_timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: updatedChainFetcher_committedProgressBlockNumber,
numEventsProcessed: updatedChainFetcher_numEventsProcessed,
reorgDetection: updatedChainFetcher_reorgDetection,
safeCheckpointTracking: updatedChainFetcher_safeCheckpointTracking
};
let init = state.chainManager;
let nextState_ctx = state.ctx;
let nextState_chainManager = {
chainFetchers: ChainMap.set(state.chainManager.chainFetchers, chain, updatedChainFetcher),
isInReorgThreshold: init.isInReorgThreshold,
isRealtime: init.isRealtime
};
let nextState_rollbackState = state.rollbackState;
let nextState_indexerStartTime = state.indexerStartTime;
let nextState_writeThrottlers = state.writeThrottlers;
let nextState_loadManager = state.loadManager;
let nextState_keepProcessAlive = state.keepProcessAlive;
let nextState_exitAfterFirstEventBlock = state.exitAfterFirstEventBlock;
let nextState_onError = state.onError;
let nextState_id = state.id;
let nextState = {
ctx: nextState_ctx,
chainManager: nextState_chainManager,
rollbackState: nextState_rollbackState,
indexerStartTime: nextState_indexerStartTime,
writeThrottlers: nextState_writeThrottlers,
loadManager: nextState_loadManager,
keepProcessAlive: nextState_keepProcessAlive,
exitAfterFirstEventBlock: nextState_exitAfterFirstEventBlock,
onError: nextState_onError,
id: nextState_id
};
let rollbackWithReorgDetectedBlockNumber;
if (typeof reorgResult !== "object") {
rollbackWithReorgDetectedBlockNumber = undefined;
} else {
let reorgDetected = reorgResult._0;
Logging.childInfo(chainFetcher.logger, ReorgDetection.reorgDetectedToLogParams(reorgDetected, state.ctx.config.shouldRollbackOnReorg));
Prometheus.ReorgCount.increment(chain);
Prometheus.ReorgDetectionBlockNumber.set(reorgDetected.scannedBlock.blockNumber, chain);
rollbackWithReorgDetectedBlockNumber = state.ctx.config.shouldRollbackOnReorg ? reorgDetected.scannedBlock.blockNumber : undefined;
}
if (rollbackWithReorgDetectedBlockNumber === undefined) {
return [
nextState,
[{
TAG: "ProcessPartitionQueryResponse",
_0: partitionQueryResponse
}]
];
}
let match$1 = state.rollbackState;
let chainManager;
if (typeof match$1 !== "object" || match$1.TAG !== "RollbackReady") {
chainManager = state.chainManager;
} else {
let eventsProcessedDiffByChain = match$1.eventsProcessedDiffByChain;
let init$1 = state.chainManager;
chainManager = {
chainFetchers: ChainMap.mapWithKey(state.chainManager.chainFetchers, (c, chainFetcher) => {
let eventsProcessedDiff = eventsProcessedDiffByChain[c];
if (eventsProcessedDiff !== undefined) {
return {
logger: chainFetcher.logger,
fetchState: chainFetcher.fetchState,
sourceManager: chainFetcher.sourceManager,
chainConfig: chainFetcher.chainConfig,
isProgressAtHead: chainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber,
numEventsProcessed: chainFetcher.numEventsProcessed + eventsProcessedDiff,
reorgDetection: chainFetcher.reorgDetection,
safeCheckpointTracking: chainFetcher.safeCheckpointTracking
};
} else {
return chainFetcher;
}
}),
isInReorgThreshold: init$1.isInReorgThreshold,
isRealtime: init$1.isRealtime
};
}
return [
{
ctx: nextState_ctx,
chainManager: {
chainFetchers: ChainMap.map(chainManager.chainFetchers, chainFetcher => ({
logger: chainFetcher.logger,
fetchState: FetchState.resetPendingQueries(chainFetcher.fetchState),
sourceManager: chainFetcher.sourceManager,
chainConfig: chainFetcher.chainConfig,
isProgressAtHead: chainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber,
numEventsProcessed: chainFetcher.numEventsProcessed,
reorgDetection: chainFetcher.reorgDetection,
safeCheckpointTracking: chainFetcher.safeCheckpointTracking
})),
isInReorgThreshold: chainManager.isInReorgThreshold,
isRealtime: chainManager.isRealtime
},
rollbackState: {
TAG: "ReorgDetected",
chain: chain,
blockNumber: rollbackWithReorgDetectedBlockNumber
},
indexerStartTime: nextState_indexerStartTime,
writeThrottlers: nextState_writeThrottlers,
loadManager: nextState_loadManager,
keepProcessAlive: nextState_keepProcessAlive,
exitAfterFirstEventBlock: nextState_exitAfterFirstEventBlock,
onError: nextState_onError,
id: nextState_id + 1 | 0
},
["Rollback"]
];
}
function submitPartitionQueryResponse(state, newItems, newItemsWithDcs, knownHeight, latestFetchedBlock, query, chain) {
let chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
let updatedChainFetcher = ChainFetcher.handleQueryResult(chainFetcher, query, newItems, newItemsWithDcs, latestFetchedBlock, knownHeight);
let updatedChainFetcher$1;
if (state.exitAfterFirstEventBlock && newItems.length !== 0) {
let firstEventBlock = newItems[0].blockNumber;
let currentEndBlock = updatedChainFetcher.fetchState.endBlock;
if (currentEndBlock !== undefined) {
if (firstEventBlock < currentEndBlock) {
let init = updatedChainFetcher.fetchState;
updatedChainFetcher$1 = {
logger: updatedChainFetcher.logger,
fetchState: {
optimizedPartitions: init.optimizedPartitions,
startBlock: init.startBlock,
endBlock: firstEventBlock,
normalSelection: init.normalSelection,
indexingAddresses: init.indexingAddresses,
contractConfigs: init.contractConfigs,
chainId: init.chainId,
latestOnBlockBlockNumber: init.latestOnBlockBlockNumber,
blockLag: init.blockLag,
buffer: init.buffer,
targetBufferSize: init.targetBufferSize,
onBlockConfigs: init.onBlockConfigs,
knownHeight: init.knownHeight,
firstEventBlock: init.firstEventBlock
},
sourceManager: updatedChainFetcher.sourceManager,
chainConfig: updatedChainFetcher.chainConfig,
isProgressAtHead: updatedChainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: updatedChainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: updatedChainFetcher.committedProgressBlockNumber,
numEventsProcessed: updatedChainFetcher.numEventsProcessed,
reorgDetection: updatedChainFetcher.reorgDetection,
safeCheckpointTracking: updatedChainFetcher.safeCheckpointTracking
};
} else {
updatedChainFetcher$1 = updatedChainFetcher;
}
} else {
let init$1 = updatedChainFetcher.fetchState;
updatedChainFetcher$1 = {
logger: updatedChainFetcher.logger,
fetchState: {
optimizedPartitions: init$1.optimizedPartitions,
startBlock: init$1.startBlock,
endBlock: firstEventBlock,
normalSelection: init$1.normalSelection,
indexingAddresses: init$1.indexingAddresses,
contractConfigs: init$1.contractConfigs,
chainId: init$1.chainId,
latestOnBlockBlockNumber: init$1.latestOnBlockBlockNumber,
blockLag: init$1.blockLag,
buffer: init$1.buffer,
targetBufferSize: init$1.targetBufferSize,
onBlockConfigs: init$1.onBlockConfigs,
knownHeight: init$1.knownHeight,
firstEventBlock: init$1.firstEventBlock
},
sourceManager: updatedChainFetcher.sourceManager,
chainConfig: updatedChainFetcher.chainConfig,
isProgressAtHead: updatedChainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: updatedChainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: updatedChainFetcher.committedProgressBlockNumber,
numEventsProcessed: updatedChainFetcher.numEventsProcessed,
reorgDetection: updatedChainFetcher.reorgDetection,
safeCheckpointTracking: updatedChainFetcher.safeCheckpointTracking
};
}
} else {
updatedChainFetcher$1 = updatedChainFetcher;
}
if (!chainFetcher.isProgressAtHead && updatedChainFetcher$1.isProgressAtHead) {
Logging.childInfo(updatedChainFetcher$1.logger, "All events have been fetched");
}
let init$2 = state.chainManager;
let nextState_ctx = state.ctx;
let nextState_chainManager = {
chainFetchers: ChainMap.set(state.chainManager.chainFetchers, chain, updatedChainFetcher$1),
isInReorgThreshold: init$2.isInReorgThreshold,
isRealtime: init$2.isRealtime
};
let nextState_rollbackState = state.rollbackState;
let nextState_indexerStartTime = state.indexerStartTime;
let nextState_writeThrottlers = state.writeThrottlers;
let nextState_loadManager = state.loadManager;
let nextState_keepProcessAlive = state.keepProcessAlive;
let nextState_exitAfterFirstEventBlock = state.exitAfterFirstEventBlock;
let nextState_onError = state.onError;
let nextState_id = state.id;
let nextState = {
ctx: nextState_ctx,
chainManager: nextState_chainManager,
rollbackState: nextState_rollbackState,
indexerStartTime: nextState_indexerStartTime,
writeThrottlers: nextState_writeThrottlers,
loadManager: nextState_loadManager,
keepProcessAlive: nextState_keepProcessAlive,
exitAfterFirstEventBlock: nextState_exitAfterFirstEventBlock,
onError: nextState_onError,
id: nextState_id
};
return [
nextState,
[
{
TAG: "UpdateChainMetaDataAndCheckForExit",
_0: "NoExit"
},
"ProcessEventBatch",
{
TAG: "NextQuery",
_0: {
TAG: "Chain",
_0: chain
}
}
]
];
}
async function processPartitionQueryResponse(state, param, dispatchAction) {
let response = param.response;
let parsedQueueItems = response.parsedQueueItems;
let itemsWithContractRegister = [];
let newItems = [];
for (let idx = 0, idx_finish = parsedQueueItems.length; idx < idx_finish; ++idx) {
let item = parsedQueueItems[idx];
if (item.eventConfig.contractRegister !== undefined) {
itemsWithContractRegister.push(item);
}
newItems.push(item);
}
let newItemsWithDcs = itemsWithContractRegister.length !== 0 ? await ChainFetcher.runContractRegistersOrThrow(itemsWithContractRegister, state.ctx.config) : itemsWithContractRegister;
return dispatchAction({
TAG: "SubmitPartitionQueryResponse",
newItems: newItems,
newItemsWithDcs: newItemsWithDcs,
knownHeight: response.knownHeight,
latestFetchedBlock: {
blockNumber: response.latestFetchedBlockNumber,
blockTimestamp: response.latestFetchedBlockTimestamp
},
query: param.query,
chain: param.chain
});
}
function updateChainFetcher(chainFetcherUpdate, state, chain) {
let init = state.chainManager;
return [
{
ctx: state.ctx,
chainManager: {
chainFetchers: ChainMap.update(state.chainManager.chainFetchers, chain, chainFetcherUpdate),
isInReorgThreshold: init.isInReorgThreshold,
isRealtime: init.isRealtime
},
rollbackState: state.rollbackState,
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
[]
];
}
function onEnterReorgThreshold(state) {
Logging.info("Reorg threshold reached");
Prometheus.ReorgThreshold.set(true);
let chainFetchers = ChainMap.map(state.chainManager.chainFetchers, chainFetcher => ({
logger: chainFetcher.logger,
fetchState: FetchState.updateInternal(chainFetcher.fetchState, undefined, undefined, undefined, chainFetcher.chainConfig.blockLag, undefined),
sourceManager: chainFetcher.sourceManager,
chainConfig: chainFetcher.chainConfig,
isProgressAtHead: chainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber,
numEventsProcessed: chainFetcher.numEventsProcessed,
reorgDetection: chainFetcher.reorgDetection,
safeCheckpointTracking: chainFetcher.safeCheckpointTracking
}));
let init = state.chainManager;
return {
ctx: state.ctx,
chainManager: {
chainFetchers: chainFetchers,
isInReorgThreshold: true,
isRealtime: init.isRealtime
},
rollbackState: state.rollbackState,
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
};
}
function injectedActionReducer(markBatchProcessed) {
return (state, action) => {
if (typeof action !== "object") {
switch (action) {
case "StartFindingReorgDepth" :
return [
{
ctx: state.ctx,
chainManager: state.chainManager,
rollbackState: "FindingReorgDepth",
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
[]
];
case "EnterReorgThreshold" :
return [
onEnterReorgThreshold(state),
[{
TAG: "NextQuery",
_0: "CheckAllChains"
}]
];
case "SuccessExit" :
Logging.info("Exiting with success");
Process.exit(0);
return [
state,
[]
];
}
} else {
switch (action.TAG) {
case "ValidatePartitionQueryResponse" :
return validatePartitionQueryResponse(state, action._0);
case "SubmitPartitionQueryResponse" :
return submitPartitionQueryResponse(state, action.newItems, action.newItemsWithDcs, action.knownHeight, action.latestFetchedBlock, action.query, action.chain);
case "FinishWaitingForNewBlock" :
let knownHeight = action.knownHeight;
let chain = action.chain;
let updatedChainFetchers = ChainMap.update(state.chainManager.chainFetchers, chain, chainFetcher => {
let updatedFetchState = FetchState.updateKnownHeight(chainFetcher.fetchState, knownHeight);
if (updatedFetchState !== chainFetcher.fetchState) {
return {
logger: chainFetcher.logger,
fetchState: updatedFetchState,
sourceManager: chainFetcher.sourceManager,
chainConfig: chainFetcher.chainConfig,
isProgressAtHead: chainFetcher.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: chainFetcher.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: chainFetcher.committedProgressBlockNumber,
numEventsProcessed: chainFetcher.numEventsProcessed,
reorgDetection: chainFetcher.reorgDetection,
safeCheckpointTracking: chainFetcher.safeCheckpointTracking
};
} else {
return chainFetcher;
}
});
let isBelowReorgThreshold = !state.chainManager.isInReorgThreshold && state.ctx.config.shouldRollbackOnReorg;
let shouldEnterReorgThreshold = isBelowReorgThreshold && ChainMap.values(updatedChainFetchers).every(chainFetcher => FetchState.isReadyToEnterReorgThreshold(chainFetcher.fetchState));
let init = state.chainManager;
let state_ctx = state.ctx;
let state_chainManager = {
chainFetchers: updatedChainFetchers,
isInReorgThreshold: init.isInReorgThreshold,
isRealtime: init.isRealtime
};
let state_rollbackState = state.rollbackState;
let state_indexerStartTime = state.indexerStartTime;
let state_writeThrottlers = state.writeThrottlers;
let state_loadManager = state.loadManager;
let state_keepProcessAlive = state.keepProcessAlive;
let state_exitAfterFirstEventBlock = state.exitAfterFirstEventBlock;
let state_onError = state.onError;
let state_id = state.id;
let state$1 = {
ctx: state_ctx,
chainManager: state_chainManager,
rollbackState: state_rollbackState,
indexerStartTime: state_indexerStartTime,
writeThrottlers: state_writeThrottlers,
loadManager: state_loadManager,
keepProcessAlive: state_keepProcessAlive,
exitAfterFirstEventBlock: state_exitAfterFirstEventBlock,
onError: state_onError,
id: state_id
};
if (shouldEnterReorgThreshold) {
return [
onEnterReorgThreshold(state$1),
[
{
TAG: "NextQuery",
_0: "CheckAllChains"
},
"ProcessEventBatch"
]
];
} else {
return [
state$1,
[
{
TAG: "NextQuery",
_0: {
TAG: "Chain",
_0: chain
}
},
"ProcessEventBatch"
]
];
}
case "EventBatchProcessed" :
let maybePruneEntityHistory = Config.shouldPruneHistory(state.ctx.config, state.chainManager.isInReorgThreshold) ? ["PruneStaleEntityHistory"] : [];
markBatchProcessed();
let state_ctx$1 = state.ctx;
let state_chainManager$1 = ChainManager.updateProgressedChains(state.chainManager, action.batch);
let state_indexerStartTime$1 = state.indexerStartTime;
let state_writeThrottlers$1 = state.writeThrottlers;
let state_loadManager$1 = state.loadManager;
let state_keepProcessAlive$1 = state.keepProcessAlive;
let state_exitAfterFirstEventBlock$1 = state.exitAfterFirstEventBlock;
let state_onError$1 = state.onError;
let state_id$1 = state.id;
let state$2 = {
ctx: state_ctx$1,
chainManager: state_chainManager$1,
rollbackState: "NoRollback",
indexerStartTime: state_indexerStartTime$1,
writeThrottlers: state_writeThrottlers$1,
loadManager: state_loadManager$1,
keepProcessAlive: state_keepProcessAlive$1,
exitAfterFirstEventBlock: state_exitAfterFirstEventBlock$1,
onError: state_onError$1,
id: state_id$1
};
let shouldExit = EventProcessing.allChainsEventsProcessedToEndblock(state_chainManager$1.chainFetchers) ? (Logging.info("All chains are caught up to end blocks."), state_keepProcessAlive$1 ? "NoExit" : "ExitWithSuccess") : (
state_exitAfterFirstEventBlock$1 && ChainMap.values(state_chainManager$1.chainFetchers).every(cf => {
if (cf.isProgressAtHead) {
return Stdlib_Option.isNone(cf.fetchState.endBlock);
} else {
return false;
}
}) ? ({
TAG: "ExitWithError",
_0: "No events found between startBlock and chain head. Cannot auto-detect endBlock."
}) : "NoExit"
);
let tasks;
tasks = typeof shouldExit !== "object" ? (
shouldExit === "ExitWithSuccess" ? [{
TAG: "UpdateChainMetaDataAndCheckForExit",
_0: shouldExit
}] : [
{
TAG: "UpdateChainMetaDataAndCheckForExit",
_0: shouldExit
},
"ProcessEventBatch"
].concat(maybePruneEntityHistory)
) : [{
TAG: "UpdateChainMetaDataAndCheckForExit",
_0: shouldExit
}];
return [
state$2,
tasks
];
case "FindReorgDepth" :
return [
{
ctx: state.ctx,
chainManager: state.chainManager,
rollbackState: {
TAG: "FoundReorgDepth",
chain: action.chain,
rollbackTargetBlockNumber: action.rollbackTargetBlockNumber
},
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
["Rollback"]
];
case "UpdateQueues" :
let shouldEnterReorgThreshold$1 = action.shouldEnterReorgThreshold;
let progressedChainsById = action.progressedChainsById;
let chainFetchers = ChainMap.mapWithKey(state.chainManager.chainFetchers, (chain, cf) => {
let chainAfterBatch = progressedChainsById[chain];
let fs = chainAfterBatch !== undefined ? chainAfterBatch.fetchState : cf.fetchState;
return {
logger: cf.logger,
fetchState: shouldEnterReorgThreshold$1 ? FetchState.updateInternal(fs, undefined, undefined, undefined, cf.chainConfig.blockLag, undefined) : fs,
sourceManager: cf.sourceManager,
chainConfig: cf.chainConfig,
isProgressAtHead: cf.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: cf.committedProgressBlockNumber,
numEventsProcessed: cf.numEventsProcessed,
reorgDetection: cf.reorgDetection,
safeCheckpointTracking: cf.safeCheckpointTracking
};
});
let init$1 = state.chainManager;
let chainManager_isInReorgThreshold = init$1.isInReorgThreshold;
let chainManager_isRealtime = init$1.isRealtime;
let chainManager = {
chainFetchers: chainFetchers,
isInReorgThreshold: chainManager_isInReorgThreshold,
isRealtime: chainManager_isRealtime
};
return [
{
ctx: state.ctx,
chainManager: chainManager,
rollbackState: state.rollbackState,
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
[{
TAG: "NextQuery",
_0: "CheckAllChains"
}]
];
case "ErrorExit" :
state.onError(action._0);
return [
state,
[]
];
case "SetRollbackState" :
return [
{
ctx: state.ctx,
chainManager: action.rollbackedChainManager,
rollbackState: {
TAG: "RollbackReady",
eventsProcessedDiffByChain: action.eventsProcessedDiffByChain
},
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
[
{
TAG: "NextQuery",
_0: "CheckAllChains"
},
"ProcessEventBatch"
]
];
}
}
};
}
function injectedInvalidatedActionReducer(markBatchProcessed, actionReducer) {
return (state, action) => {
if (typeof action === "object") {
switch (action.TAG) {
case "EventBatchProcessed" :
if (isPreparingRollback(state)) {
Logging.info("Finished processing batch before rollback, actioning rollback");
markBatchProcessed();
return [
{
ctx: state.ctx,
chainManager: ChainManager.updateProgressedChains(state.chainManager, action.batch),
rollbackState: state.rollbackState,
indexerStartTime: state.indexerStartTime,
writeThrottlers: state.writeThrottlers,
loadManager: state.loadManager,
keepProcessAlive: state.keepProcessAlive,
exitAfterFirstEventBlock: state.exitAfterFirstEventBlock,
onError: state.onError,
id: state.id
},
["Rollback"]
];
}
break;
case "ErrorExit" :
return actionReducer(state, action);
}
}
Logging.trace({
msg: "Invalidated action discarded",
action: S$RescriptSchema.convertOrThrow(action, Utils.Schema.variantTag)
});
return [
state,
[]
];
};
}
function checkAndFetchForChain(waitForNewBlock, executeQuery, state, dispatchAction) {
return async chain => {
let chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
if (isPreparingRollback(state)) {
return;
}
let fetchState = chainFetcher.fetchState;
let isRealtime = state.chainManager.isRealtime;
let reducedPolling = !isRealtime;
return await SourceManager.fetchNext(chainFetcher.sourceManager, fetchState, async query => {
try {
let response = await executeQuery(chainFetcher.sourceManager, query, fetchState.knownHeight, isRealtime);
return dispatchAction({
TAG: "ValidatePartitionQueryResponse",
_0: {
chain: chain,
response: response,
query: query
}
});
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
return dispatchAction({
TAG: "ErrorExit",
_0: ErrorHandling.make(exn, undefined, undefined)
});
}
}, knownHeight => waitForNewBlock(chainFetcher.sourceManager, knownHeight, isRealtime, reducedPolling), knownHeight => dispatchAction({
TAG: "FinishWaitingForNewBlock",
chain: chain,
knownHeight: knownHeight
}), state.id);
};
}
function injectedTaskReducer(waitForNewBlock, executeQuery, getLastKnownValidBlock) {
return async (state, task, dispatchAction) => {
if (typeof task !== "object") {
switch (task) {
case "ProcessEventBatch" :
if (state.ctx.inMemoryStore.isProcessing || isPreparingRollback(state)) {
return;
}
let match = state.rollbackState;
let isRollbackBatch;
isRollbackBatch = typeof match !== "object" ? false : match.TAG === "RollbackReady";
let batch = ChainManager.createBatch(state.chainManager, state.ctx.inMemoryStore.processedCheckpointId, state.ctx.config.batchSize, isRollbackBatch);
let progressedChainsById = batch.progressedChainsById;
let isBelowReorgThreshold = !state.chainManager.isInReorgThreshold && state.ctx.config.shouldRollbackOnReorg;
let shouldEnterReorgThreshold = isBelowReorgThreshold && ChainMap.values(state.chainManager.chainFetchers).every(chainFetcher => {
let chainAfterBatch = progressedChainsById[chainFetcher.fetchState.chainId];
return FetchState.isReadyToEnterReorgThreshold(chainAfterBatch !== undefined ? chainAfterBatch.fetchState : chainFetcher.fetchState);
});
if (shouldEnterReorgThreshold) {
dispatchAction("EnterReorgThreshold");
}
if (Utils.Dict.isEmpty(progressedChainsById)) {
if (EventProcessing.allChainsEventsProcessedToEndblock(state.chainManager.chainFetchers)) {
Logging.info("All chains are caught up to end blocks.");
if (!state.keepProcessAlive) {
updateChainMetadataTable(state.chainManager, state.ctx.inMemoryStore);
await InMemoryStore.flush(state.ctx.inMemoryStore);
return dispatchAction("SuccessExit");
} else {
return;
}
} else {
return;
}
}
let inMemoryStore = state.ctx.inMemoryStore;
inMemoryStore.isProcessing = true;
dispatchAction({
TAG: "UpdateQueues",
progressedChainsById: progressedChainsById,
shouldEnterReorgThreshold: shouldEnterReorgThreshold
});
InMemoryStore.setBatchDcs(inMemoryStore, batch);
let res;
try {
res = await EventProcessing.processEventBatch(batch, inMemoryStore, state.loadManager, state.ctx, state.chainManager.chainFetchers);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
let errHandler = ErrorHandling.make(exn, undefined, "A top level unexpected error occurred during processing");
return dispatchAction({
TAG: "ErrorExit",
_0: errHandler
});
}
if (res.TAG === "Ok") {
return dispatchAction({
TAG: "EventBatchProcessed",
batch: batch
});
} else {
return dispatchAction({
TAG: "ErrorExit",
_0: res._0
});
}
break;
case "Rollback" :
let match$1 = state.rollbackState;
if (typeof match$1 !== "object") {
if (match$1 === "NoRollback") {
return Stdlib_JsError.throwWithMessage("Internal error: Rollback initiated with invalid state");
} else {
return;
}
}
switch (match$1.TAG) {
case "ReorgDetected" :
let chain = match$1.chain;
let chainFetcher = ChainMap.get(state.chainManager.chainFetchers, chain);
dispatchAction("StartFindingReorgDepth");
let rollbackTargetBlockNumber = await getLastKnownValidBlock(chainFetcher, match$1.blockNumber, state.chainManager.isRealtime);
SourceManager.onReorg(chainFetcher.sourceManager, rollbackTargetBlockNumber);
return dispatchAction({
TAG: "FindReorgDepth",
chain: chain,
rollbackTargetBlockNumber: rollbackTargetBlockNumber
});
case "FoundReorgDepth" :
let rollbackTargetBlockNumber$1 = match$1.rollbackTargetBlockNumber;
let reorgChain = match$1.chain;
if (state.ctx.inMemoryStore.isProcessing) {
return Logging.info("Waiting for batch to finish processing before executing rollback");
}
let startTime = Hrtime.makeTimer();
let chainFetcher$1 = ChainMap.get(state.chainManager.chainFetchers, reorgChain);
let logger = Logging.createChildFrom(chainFetcher$1.logger, {
action: "Rollback",
reorgChain: reorgChain,
targetBlockNumber: rollbackTargetBlockNumber$1
});
Logging.childInfo(logger, "Started rollback on reorg");
Prometheus.RollbackTargetBlockNumber.set(rollbackTargetBlockNumber$1, reorgChain);
await InMemoryStore.flush(state.ctx.inMemoryStore);
let checkpointId = await state.ctx.persistence.storage.getRollbackTargetCheckpoint(reorgChain, rollbackTargetBlockNumber$1);
let rollbackTargetCheckpointId = checkpointId !== undefined ? checkpointId : 0n;
let eventsProcessedDiffByChain = {};
let newProgressBlockNumberPerChain = {};
let rollbackedProcessedEvents = 0;
let rollbackProgressDiff = await state.ctx.persistence.storage.getRollbackProgressDiff(rollbackTargetCheckpointId);
for (let idx = 0, idx_finish = rollbackProgressDiff.length; idx < idx_finish; ++idx) {
let diff = rollbackProgressDiff[idx];
let eventsProcessedDiff = Stdlib_Option.getOrThrow(Stdlib_Float.fromString(diff.events_processed_diff), undefined);
rollbackedProcessedEvents = rollbackedProcessedEvents + eventsProcessedDiff;
eventsProcessedDiffByChain[diff.chain_id] = eventsProcessedDiff;
newProgressBlockNumberPerChain[diff.chain_id] = rollbackTargetCheckpointId === 0n && diff.chain_id === reorgChain ? Primitive_int.min(diff.new_progress_block_number, rollbackTargetBlockNumber$1) : diff.new_progress_block_number;
}
let chainFetchers = ChainMap.mapWithKey(state.chainManager.chainFetchers, (chain, cf) => {
let newProgressBlockNumber = newProgressBlockNumberPerChain[chain];
if (newProgressBlockNumber === undefined) {
if (Primitive_object.equal(chain, reorgChain)) {
return {
logger: cf.logger,
fetchState: FetchState.rollback(cf.fetchState, rollbackTargetBlockNumber$1),
sourceManager: cf.sourceManager,
chainConfig: cf.chainConfig,
isProgressAtHead: cf.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: Primitive_int.min(cf.committedProgressBlockNumber, rollbackTargetBlockNumber$1),
numEventsProcessed: cf.numEventsProcessed,
reorgDetection: ReorgDetection.rollbackToValidBlockNumber(cf.reorgDetection, rollbackTargetBlockNumber$1),
safeCheckpointTracking: cf.safeCheckpointTracking
};
} else {
return cf;
}
}
let fetchState = FetchState.rollback(cf.fetchState, newProgressBlockNumber);
let newTotalEventsProcessed = cf.numEventsProcessed - eventsProcessedDiffByChain[chain];
if (cf.committedProgressBlockNumber !== newProgressBlockNumber) {
Prometheus.ProgressBlockNumber.set(newProgressBlockNumber, chain);
}
if (cf.numEventsProcessed !== newTotalEventsProcessed) {
Prometheus.ProgressEventsCount.set(newTotalEventsProcessed, chain);
}
let safeCheckpointTracking = cf.safeCheckpointTracking;
return {
logger: cf.logger,
fetchState: fetchState,
sourceManager: cf.sourceManager,
chainConfig: cf.chainConfig,
isProgressAtHead: cf.isProgressAtHead,
timestampCaughtUpToHeadOrEndblock: cf.timestampCaughtUpToHeadOrEndblock,
committedProgressBlockNumber: newProgressBlockNumber,
numEventsProcessed: newTotalEventsProcessed,
reorgDetection: Primitive_object.equal(chain, reorgChain) ? ReorgDetection.rollbackToValidBlockNumber(cf.reorgDetection, rollbackTargetBlockNumber$1) : cf.reorgDetection,
safeCheckpointTracking: safeCheckpointTracking !== undefined ? SafeCheckpointTracking.rollback(safeCheckpointTracking, newProgressBlockNumber) : undefined
};
});
let diff$1 = await InMemoryStore.prepareRollbackDiff(state.ctx.inMemoryStore, state.ctx.persistence, rollbackTargetCheckpointId, state.ctx.inMemoryStore.committedCheckpointId + 1n, newProgressBlockNumberPerChain);
let init = state.chainManager;
let chainManager_isInReorgThreshold = init.isInReorgThreshold;
let chainManager_isRealtime = init.isRealtime;
let chainManager = {
chainFetchers: chainFetchers,
isInReorgThreshold: chainManager_isInReorgThreshold,
isRealtime: chainManager_isRealtime
};
Logging.childTrace(logger, {
msg: "Finished rollback on reorg",
entityChanges: {
deleted: diff$1.deletedEntities,
upserted: diff$1.setEntities
},
rollbackedEvents: rollbackedProcessedEvents,
beforeCheckpointId: state.ctx.inMemoryStore.committedCheckpointId,
targetCheckpointId: rollbackTargetCheckpointId
});
Prometheus.RollbackSuccess.increment(Hrtime.toSecondsFloat(Hrtime.timeSince(startTime)), rollbackedProcessedEvents);
return dispatchAction({
TAG: "SetRollbackState",
rollbackedChainManager: chainManager,
eventsProcessedDiffByChain: eventsProcessedDiffByChain
});
case "RollbackReady" :
return Stdlib_JsError.throwWithMessage("Internal error: Rollback initiated with invalid state");
}
case "PruneStaleEntityHistory" :
let runPrune = async () => {
let safeCheckpointId = ChainManager.getSafeCheckpointId(state.chainManager);
if (safeCheckpointId === undefined) {
return;
}
await state.ctx.persistence.storage.pruneStaleCheckpoints(safeCheckpointId);
for (let idx = 0, idx_finish = state.ctx.persistence.allEntities.length; idx < idx_finish; ++idx) {
if (idx !== 0) {
await Utils.delay(1000);
}
let entityConfig = state.ctx.persistence.allEntities[idx];
let timeRef = Hrtime.makeTimer();
try {
await state.ctx.persistence.storage.pruneStaleEntityHistory(entityConfig.name, entityConfig.index, safeCheckpointId);
} catch (raw_exn) {
let exn = Primitive_exceptions.internalToException(raw_exn);
ErrorHandling.mkLogAndRaise(Logging.createChild({
entityName: entityConfig.name,
safeCheckpointId: safeCheckpointId
}), `Failed to prune stale entity history`, exn);
}
Prometheus.RollbackHistoryPrune.increment(Hrtime.toSecondsFloat(Hrtime.timeSince(timeRef)), entityConfig.name);
}
};
return Throttler.schedule(state.writeThrottlers.pruneStaleEntityHistory, runPrune);
}
} else {
switch (task.TAG) {
case "NextQuery" :
let chainCheck = task._0;
let fetchForChain = checkAndFetchForChain(waitForNewBlock, executeQuery, state, dispatchAction);
if (typeof chainCheck === "object") {
return await fetchForChain(chainCheck._0);
}
await Promise.all(ChainMap.keys(state.chainManager.chainFetchers).map(__x => fetchForChain(__x)));
return;
case "ProcessPartitionQueryResponse" :
processPartitionQueryResponse(state, task._0, dispatchAction);
return;
case "UpdateChainMetaDataAndCheckForExit" :
let chainManager$1 = state.chainManager;
let shouldExit = task._0;
if (typeof shouldExit === "object") {
return dispatchAction({
TAG: "ErrorExit",
_0: ErrorHandling.make(Stdlib_JsError.throwWithMessage(shouldExit._0), undefined, undefined)
});
}
if (shouldExit !== "ExitWithSuccess") {
return updateChainMetadataTable(chainManager$1, state.ctx.inMemoryStore);
}
updateChainMetadataTable(chainManager$1, state.ctx.inMemoryStore);
await InMemoryStore.flush(state.ctx.inMemoryStore);
return dispatchAction("SuccessExit");
}
}
};
}
function makeReducers(waitForNewBlockOpt, executeQueryOpt, getLastKnownValidBlockOpt, markBatchProcessed) {
let waitForNewBlock = waitForNewBlockOpt !== undefined ? waitForNewBlockOpt : SourceManager.waitForNewBlock;
let executeQuery = executeQueryOpt !== undefined ? executeQueryOpt : SourceManager.executeQuery;
let getLastKnownValidBlock = getLastKnownValidBlockOpt !== undefined ? getLastKnownValidBlockOpt : ChainFetcher.getLastKnownValidBlock;
let actionReducer = injectedActionReducer(markBatchProcessed);
return {
actionReducer: actionReducer,
invalidatedActionReducer: injectedInvalidatedActionReducer(markBatchProcessed, actionReducer),
taskReducer: injectedTaskReducer(waitForNewBlock, executeQuery, getLastKnownValidBlock)
};
}
export {
WriteThrottlers,
make$1 as make,
getId,
setChainManager,
isPreparingRollback,
updateChainMetadataTable,
validatePartitionQueryResponse,
submitPartitionQueryResponse,
processPartitionQueryResponse,
updateChainFetcher,
onEnterReorgThreshold,
injectedActionReducer,
injectedInvalidatedActionReducer,
checkAndFetchForChain,
injectedTaskReducer,
makeReducers,
}
/* Env Not a pure module */