stargately / blockroma

a open-source blockchain explorer built with typescript for Ethereum web3 compatible blockchains
https://blockroma.com
32 stars 20 forks source link

more indexer feature parity #40

Open doranoda opened 1 year ago

doranoda commented 1 year ago

Indexer

TODO: Add description

Structure

The indexer is split into multiple fetchers. Each fetcher has its own supervising tree with a separate TaskSupervisor for better detecting of memory, message or blocking problems.

Most fetchers have their Supervisor module generated automatically using use Indexer.Fetcher macro.

There are different fetchers described below, but the final step of almost all of them is importing data into database. A map of lists of different entities is constructed and fed to Explorer.Chain.import method. This method assigns different runners from Explorer.Chain.Import.Runner namespace, matching key in map to option_key attribute of a runner. The runners are then performing according to the order specified in stages in Explorer.Chain.Import.Stage.

Transformers

Some data has to be extracted from already fetched data, and there're several transformers in lib/indexer/transform to do just that. They normally accept a part of the Chain.import-able map and return another part of it.

Root fetchers

Both block fetchers retrieve/extract the blocks themselves and the following additional data:

The following stubs for further async fetching are inserted as well:

Realtime fetcher also immediately fetches from the node:

The following async fetchers are launched for importing missing data:

Async fetchers

These are responsible for fetching additional block data not retrieved in root fetchers. Most of them are based off BufferedTask, and the basic algorithm goes like this:

  1. Make an initial streaming request to database to fetch identifiers of all existing unfetched items.
  2. Accept new identifiers for fetching via async_fetch() method.
  3. Split identifier in batches and run tasks on TaskSupervisor according to max_batch_size and max_concurrency settings.
  4. Make requests using EthereumJSONRPC.
  5. Optionally post-process results using transformers.
  6. Optionally pass new identifiers to other async fetchers using async_fetch.
  7. Run Chain.import with fetched data.

Additionally:

Temporary workers

These workers are created for fetching information, which previously wasn't fetched in existing fetchers, or was fetched incorrectly. After all deployed instances get all needed data, these fetchers should be deprecated and removed.

Memory Usage

The work queues for building the index of all blocks, balances (coin and token), and internal transactions can grow quite large. By default, the soft-limit is 1 GiB, which can be changed in config/config.exs:

config :indexer, memory_limit: 1 <<< 30

Memory usage is checked once per minute. If the soft-limit is reached, the shrinkable work queues will shed half their load. The shed load will be restored from the database, the same as when a restart of the server occurs, so rebuilding the work queue will be slower, but use less memory.

If all queues are at their minimum size, then no more memory can be reclaimed and an error will be logged.

Websocket Keepalive

This defaults to 150 seconds, but it can be set via adding a configuration to subscribe_named_arguments in the appropriate config file (indexer/config//.exs) called :keep_alive. The value is an integer representing milliseconds.

Testing

Parity

Mox

This is the default setup. mix test will work on its own, but to be explicit, use the following setup:

export ETHEREUM_JSONRPC_CASE=EthereumJSONRPC.Case.Parity.Mox
mix test --exclude no_parity

HTTP / WebSocket

export ETHEREUM_JSONRPC_CASE=EthereumJSONRPC.Case.Parity.HTTPWebSocket
mix test --exclude no_parity
Protocol URL
HTTP http://localhost:8545
WebSocket ws://localhost:8546

Geth

Mox

export ETHEREUM_JSONRPC_CASE=EthereumJSONRPC.Case.Geth.Mox
mix test --exclude no_geth

HTTP / WebSocket

export ETHEREUM_JSONRPC_CASE=EthereumJSONRPC.Case.Geth.HTTPWebSocket
mix test --exclude no_geth
Protocol URL
HTTP https://mainnet.infura.io/8lTvJTKmHPCHazkneJsY
WebSocket wss://mainnet.infura.io/ws/8lTvJTKmHPCHazkneJsY