Solana infrastructure, included with every server. LEARN MORE>

OpenInfra.shopeninfra.sh
Engineering10 min read

What Makes a Solana Indexer Reliable?

OpenInfra Team·August 6, 2026
What Makes a Solana Indexer Reliable?

A Solana indexer is easy to start and hard to trust.

The first version is usually straightforward: connect to a live feed, decode events, and write useful records to a database. The product immediately feels more capable. It can show activity, balances, positions, trades, history, or analytics without asking a chain endpoint to do every piece of work at page-load time.

The difficult questions arrive later. What happens when the consumer restarts halfway through a batch? What does the database contain after a network interruption? How does the system prove that it did not skip a slot, count an event twice, or build a user-facing view from incomplete state?

A reliable indexer is built around those questions. Receiving data quickly matters. Knowing what has been durably processed matters more.

An indexer is a system of records

An indexer is not only a listener. It turns chain data into records that another application can query and trust.

That normally involves several stages: receive data, select the records that matter, decode them, store a durable representation, update derived application views, and record progress. Each stage can fail independently. A stream can be healthy while a database is slow. A database write can succeed while a derived view update fails. A process can stop after persisting raw data but before advancing its checkpoint.

The design must account for these partial states. If it does, failures are routine operational events. If it does not, a restart can become a manual investigation.

The stream is an input, not the database

Yellowstone gRPC is a useful input for indexers that continuously consume filtered accounts, transactions, and blocks. It gives the consumer a real-time feed of the information it has chosen to follow.

But a stream cannot be the indexer’s only memory. Connections reconnect. Deployments replace processes. Consumers can stop while work is in flight. A message may be delivered again after recovery. The indexer needs a durable state of its own that survives the lifecycle of any individual connection.

The simple model is: the stream tells the indexer what to consider next; the database records what the indexer has accepted as processed.

Start with the data contract

Before writing a parser, describe what the indexer promises to produce.

For a protocol, that may be a list of swaps, positions, liquidity changes, or program-specific actions. For a wallet product, it may be transaction history, balances, token metadata, and activity timelines. For analytics, it may be normalised events that can be grouped later in many ways.

Then decide which source records are sufficient to explain each output. Keep stable identifiers for the original transaction, instruction, account, and slot where appropriate. If someone asks why a balance or position changed, the product should be able to trace that result back to a clear chain record and parsing rule.

This is why raw records and derived application state should not be treated as the same thing.

Store raw input and derived state separately

Raw input is the durable chain-facing record: the transaction, account update, block information, or parsed event payload that the indexer received. Derived state is the representation the product wants to query: a current balance, open position, portfolio history, leaderboard, or chart series.

Keeping both layers gives the team room to improve. A new product feature may need a different aggregation. A parser bug may require rebuilding only a derived table. A business rule may change while the underlying chain records remain the same. Without raw input, those changes can require rediscovering data or replaying an uncertain historical path.

The raw layer does not have to preserve every byte forever. It should preserve enough information to reproduce and explain the views the product relies on. The exact retention policy depends on the workload, storage budget, and compliance needs. The principle is stable: application state should be reproducible from an authoritative ingestion record.

Give every record a stable identity

An event can be delivered twice. A recovery pass can read data that was already written. A process can write the first half of a batch and stop before writing the second half.

None of these should corrupt the product.

Use stable identifiers so the same logical record maps to the same database entry. Depending on the data model, that can include a transaction signature, instruction index, account address, slot, program identifier, or a combination of them. Then make writes idempotent: processing the same input again should result in the same final state.

Idempotency does not mean ignoring every duplicate blindly. It means the system can recognise whether a record already exists and apply a defined rule. Some records may be updated when later information is available. Some may be immutable. The important part is that recovery does not create a second version of the same chain event just because the consumer ran twice.

A checkpoint proves progress

A checkpoint is the point in the chain that the indexer knows it has fully processed.

For a slot-oriented ingestion process, store the last slot through which the relevant work is complete. Advance the checkpoint only after the associated raw records and required derived writes have succeeded. If a transaction contains several events, do not mark the enclosing unit complete until the records your contract requires are safely stored.

This turns a restart into a deterministic operation. Read the checkpoint, resume from that point, and process forward. There is no need to guess from application logs which message might have been the last good one.

The checkpoint must be durable. Keeping it only in process memory removes its value. Updating it in a separate, uncoordinated process can also create a gap between “the indexer says it processed this” and “the data is actually present.” Where possible, make the progress update part of the same durable workflow as the data it represents.

Think in terms of at-least-once delivery

Reliable systems often accept that an input may be seen more than once. The goal is not to force a fragile illusion of exactly-once delivery across networks, processes, and databases. The goal is to make repeated processing safe and visible.

