cloud-sre

Erigon Mainnet アーカイブノードが 25393069 で止まったとき、chaindata だけを消して戻した記録

Ethereum Mainnet の Erigon アーカイブノードで、v3.5.0 が block 25393069 の gas used mismatch で止まった。snapshots を残し、chaindata だけを作り直して復旧したときの現場メモ。

Jun 30, 2026
EthereumErigonarchive-nodechaindatatroubleshooting

この障害は、最初は「1 ブロックだけ壊れている」ように見えた。

Ethereum Mainnet の Erigon アーカイブノードを erigontech/erigon:v3.5.0 に上げたあと、Execution stage が block 25393069 付近で毎回止まった。 一時的な peer 不足でも、ランダムな disk I/O の揺れでもない。 同じブロックで gas used mismatch が繰り返し出ていた。

最終的に効いたのは、ブロックをスキップすることでも、/data 全体を消すことでもなかった。 既存の snapshots は残し、/data/chaindata だけを削除して、Erigon に execution database を作り直させた。 その後、ノードは 25393069 を越え、RPC の高さは 25393999 まで commit され、さらに後続ブロックの実行も続いた。

用語

用語意味
Archive node / アーカイブノード過去の receipt、trace、historical state query を返すために履歴状態を保持するノード。
ErigonEthereum execution client のひとつ。headers、bodies、senders、execution、trie、indexes などを stage に分けて同期する。
Datadirノードのデータディレクトリ。このケースでは /data。snapshots、chaindata、logs、downloader data が入る。
chaindataErigon の主要な execution database。削除すると execution state は作り直しになるが、snapshots 全体を消すわけではない。
Snapshot事前に構築された chain data。これがあると、genesis から昔ながらの線形 replay をする必要が小さくなる。
Gas-used mismatchノードがブロックを実行して計算した gas used が、block header の値と一致しない状態。execution client はそのブロックを無効として扱う。
Staged syncErigon の同期方式。HeadersBodiesSendersExecutionTxLookupFinish が別々の進捗を持つ。

症状

起動形態は Ethereum Mainnet の archive node として普通のものだった。

erigontech/erigon:v3.5.0
--chain=mainnet
--datadir=/data
--prune.mode=archive

datadir は古いバージョンから in-place upgrade したもの。 アップグレード後、Erigon は DB を開けるし、ローカル snapshots も見えている。 しかし execution が同じブロックを越えられなかった。

中心のエラーはこれだった。

gas used mismatch block=25393069 header=20304193 execution=20137672
[4/6 Execution] rw exit err="invalid block, block=25393069, invalid block, gas used by execution: 20137672, in header: 20304193"
[4/6 Execution] Execution failed err="invalid block, block=25393069 ..."
Cannot update chain head err="updateForkChoice: [4/6 Execution] invalid block, block=25393069 ..."

eth_syncing の stage も同じ場所で止まっていた。

Headers     25393067
Bodies      25393067
Senders     25393067
Execution   25393067
Finish      25393067

ノード全体が死んでいるわけではない。 25393069 近辺で安定して失敗していた。

なぜブロックをスキップしなかったか

execution client は、失敗したブロックを安全にスキップできない。

もし 25393069 の実行結果が header と一致しないなら、その後の state root、receipts、traces も信用できない。 archive node ではなおさら、後続の historical state や trace が前の execution state に依存している。

現実的な選択肢は二つだけだった。

  1. bad-block marker、index、local stage の問題だと切り分けて、その層だけを直す。
  2. 現在の execution database を捨て、信頼できる snapshots と block data から作り直す。

先に試した軽い修復

Erigon の image には integration tool が入っている。 まず存在を確認した。

docker exec erigon command -v integration
docker exec erigon integration --version

次に、主プロセスと integration が同時に /data を書かないように Erigon を止める。

docker stop erigon

stage の状態を確認する。

docker run --rm \
  -v /data:/data \
  --entrypoint integration \
  erigontech/erigon:v3.5.0 \
  print_stages --datadir=/data

この時点で Execution25393067。 ローカルの block snapshots と DB 内の headers/bodies は、その周辺より先まで存在していた。

bad-block marker を消す。

docker run --rm \
  -v /data:/data \
  --entrypoint integration \
  erigontech/erigon:v3.5.0 \
  clear_bad_blocks --datadir=/data

これは BadHeaderNumber table を clear した。

続いて senders を補う。

docker run --rm \
  -v /data:/data \
  --entrypoint integration \
  erigontech/erigon:v3.5.0 \
  stage_senders --datadir=/data --chain=mainnet --block=25393070

最後に unwind して execution を再実行した。

docker run --rm \
  -v /data:/data \
  --entrypoint integration \
  erigontech/erigon:v3.5.0 \
  stage_exec --datadir=/data --chain=mainnet --unwind=100 --block=25393070

ここでの教訓は、stage_exec は短時間で終わる修復ボタンではない、ということ。 MDBX に大量の書き込みが続き、docker stats の Block I/O は数百 GB まで増えた。 それだけで hung とは言えない。 ただ、復旧判断を急ぐ現場では、短時間で明確な結果を返す手段でもなかった。

