Cache lifetime-constant Beacon Node API responses
@varex83 がすでに取り組んでいます。
2026年9月3日 から。
評価
この issue はまだ評価されていません。
説明
Summary
Several Beacon Node API responses are constant for the lifetime of the client (genesis data, the chain spec, slots-per-epoch / slot duration, the deposit contract, …) yet Pluto re-fetches them over HTTP on every call. We should cache these once and reuse them, while being conservative: only cache what is genuinely immutable, and when in doubt make the extra call rather than risk serving stale data.
This came out of the core/scheduler review (PR #466). SchedulerActor::get_duty_definition issues a live fetch_slots_config() round-trip on every query just to derive slots_per_epoch:
slots_per_epoch never changes, so this is a per-call beacon round-trip on a hot path that blocks the actor. A general cache for lifetime-constant responses fixes this class of problem rather than just the one call site.
Where caching should live
Almost all the constant-data helpers funnel through two private base fetchers in extensions.rs, which makes them the natural memoization points:
fetch_spec_data(backs spec / slots config / fork config / domains): https://github.com/NethermindEth/pluto/blob/6a25109aaa6341f551754d0a02728dd75d8917eb/crates/eth2api/src/extensions.rs#L231fetch_genesis_data(backs genesis time / validators root / fork version / genesis domain): https://github.com/NethermindEth/pluto/blob/6a25109aaa6341f551754d0a02728dd75d8917eb/crates/eth2api/src/extensions.rs#L238
Memoizing just these two makes every downstream helper (fetch_genesis_time, fetch_slots_config, fetch_fork_config, fetch_domain*, …) cheap.
Note that EthBeaconNodeApiClient itself is an OpenAPI-generated struct, so a cache field can't simply be added to it without changing the generator:
A caching layer (or a small wrapper type) is therefore preferable. There is already precedent for this pattern in the crate — the epoch-aware ValidatorCache:
Reference: what Charon caches
Charon delegates this caching to go-eth2-client. Its eth2wrap code generator marks each endpoint with a Latency flag; Latency: false means "this endpoint is cached in go-eth2-client" (the generator literally emits that comment):
- interfaces map: https://github.com/ObolNetwork/charon/blob/749d2d7ab0b8ace34f2e686a5af5910c8adb4dc0/app/eth2wrap/genwrap/genwrap.go#L99
- "cached" comment template: https://github.com/ObolNetwork/charon/blob/749d2d7ab0b8ace34f2e686a5af5910c8adb4dc0/app/eth2wrap/genwrap/genwrap.go#L62
The endpoints Charon/go-eth2-client cache (Latency: false) are: BeaconBlockRoot, DepositContract, Domain, Genesis, NodePeerCount, NodeVersion, SlotDuration, SlotsPerEpoch, Spec. Notably ForkSchedule and Fork are NOT cached by Charon (Latency: true).
Category 1 — cache these (lifetime-constant; Charon caches them too)
| Data | Pluto method | Backing endpoint |
|---|---|---|
| Genesis (time, validators root, genesis fork version) | fetch_genesis_data → fetch_genesis_time, fetch_genesis_validators_root, fetch_genesis_fork_version |
get_genesis |
| Chain spec | fetch_spec_data / fetch_spec |
get_spec |
| Slot duration & slots-per-epoch | fetch_slots_config |
derived from spec |
| Signing domains | fetch_domain_type, fetch_genesis_domain, fetch_domain |
derived from spec + genesis |
| Deposit contract (address, chain id) | — (not yet wrapped) | get_deposit_contract |
| Beacon node version | — (not yet wrapped) | get_node_version |
Caching fetch_spec_data and fetch_genesis_data covers the whole first four rows at once.
Category 2 — semantically constant-ish, but do NOT cache (err on the side of caution)
| Data | Pluto method | Why not |
|---|---|---|
| Fork schedule | fetch_fork_config → get_fork_schedule |
Future forks can be (re)scheduled. Charon explicitly does not cache this (Latency: true). In Pluto it is derived from the spec, so it rides along with the spec cache only as far as the spec itself; treat new forks as a reason to re-read. |
| State fork | get_state_fork |
Per-state / per-fork-epoch, not lifetime-constant. Charon does not cache it. |
| Peer count | get_peer_count |
go-eth2-client marks it Latency: false, but it is semantically dynamic — do not cache in Pluto. |
| Block root | get_block_root |
Cached by go-eth2-client keyed by block_id; a single value cache is wrong here. Out of scope. |
Everything else (duties, validator state, syncing status, head/finality, pools, block production/submission, rewards, …) is dynamic and must not be cached.
Proposed scope
- Introduce a memoization layer for the two base fetchers (
fetch_spec_data,fetch_genesis_data) so all constant-data helpers become effectively free after the first call. - Use it to make
slots_per_epochavailable synchronously to the scheduler, removing the per-callfetch_slots_config()round-trip atscheduler.rs#L341. - Optionally extend the same mechanism to
get_deposit_contractandget_node_versionif/when they get high-level wrappers. - Leave Category 2 endpoints uncached.
- 主要言語
- Rust
- スター
- 8
- フォーク
- 5
- 平均マージ
- 4日 18時間
- マージ済み PR(30日)
- 4
環境構築
- Dockerfile または Docker Compose ファイルあり
- プルリクエストのテンプレートあり
- コントリビューションガイドを読む
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
NethermindEth/pluto のほかの issue
-
難易度 4/5 3〜5日 初心者へのやさしさ 52/100
NethermindEth/pluto#719 ·
-
enhancement rust
難易度 4/5 3〜5日 初心者へのやさしさ 45/100
NethermindEth/pluto#640 ·
-
rust
難易度 5/5 1週間以上 初心者へのやさしさ 45/100
NethermindEth/pluto#638 · コメント 1 件 ·
-
enhancement rust
難易度 5/5 1週間以上 初心者へのやさしさ 35/100
NethermindEth/pluto#635 ·
-
Improve `alpha test peers`: match Charon's probes; reduce timeouts対応中かも @varex83agent が 22 日前に担当しました。 オープンbug track:orchestration-cli
難易度 5/5 1週間以上 初心者へのやさしさ 28/100
NethermindEth/pluto#632 ·
NethermindEth/pluto の issue をすべて見る
似ている issue
-
checkpoint/verify/rebuild_indexes on a thread holding a WriteTransaction deadlock instead of INT-001オープンbug known-issue
難易度 2/5 1〜3時間 初心者へのやさしさ 74/100
project-minigraf/minigraf#734 ·
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
-
難易度 1/5 1時間未満 初心者へのやさしさ 88/100
メンテナーはふだん 4 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 70/100
メンテナーはふだん 1 日以内に返信
-
難易度 2/5 1〜3時間 初心者へのやさしさ 64/100
Nimblesite/lspkit#26 ·
メンテナーはふだん 1 日以内に返信