How to Benchmark Subgraph Performance and Reliability

Subgraph benchmarks only test a few queries on a warm cache. Learn how to properly benchmark subgraph performance by measuring indexing speed, time to chain head, block lag, query latency under load, and recovery, so you can choose infrastructure that holds up in production.

How to Benchmark Subgraph Performance and Reliability

The typical way to assess subgraph performance is to deploy a popular subgraph, run a few GraphQL queries, and compare response times. But that's an incomplete testing methodology.

A common reason why a subgraph provider might seem faster can be boiled down to any of these reasons:

  • warm cache,
  • an existing deployment,
  • a different RPC provider, or
  • a database that has already indexed the same workload.

If you are choosing infrastructure for a mission-critical system, these are the criteria you should go by:

  • How quickly can a new deployment index?
  • How closely does it stay behind the chain head?
  • What happens when activity spikes?
  • How fast are real application queries?
  • Does indexing recover when something goes wrong?
  • Can you trust the data after reorgs and restarts?

A useful benchmark should test the whole lifecycle of a subgraph rather than a single query.

We wrote this guide to cover a straightforward way to measure subgraph performance.

Rule #1: Start with the same subgraph

The most important rule is simple: compare the same workload.

Both providers should receive the same:

  • subgraph.yaml
  • schema
  • mappings
  • ABIs
  • start block
  • contract addresses
  • network
  • deployment configuration

Small differences can materially change performance.

A subgraph with five event handlers is not comparable to one with fifteen. Moving the start block forward can remove millions of blocks from the sync. Adding an eth_call inside a handler can make indexing considerably slower.

For provider comparisons, deploy from the same source commit.

Uniswap, ENS, Aave, and other well-known subgraphs look attractive for benchmarks because they have a lot of data.

But these are poor choices for measuring fresh indexing performance.

A provider may already have:

  • indexed the same deployment
  • cached the underlying data
  • warmed database pages
  • optimized the workload specifically
  • retained data from another customer using the same deployment

You can end up measuring existing data rather than indexing.

A better test uses a contract unlikely to have been indexed by either provider.

Ideally, use one of your own contracts. Otherwise, choose a relatively obscure contract with enough historical activity to create a meaningful workload.

You want enough data to expose differences between systems without accidentally testing an already-warm dataset.

Rule #3: Benchmark indexing and querying separately

People often say one provider is "faster" without specifying what they mean.

There are at least two different measurements.

Indexing performance is how quickly blockchain data is processed and written into the subgraph database.

Query performance is how quickly you can retrieve that indexed data through GraphQL.

Treat them as separate benchmarks.

Rule #4: Measure historical indexing speed

You want to look for:

  • deployment time
  • time until the first block is processed
  • blocks indexed per second
  • total time to reach chain head

For entity-heavy subgraphs, blocks per second alone can be misleading. One block might contain no relevant events while another produces thousands of entity writes.

If you can measure it, also track:

  • events processed per second
  • entities written per second
  • database growth
  • number of contract calls made during indexing

The most useful number is usually time to chain head, because that gives an estimate of the real cost of deploying a new subgraph or recovering from a reindex.

Rule #5: Measure chain-head freshness

Historical sync speed tells you how quickly a deployment catches up. Once it reaches head, a different question matters:

How far behind the blockchain does it normally run?

For a live application, this is often more important than initial indexing speed.

Query _meta periodically:

{
  _meta {
    block {
      number
      timestamp
    }
  }
}

Compare the indexed block against the current chain head.

Useful metrics include:

  • median block lag
  • p95 block lag
  • maximum observed lag
  • time required to recover after falling behind

On fast chains, block count alone can also be misleading. Convert the difference into seconds where possible.

A subgraph that stays one block behind on Ethereum behaves very differently from one that stays one block behind on a chain producing blocks several times per second.

Test during high activity

To get the most accurate results, run tests when the chain has high load.

Watch the deployment through periods with:

  • high transaction volume
  • network congestion
  • protocol launches
  • incentive campaigns

You want to know whether lag accumulates under load and, if it does, how quickly the system catches up.

Rule #6: Test concurrency

A 30 ms response time at one request per second does not mean the endpoint can sustain 500 requests per second.

Increase load gradually.

For example:

1 RPS
10 RPS
50 RPS
100 RPS
250 RPS

At each level, record latency and errors.

What you are looking for is the point where:

  • latency starts increasing sharply
  • requests begin timing out
  • rate limits appear
  • errors increase
  • indexing performance is also affected by query traffic

On some architectures, heavy querying and indexing compete for database or compute resources. A good assessment should therefore monitor chain-head lag at the same time.

You may find that query latency remains acceptable while the indexer struggles to keep up with the chain tip.

Do not optimize the benchmark for the result you want

Different subgraphs stress different parts of the system.

One may be RPC-heavy because its mappings make many contract calls. Another may be write-heavy because every event updates several entities. Another may index quickly but expose expensive GraphQL relationships.

If the workload matters to your application, include it.

For a serious evaluation, testing two or three different subgraph profiles is much better than deciding based on one type of subgraph.

What a good result looks like

There is no single metric that makes a subgraph provider "fast."

Ideally, you want a combination of:

  • fast historical sync
  • consistently low chain-head lag
  • predictable query latency
  • enough query throughput for peak load
  • graceful behavior when traffic increases
  • reliable recovery
  • accurate canonical data through reorgs

Obviously, this depends on the application. For example, an analytics dashboard may tolerate several seconds of additional indexing lag but care about large historical queries.

A trading system may care far more about freshness, while a consumer application may care primarily about p95 query latency and uptime.

About Ormi

Ormi is the next-generation data layer for Web3, purpose-built for real-time, high-throughput applications like DeFi, gaming, wallets, and on-chain infrastructure. Its hybrid architecture ensures sub-30ms latency and up to 4,000 RPS for live subgraph indexing.

With 99.9% uptime and deployments across ecosystems representing $50B+ in TVL and $100B+ in annual transaction volume, Ormi is trusted to power the most demanding production environments without throttling or delay.