From c0793f0485d744bb713ca1d149b082b458ffde39 Mon Sep 17 00:00:00 2001 From: Hubert Bugaj Date: Tue, 21 Apr 2026 18:52:34 +0200 Subject: [PATCH] feat: cache process immutable strings --- src/lotus_json/arc.rs | 22 ++++++++++++++++++++++ src/rpc/methods/eth.rs | 26 ++++++++++++++++++-------- src/rpc/methods/f3.rs | 10 ++++++++-- src/rpc/methods/net.rs | 9 +++++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/lotus_json/arc.rs b/src/lotus_json/arc.rs index 40785eff7e2c..269a3986ac94 100644 --- a/src/lotus_json/arc.rs +++ b/src/lotus_json/arc.rs @@ -23,3 +23,25 @@ impl HasLotusJson for Arc { Arc::new(T::from_lotus_json(lotus_json)) } } + +// `Arc` can't reuse the blanket impl above (which requires `T: Sized`, +// and `str` is not). `Arc` already satisfies every `HasLotusJson` +// consumer bound directly (`Serialize + DeserializeOwned + JsonSchema + +// Clone + 'static`), so we make `LotusJson = Self` and keep the conversions +// as identity — serializing the cached value takes no allocation. +impl HasLotusJson for Arc { + type LotusJson = Self; + + #[cfg(test)] + fn snapshots() -> Vec<(serde_json::Value, Self)> { + unimplemented!("tests are trivial for HasLotusJson") + } + + fn into_lotus_json(self) -> Self::LotusJson { + self + } + + fn from_lotus_json(lotus_json: Self::LotusJson) -> Self { + lotus_json + } +} diff --git a/src/rpc/methods/eth.rs b/src/rpc/methods/eth.rs index 3966c8e5b7b7..be0c9d5bdf9d 100644 --- a/src/rpc/methods/eth.rs +++ b/src/rpc/methods/eth.rs @@ -77,7 +77,7 @@ use serde::{Deserialize, Serialize}; use std::num::NonZeroUsize; use std::ops::RangeInclusive; use std::str::FromStr; -use std::sync::{Arc, LazyLock}; +use std::sync::{Arc, LazyLock, OnceLock}; use utils::{decode_payload, lookup_eth_address}; static FOREST_TRACE_FILTER_MAX_RESULT: LazyLock = @@ -764,17 +764,23 @@ impl RpcMethod<0> for Web3ClientVersion { const PERMISSION: Permission = Permission::Read; type Params = (); - type Ok = String; + type Ok = Arc; async fn handle( _: Ctx, (): Self::Params, _: &http::Extensions, ) -> Result { - Ok(format!( - "forest/{}", - *crate::utils::version::FOREST_VERSION_STRING - )) + // Version string is baked in at build time; cache once. + static CACHED: OnceLock> = OnceLock::new(); + Ok(CACHED + .get_or_init(|| { + Arc::::from(format!( + "forest/{}", + *crate::utils::version::FOREST_VERSION_STRING + )) + }) + .clone()) } } @@ -844,14 +850,18 @@ impl RpcMethod<0> for EthChainId { const PERMISSION: Permission = Permission::Read; type Params = (); - type Ok = String; + type Ok = Arc; async fn handle( ctx: Ctx, (): Self::Params, _: &http::Extensions, ) -> Result { - Ok(format!("{:#x}", ctx.chain_config().eth_chain_id)) + // `eth_chain_id` is fixed for the process lifetime; cache the hex form. + static CACHED: OnceLock> = OnceLock::new(); + Ok(CACHED + .get_or_init(|| Arc::::from(format!("{:#x}", ctx.chain_config().eth_chain_id))) + .clone()) } } diff --git a/src/rpc/methods/f3.rs b/src/rpc/methods/f3.rs index 9f9ed6dd225b..258884e2e373 100644 --- a/src/rpc/methods/f3.rs +++ b/src/rpc/methods/f3.rs @@ -63,14 +63,20 @@ impl RpcMethod<0> for GetRawNetworkName { const PERMISSION: Permission = Permission::Read; type Params = (); - type Ok = String; + type Ok = Arc; async fn handle( ctx: Ctx, (): Self::Params, _: &http::Extensions, ) -> Result { - Ok(ctx.chain_config().network.genesis_name().into()) + // Network is fixed for the process lifetime; cache the genesis name. + static CACHED: OnceLock> = OnceLock::new(); + Ok(CACHED + .get_or_init(|| { + Arc::::from(String::from(ctx.chain_config().network.genesis_name())) + }) + .clone()) } } diff --git a/src/rpc/methods/net.rs b/src/rpc/methods/net.rs index 687d58be51b4..dc045bc531c7 100644 --- a/src/rpc/methods/net.rs +++ b/src/rpc/methods/net.rs @@ -7,6 +7,7 @@ pub use types::*; use std::any::Any; use std::num::NonZeroU64; use std::str::FromStr; +use std::sync::{Arc, OnceLock}; use std::time::Instant; use crate::libp2p::chain_exchange::TipsetBundle; @@ -282,14 +283,18 @@ impl RpcMethod<0> for NetVersion { const NAME_ALIAS: Option<&'static str> = Some("net_version"); type Params = (); - type Ok = String; + type Ok = Arc; async fn handle( ctx: Ctx, (): Self::Params, _: &http::Extensions, ) -> Result { - Ok(ctx.chain_config().eth_chain_id.to_string()) + // `eth_chain_id` is fixed for the process lifetime; cache the decimal form. + static CACHED: OnceLock> = OnceLock::new(); + Ok(CACHED + .get_or_init(|| Arc::::from(ctx.chain_config().eth_chain_id.to_string())) + .clone()) } }