UNPKG

envio

Version:

A latency and sync speed optimized, developer friendly blockchain data indexer.

1,087 lines (1,067 loc) • 48.4 kB
// 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 */