
The whole series, in one picture
RECOVERING "CURRENT STATE" AFTER AN OUTAGE
graph-node replay
~102,000,000 blocks replayed from block 8,738,570
snapshot indexer
~5 batched Multicall3 requests, current state only
Same question — “what's staked right now?” — two very different bills.
This is the entire argument of the series: graph-node can only recover by re-deriving history event by event. A snapshot just asks the chain what's true right now.
The Hidden Cost of Self-Hosting graph-node
It worked. That’s not the same as it being cheap to keep working.
In Part 1, we walked through what a subgraph is and how FaraLand’s staking backend used one — five staking contracts, watched by a self-hosted graph-node instance, deployed through a self-hosted IPFS node. This post is about why that setup, despite doing its job, became a problem worth solving.
We’re running a general-purpose indexing platform for five contracts
graph-node is not a lightweight script. It’s a full indexing platform designed to host any number of arbitrary subgraphs for any number of tenants, and it needs real infrastructure to do that
| Component | What it’s for |
|---|---|
| Database | Every entity, every block cursor, the full indexed history |
| IPFS node | Subgraph manifests & compiled WASM mappings, content-addressed and pulled at deploy time |
| BSC RPC access | Continuous access — and specifically, wide historical eth_getLogs support |
All three of those have to be up, monitored, patched, and paid for, all the time — regardless of how much traffic the staking page is actually getting. We were operating a multi-tenant indexing engine to serve exactly one purpose: decode events from five contracts into a handful of entity types. That’s a lot of general-purpose machinery for a fairly narrow, fixed job.
The real pain point: recovery means starting from block zero
Here’s the part that actually forced the decision. graph-node’s indexing is fundamentally a replay log. If something goes wrong — a schema change, a corrupted index, a graph-node version upgrade that needs a clean re-sync, moving to new infrastructure — there is no “resume from where we mostly were.” The only supported recovery path is: redeploy the subgraph, and it starts over from startBlock, replaying every single matching event from scratch, in order, through WASM, before the API is serving current data again.
For most of our data sources that’s already a lot of blocks. For the oldest one — OldStaking, which starts at block 8,738,570 — that means replaying essentially the entire staking history of the contract, out of well over a few hundred million blocks BSC has produced since then. That’s not a five-minute restart. Depending on RPC throughput, that’s a process that can run for hours, sometimes much longer, during which the staking dashboard is either down or silently serving stale data.
The moment we actually need a full historical replay to go fast is also the moment we're most likely to get throttled by the RPC endpoint we're depending on.
Public RPCs make it worse, exactly when we need it most
Historical replay leans hard on eth_getLogs over wide block ranges — and that’s precisely the call most RPC providers restrict the hardest. Range caps vary wildly between providers (anywhere from a few hundred blocks to several thousand, and rarely documented up front), rate limits kick in under sustained load, and some providers explicitly tier “current state” access differently from “historical/archive” access, sometimes behind a separate paid plan entirely.
None of this is a graph-node bug
To be clear: none of this is graph-node behaving incorrectly. It’s built to be a robust, general-purpose, multi-tenant indexing platform, and it’s good at that job. The mismatch was on our side — we were paying the operational cost of running that entire platform, with its recovery model, to serve a job that’s actually much narrower: keep five contracts’ worth of staking state up to date and queryable.
That mismatch raised an obvious question: if all we actually need is “current, correct state, queryable over GraphQL, that recovers in seconds instead of hours” — do we need graph-node’s replay-from-genesis model to get there? Or could we ask the chain a different kind of question — not “replay everything that happened,” but “what is true right now” — and start from there?
That question is what Part 3 is about.
**Disclaimer: Digital asset prices are subject to high market risk and price volatility. $FARA token and NFT are designed solely for use within our on-chain gaming ecosystem and is intended exclusively for participation in gameplay experiences. They are not, and should not be considered as, an investment vehicle, security, financial instrument, or speculative asset. Holders should have no expectation of profit, return on investment, or appreciation in value from the purchase, holding, or use of this token and NFT.
