Back to Blog
StreamingFast

Substreams sinks are now built in: Uniswap V4 indexed in under 8 minutes

We've folded substreams-sink-sql into the Substreams CLI, while greatly improving its speed and performance. You now run substreams sink postgres or substreams sink clickhouse directly, with no separate binary to install and no run vs from-proto decision to make. Pass a package and the mode is auto-detected.

The change is more than tidying. The new sink adds a disk spool, defers Postgres constraints until you hit chain head, and parses protobuf with a much faster unmarshaller. In a Uniswap V4 benchmark run by The Graph's BuildersDAO, a full Uniswap V4 backfill on Ethereum went from 23 hours on a subgraph to just 7 minutes 36 seconds on Substreams with a Pro Plan subscription. That's 182x faster!

The benchmark

BuildersDAO wanted a like-for-like comparison of subgraphs vs Substreams for Uniswap V4 indexing. Same events, same Postgres database, same block ranges: 21,688,328 to 23,535,547 on Ethereum (about 1.8M blocks) and 25,350,987 to 36,712,209 on Base (about 11.4M blocks). They reprocessed each range with a subgraph, then with Substreams on a cold cache at different worker counts, using Substreams v1.22.0 for the latest runs.

Bar chart comparing Uniswap V4 backfill times. On Ethereum, a subgraph takes 23 hours versus 7 minutes 36 seconds on Substreams, 182x faster. On Base, 94 hours 19 minutes versus 2 hours 6 minutes, 45x faster. Bar chart comparing Uniswap V4 backfill times. On Ethereum, a subgraph takes 23 hours versus 7 minutes 36 seconds on Substreams, 182x faster. On Base, 94 hours 19 minutes versus 2 hours 6 minutes, 45x faster.
NetworkSubgraphSubstreams, 75 workersSpeedup
Ethereum23h 00m7m 36s182x
Base94h 19m2h 06m45x

Benchmark environment: Ubuntu 24.04, PostgreSQL 16.14, 1.8 TB disk, Uniswap V4 events package, StreamingFast endpoints for Ethereum and Base mainnet.

More workers, more speed

Substreams parallelizes historical processing, and you control how far with a single header, X-Substreams-Parallel-Workers. Here is the full scaling curve from the v1.22.0 runs:

WorkersEthereum Network (time)vs subgraphBase Network (time)vs subgraph
18h 12m2.8x28h 41m3.3x
51h 37m14x26h 51m3.5x
1536m38x9h 10m10x
757m 36s182x2h 06m45x
3007m 30s*184x56m 38s100x

BuildersDAO benchmark · Substreams v1.22.0 · cold cache.

*Ethereum's gains flatten around 75 workers, while Base has far more data to chew through, so it keeps gaining from 75 to 300. The longer the backfill, the more the extra workers pay off.

Line chart of Uniswap V4 backfill time against parallel worker count, on a log scale. Ethereum falls from 8 hours 12 minutes at one worker to 7 minutes 36 seconds at 75 workers and flattens thereafter. Base falls from 28 hours 41 minutes to 56 minutes 38 seconds at 300 workers. Dashed lines mark each chain's subgraph baseline. Line chart of Uniswap V4 backfill time against parallel worker count, on a log scale. Ethereum falls from 8 hours 12 minutes at one worker to 7 minutes 36 seconds at 75 workers and flattens thereafter. Base falls from 28 hours 41 minutes to 56 minutes 38 seconds at 300 workers. Dashed lines mark each chain's subgraph baseline.

Log scale on the time axis; dashed lines are each chain's subgraph baseline.

At 300 workers, throughput hit 4,103 blocks/sec on Ethereum and 3,343 blocks/sec on Base, end to end into Postgres.

What changed in the sink

Earlier benchmark runs by BuildersDAO pointed us at the bottlenecks. Three changes account for most of the improvement since those runs:

  • A disk spool. Data downloads to a local spool (8 GiB by default) and is committed to the database asynchronously, so a slow database no longer stalls the stream. This alone delivers up to 10x on Postgres sink throughput. Make sure the sink host has disk headroom for the spool.
  • Constraints applied at chain head. Postgres constraints are no longer created at the start of a backfill. They're applied when you reach live blocks, or on demand with substreams sink postgres constraints apply, and they can be built in parallel. It's the same trick as loading Postgres without indexes and building them afterwards.
  • A faster protobuf unmarshaller. We replaced dynamicpb with hyperpb, an 18x improvement on parsing and one memory arena per block instead of thousands of allocations.

BuildersDAO also had a practical tip to share: ingest raw tables to chain head first, then pause and build your materialized views. Heavy materialized views running during the backfill were the bottleneck, and turning them off gave an immediate boost.

Migrating

Existing databases keep working. Cursor tables and schemas are unchanged, so you can pause a sink, update the CLI and resume from your cursor without reprocessing. The main differences:

BeforeNow
substreams-sink-sql run $DSN manifest.yaml 100:200substreams sink postgres manifest.yaml -s 100 -t 200 --dsn $DSN
substreams-sink-sql from-proto ...Auto-detected from the output module type
substreams-sink-sql setup $DSN manifest.yamlsubstreams sink postgres setup manifest.yaml --dsn $DSN
DSN as positional argument, or PG_DSN / CLICKHOUSE_DSN--dsn flag or SUBSTREAMS_SINK_DSN
--metrics-listen-addr--prometheus-addr

The migration guide covers every flag and Docker change. The substreams-sink-sql repo is being deprecated, and new SQL dialects now live in the substreams repo under sink/sql.

The Substreams skill is updated too

The Substreams skill for AI coding agents now teaches the built-in substreams sink postgres and substreams sink clickhouse commands, so agents like Claude Code generate current syntax instead of the deprecated binary.

To install the skill, follow the skills overview, or get all nine from the GitHub repo to turn yourself into a Substreams expert.

Try it

Update to the latest Substreams CLI (v1.22.0 or later), point substreams sink postgres or substreams sink clickhouse at your package, and set your worker count.

A big thank you to the BuildersDAO team for running this benchmark and for pushing us to look harder at where sync time could be improved. Their feedback shaped several of these changes.

Have results of your own? Come tell us in Discord.