This mindset simplifies recovery. The consumer can replay a small range around its checkpoint. It can retry writes after a timeout. It can restart a worker without requiring a special reconciliation script. The database, rather than a transient connection, becomes the place where the system decides whether a record is new.

The trade-off is deliberate data modelling. Stable keys, uniqueness rules, and transactional writes take thought. They are still cheaper than repairing a silently inconsistent history.

Use RPC to close a gap

Live ingestion makes the indexer current. RPC gives it a confirmed reference point.

After a disconnect, restart, or deployment, do not assume that a newly opened stream has filled every interval since the last consumer stopped. Use the saved checkpoint to identify the range that needs attention. Read confirmed blocks, transactions, or account state through RPC as appropriate for the indexer’s contract. Store or reconcile the missed records, then resume the live consumer.

The exact repair method differs by workload. A transaction indexer may fetch a block range. An account- state indexer may read the latest confirmed account data and compare it with its stored representation. A program-specific service may replay known relevant transactions. What matters is that the recovery path is designed before the first incident.

Ordering and commitment need explicit rules

Chain data is not useful merely because it arrived first. The indexer must decide which commitment level its product represents and how it handles changes that can occur before finality.

For a user-facing history or accounting-like record, a confirmed state model is usually easier to explain. For a low-latency product, the system may keep provisional information separately and then reconcile it as confirmation arrives. Do not blend the two without labels. Users and downstream services need to know whether a record is an early observation, a confirmed event, or a final historical record.

The same applies to ordering. Make the record model explicit enough that sorting and replay are deterministic. If the product exposes events within a transaction, preserve the information needed to order them consistently rather than relying on the incidental arrival order of worker processes.

Backpressure is an application feature

Every indexer must have a response when records arrive faster than a downstream stage can complete them.

Queues are useful, but an unbounded queue only delays failure. Define limits. Measure queue depth, oldest unprocessed item, processing latency, database write time, and the distance between the latest available data and the checkpoint. Decide whether the worker should slow intake, increase parallelism within safe limits, or pause and recover from a durable checkpoint.

The correct response depends on the service. A noncritical analytics pipeline can often catch up later. A risk system may need alerting well before lag becomes material. In either case, the indexer should make its state observable. “Connected” is not enough; operators need to know whether the consumer is actually keeping up.

Rebuilds should be normal, not frightening

At some point, a parser will change. A protocol will introduce a new instruction. A derived view will need a better schema. A bug will be found in an aggregation.

Build the indexer so that rebuilding is a supported operation. Version parsers and transformations. Keep the input and output layers distinct. Make a replay of a defined range possible in a separate environment before it touches production tables. Track the software version or transformation version that produced important derived records when that information will help debugging.

This is not unnecessary ceremony. It is how a data product remains correct as both the chain and the application evolve.

The practical rule

A reliable indexer can answer a small set of questions at any time:

  • What data is this consumer responsible for?
  • What is the last point it fully processed?
  • Can the same event be applied again safely?
  • How does it repair a gap after a restart?
  • Can the product explain how a derived record was produced?

When those answers are clear, a fast stream becomes a dependable system. The application is no longer merely receiving Solana data. It is maintaining a record of the chain activity its users and services rely on.

Test recovery before production depends on it

Recovery is easiest to trust when it has been exercised deliberately. Stop a consumer while it has work in flight. Restart it from its stored checkpoint. Simulate a database timeout. Re-run a small slot range. Then compare the resulting records with an independently read confirmed reference.

These tests expose assumptions that normal happy-path traffic never reaches: checkpoints moved too early, duplicate keys that are not actually unique, queues that disappear during a deploy, or derived state that does not rebuild from its raw records. Record the expected result of each test so it becomes part of the service’s operating knowledge, not a one-time debugging session.

An indexer does not become reliable because it never fails. It becomes reliable because a failure has a bounded, rehearsed path back to correct state.

Document that path alongside the code. State the data source, filter, commitment rule, checkpoint location, replay procedure, and the signals that mean the consumer is unhealthy. These details are operational requirements, not implementation trivia. They let a new engineer understand the system without reconstructing its safety properties from production incidents.

That clarity is the real output of indexer engineering: not only data in a table, but a system the team can verify, repair, and safely change.

It makes the product safer for users and calmer for the people who operate it.

Correctness should remain observable every day.

The value appears over time. A well-designed indexer lets a team ship new views, investigate an unexpected record, replay a corrected parser, and recover from an ordinary deployment without turning any of those tasks into a chain-wide emergency.

Get Started

Stop paying for RPC.

Every server includes the full Solana stack. See how the economics work in practice.