---
title: "ヒストリカルログを高速化するために行った再構築"
description: "eth_getLogs の背後にあるストレージとクエリエンジンを再構築し、大規模範囲のヒストリカルログ読み取りの p99 レイテンシを全リージョンで2倍以上高速化しました。"
---

# ヒストリカルログを高速化するために行った再構築

<ImageBlock
  src="https://media.alchemy.com/blog/how-we-rebuilt-historical-logs-for-speed-16x9-20260821.png"
  alt="履歴ログを高速化のために再構築した方法"
  width={1920}
  height={1080}
  priority
/>

インデクサー、DEXアグリゲーター、オンチェーン監視システムを構築しているチームに、アプリのどこで速度が落ちるか尋ねれば、返ってくる答えはほぼ決まっている。大きなレンジの`eth_getLogs`だ。

これは、数千から数十万ブロックをスキャンして一つの質問に答える呼び出しだ。先月このコントラクトに当たった transfer はどれか。このレンジでどのプールの状態が変わったか。ローンチ以降このアドレスでどのイベントが発火したか。こうした読み取りは実際のアプリケーションの根幹であり、同時に最も詰まりやすい読み取りでもある。そこで我々はこれらを処理するエンジンを再構築し、p99でのメソッドの速度を全リージョンで2倍以上に高速化した。その方法を説明する。

## eth_getLogsとは何か、なぜ重要なのか

`eth_getLogs`は、スマートコントラクトが実行時に発するイベント(トークンの転送、スワップ、承認、あるいはコントラクトが定義する任意のカスタムイベント)を返すJSON-RPCメソッドだ。コントラクトアドレス、イベントシグネチャやインデックス化されたトピックの値(`Transfer`や`Approval`など)、そして`fromBlock`と`toBlock`によるブロックレンジでフィルタする。平たく言えば、何を探すか、どこまで遡るかを指定すれば、条件に一致するイベントをすべて返してくれる。