docker run --rm で一時的な integration container を動かす場合は、終了時の情報も取り逃しやすい。

  • container は exit 後すぐ消えることがある。
  • docker wait や log follow を先に仕込んでおくと、exit code と最後の log を残しやすい。

chaindata 削除に切り替えた理由

上流 issue には、snapshots を残して chaindata を削除すると復旧した、という報告があった [1]

これはノード全体の削除ではない。 対象はここだけ。

/data/chaindata

触らないものは次の通り。

/data/snapshots
/data/downloader
/data/logs

snapshots が残っていれば、Erigon はローカルデータから execution state を作り直せる。 もちろんコストはある。 Execution History、trie、indexes、TxLookup は再構築される。 それでも /data 全体を消すよりはずっと小さい。

実際の手順

まず、Erigon や integration が datadir を書いていないことを確認する。

docker ps -a --format 'table {{.Names}}\t{{.Status}}\t{{.Command}}'
ps -eo pid,stat,pcpu,pmem,etime,args | grep -E 'erigon|integration stage_exec' | grep -v grep

一時的な execution container を止める。

docker stop -t 60 <temporary-integration-container>

stage_exec が残っていないことを確認する。

ps -eo args | grep -E 'integration stage_exec' | grep -v grep

chaindata だけ削除する。

rm -rf /data/chaindata
test ! -e /data/chaindata && echo chaindata_deleted

Erigon を起動する。

docker start erigon

起動直後、RPC が一時的に 0x0 を返すことがある。 これは execution database を消した直後なので自然な状態。 stage がもう一度進む必要がある。

{
  "currentBlock": "0x0",
  "stages": [
    { "stage_name": "Execution", "block_number": "0x0" },
    { "stage_name": "TxLookup", "block_number": "0x0" },
    { "stage_name": "Finish", "block_number": "0x0" }
  ]
}

復旧中に見えたログ

最初の良いサインは、ローカル snapshots を再利用していることだった。

[1/6 OtterSync] Skipping SyncSnapshots, local preverified. Use snapshots reset to resync

次に execution history を補う。

Downloading Execution History progress=30363/36158

この間、eth_blockNumber が動かないことがある。 それだけでは失敗ではない。

その後、block insert と execution に戻った。

[BlockCollector] Inserting blocks from=25392000 to=25392999
[BlockCollector] Inserting blocks from=25393000 to=25393999
[4/6 Execution] parallel starting from=25392365 to=25393999

本当に見たいのは、元の失敗ブロックを越えたかどうか。

[4/6 Execution] parallel executed blk=25392996
[4/6 Execution] parallel executed blk=25393199

2539319925393069 より大きい。 この時点で、再構築後の execution は元の失敗点を越えた。

その後、RPC stage も commit された。

eth_blockNumber = 0x1837b4f
Execution      = 0x1837b4f
TxLookup       = 0x1837b4f
Finish         = 0x1837b4f

0x1837b4f25393999。 ここまで、古い gas used mismatch block=25393069 は再発していない。

再利用できる runbook

Erigon archive node が同じ execution block で繰り返し止まる場合、私はこの順序で見る。

  1. 正確な failure log を残す。block number、header gas、execution gas を記録する。
  2. 実際に動いている image と起動引数を確認する。Terraform や source tree の期待値だけを見ない。
  3. eth_syncingintegration print_stages で、どの stage が止まっているかを見る。
  4. bad-block marker が疑わしいなら clear_bad_blocks を試す。ただし、それで必ず復旧するとは考えない。
  5. stage_exec を動かす前に、datadir を書く process が一つだけであることを確認する。
  6. reset するなら /data/chaindata だけを削除し、snapshots は残す。
  7. restart 後は eth_blockNumber だけでなく、Execution HistoryExecutionTxLookupFinish を見る。
  8. 最後は、元の失敗ブロックを越えたかどうかで判断する。

一番重要な境界は単純だ。 主 Erigon と integration command に同時に /data を書かせない。

この障害から残した判断

固定ブロックの gas-used mismatch は、普通の再起動だけでは説明しにくい。 client の execution rule か、現在の client とローカル execution database の組み合わせが壊れている可能性が高い。

今回、v3.5.0 でも 25393069 の失敗は再現した。 軽い command で marker を消したり一部 stage を再実行したりはできたが、復旧時間としては明確な答えにならなかった。 chaindata を削除すると、Erigon はローカル snapshots から作り直し、失敗ブロックを越えた。

次に同じ形を見るなら、判断順はこうする。

  • fixed-block failure かを確認する。
  • 同じ signature の upstream report を探す。
  • 低リスクの確認と marker cleanup を先にやる。
  • reset が必要なら、必要な層だけ reset する。

きれいな修正ではない。 ただし、境界が明確で、検証点もはっきりした workaround だった。

参考資料

  1. erigontech/erigon issue #22019: sync stopped around block 25393072/25393073
  2. Erigon v3.5.0 integration command README
  3. Erigon v3.5.0 release