これはEthereumにおけるイベント駆動型インデックスの基盤だ。これがなければ、目的のトランザクションを見つけるために全てのトランザクションを手作業でスキャンすることになる。これがあれば、アクティビティフィードや転送フィード、承認の監視、リアルタイムまたは過去分のイベントインデクサーを構築できる。我々が提供する中でも特に利用頻度が高く、運用上重要なメソッドの一つだ。ログとトピックについて詳しくない場合は、[`eth_getLogs`の詳細解説](https://www.alchemy.com/docs/reference/eth-getlogs)でリクエストとレスポンスの構造を実例とともに解説している。

このメソッドの残りの部分が、エンジニアリング上の課題の形を決めている。多くのプロバイダーは一度にクエリできるブロック数に上限を設けている。インデクサーはチェーンの reorg に対応するため、確認数を待つ必要がある。トピックフィルタはインデックス化されたフィールドにしか一致しないため、インデックス化されていない引数は生データからデコードする必要がある。そしてチェーンヘッド付近の呼び出しはレイテンシに敏感で、誤ったノードタイプへのルーティングはエラーや空のレスポンスを引き起こしかねない。

レイテンシに最も影響するのはレンジのサイズだ。1ブロックのリクエストなら瞬時に返る。しかし履歴をリプレイするインデクサーや、数週間にわたってコントラクトを監視するモニターにとって、レンジはすぐに広がり、クエリのコストもそれに応じて上がる。ここで、レイテンシは誤差の範囲を超え、顧客がどれだけ速く構築できるかを左右する要因になる。

このメソッド自体は誰でも提供している。しかしその裏にあるエンジンについて語る者は少ない。エンジンの再構築にはコストがかかり、ほとんどのプロバイダーはそこまで手をつけていないからだ。

## なぜ平均レイテンシよりp99レイテンシの方が重要なのか

痛みを引き起こすのは平均値ではない。p99だ。すべてのリクエストを速い順から遅い順に並べたとき、p99は100件のうち最も遅い1件を指す。平均値はこれを覆い隠す。99件のリクエストを60ミリ秒で処理していても、100件目が丸々1秒かかることはあり得る。

その最も遅いリクエストこそが、ダッシュボードをフリーズさせたり、インデクサーをチェーンから遅れさせたりするものであり、ユーザーが実際に目にしているものだ。平均レイテンシは問題なく見えていても、テール(裾)が静かにワークロードを壊していく。インデクサーやモニターを運用するチームにとって、テールこそがワークロードそのものだ。

## eth_getLogsをどのように最適化したか

我々はこのメソッドを処理するストレージとクエリエンジンを、既存のものをチューニングするのではなく、一から再構築した。

このような呼び出しを高速化する方法はいくつかある。クエリパラメータを調整する、キャッシュを追加する、受け付けるレンジに上限を設ける、あるいは単純に同じハードウェアを増強する、といった手だ。しかしこれらの多くは、根本を変えずに多少の余裕を得るだけに終わる。

我々はさらに踏み込んだ。過去のログ読み取りを他のワークロードと同じように扱う汎用のマネージドデータベースに頼るのではなく、`getLogs`のアクセスパターン(非常に大規模なデータセットに対する高速なフィルタ済みスキャン)専用にチューニングされたストレージとクエリレイヤーを、我々自身が運用する専用インフラ上で稼働させている。

既存システムのつまみを調整したり、キャッシュを継ぎ足したりしたわけではない。顧客が行う最も重要な呼び出しの一つを支えるエンジンそのものに、メソッド単位で投資した。これはコストのかかる、メソッド単位のインフラ整備であり、だからこそ改善が平均値だけでなくテールに表れる。

## 結果はどうなったか

要点はシンプルだ。p99で2倍以上高速化した。全リージョンで。

インデクサーやモニターを運用しているなら、ワークロードを壊すのはp99のレイテンシだ。平均が良くなるのも悪くはない。だがテールが改善することこそが、ダッシュボードの応答性を保ち、インデクサーをチェーンに追いつかせ続ける鍵になる。

すでに大規模にこれを運用しているチームがその効果を実感している。ある大手暗号資産取引所は、過去のログ読み取りについて、我々のエンジンと自前のノード運用とをベンチマークし、自社のワークロードにおいて大幅なp99の改善を測定した。これは我々のダッシュボード上だけでなく、顧客自身の計測に基づく、本番環境での実証だ。

## これはあなたにとって何を意味するか

インデックス作成、集計、監視のために過去のログ読み取りに依存しているアプリケーションであれば、このメソッド専用に再構築されたインフラの上で、全リージョンにおいて、より速い読み取りと劇的に改善されたp99テールが得られる。実際に使う上で知っておくべき点がいくつかある。

1. 従量課金プランおよびエンタープライズプランでは、`eth_getLogs`はEthereum、Base、Optimism、Arbitrum、Polygon、Robinhoodなど主要なチェーンにおいて、単一クエリでの無制限のブロックレンジをサポートしている。そのため履歴のリプレイに自前のページネーションを実装する必要はない。
2. チェーンヘッド付近のログについては、[WebSocketサブスクリプション](https://www.alchemy.com/docs/reference/subscription-api)が新しいログを到着次第プッシュしてくれるため、`eth_getLogs`は本来の用途である過去分の読み取りのために温存できる。
3. インテグレーションは何も変わらない。同じメソッド、同じリクエスト形式が、より高速なエンジンによって処理されるだけだ。

メソッド単位で投資する意味は、あなたのワークロードを規定する読み取りこそが、我々がエンジンをチューニングした対象そのものであるという点にある。[無料のAlchemyアカウントを作成](https://dashboard.alchemy.com/signup)し、あなたのインデクサーを我々に向けて、自分自身の計測でその違いを確かめてほしい。

## よくある質問

### eth_getLogsは何を返すか

コントラクトアドレス、イベントシグネチャとインデックス化されたトピックの値、ブロックレンジといったフィルタ条件に一致するイベントログの配列を返す。各ログには、発行元のアドレス、トピック、データペイロード、そしてそれがどのブロックとトランザクションから来たかが含まれる。リクエストとレスポンスの完全な仕様については[APIリファレンス](https://www.alchemy.com/docs/reference/eth-getlogs)を参照してほしい。

### なぜインデクサーにとって平均レイテンシよりp99レイテンシの方が重要なのか

インデクサーはリクエストを順次、またはバッチで処理するため、最も遅いリクエストがペースを決めるからだ。平均値が低くてもテールが長ければ、インデクサーは遅い1パーセントのリクエストで定期的に詰まり、チェーンヘッドから遅れ、古いデータを提供することになる。p99はまさにそうしたリクエストを測る指標だ。

### Alchemyで単一のeth_getLogs呼び出しにおいて問い合わせられるブロックレンジはどれくらいか

無料プランでは1クエリあたり10ブロックまで。従量課金プランおよびエンタープライズプランでは、Ethereum、Base、Optimism、Arbitrum、Polygon、Worldchain、BNBなど複数のチェーンでブロックレンジは無制限で、レスポンスは150MBを上限とする。チェーンごとの完全な一覧は[`eth_getLogs`リファレンス](https://www.alchemy.com/docs/reference/eth-getlogs)にある。

### 大規模な過去データのクエリを高速に保つにはどうすればよいか

できる限り絞り込んでフィルタすること。ブロックレンジだけでなく、必要なコントラクトアドレスとイベントトピックを指定する。無制限レンジをサポートしていないチェーンで非常に広いレンジを扱う場合は、クエリを小さなチャンクに分割する。そして今後発生する新しいログについては、ポーリングではなく[WebSocketサブスクリプション](https://www.alchemy.com/docs/reference/subscription-api)を利用する。
