refactor(hermes): state->price_feed_metadata downcasting

This commit is contained in:
Reisen 2024-04-10 14:03:59 +00:00 committed by Reisen
parent d1c5d93c8e
commit ce4019b63f
6 changed files with 133 additions and 74 deletions

View File

@ -426,7 +426,7 @@ where
pub async fn is_ready(state: &State) -> bool { pub async fn is_ready(state: &State) -> bool {
let metadata = state.aggregate_state.read().await; let metadata = state.aggregate_state.read().await;
let price_feeds_metadata = state.price_feeds_metadata.read().await; let price_feeds_metadata = state.price_feed_meta.data.read().await;
let has_completed_recently = match metadata.latest_completed_update_at.as_ref() { let has_completed_recently = match metadata.latest_completed_update_at.as_ref() {
Some(latest_completed_update_time) => { Some(latest_completed_update_time) => {
@ -456,7 +456,7 @@ mod test {
super::*, super::*,
crate::{ crate::{
api::types::PriceFeedMetadata, api::types::PriceFeedMetadata,
price_feeds_metadata::store_price_feeds_metadata, price_feeds_metadata::PriceFeedMeta,
state::test::setup_state, state::test::setup_state,
}, },
futures::future::join_all, futures::future::join_all,
@ -809,13 +809,11 @@ mod test {
// Add a dummy price feeds metadata // Add a dummy price feeds metadata
store_price_feeds_metadata( state
&state, .store_price_feeds_metadata(&[PriceFeedMetadata {
&[PriceFeedMetadata {
id: PriceIdentifier::new([100; 32]), id: PriceIdentifier::new([100; 32]),
attributes: Default::default(), attributes: Default::default(),
}], }])
)
.await .await
.unwrap(); .unwrap();

View File

@ -26,15 +26,27 @@ mod rest;
pub mod types; pub mod types;
mod ws; mod ws;
#[derive(Clone)] pub struct ApiState<S = State> {
pub struct ApiState { pub state: Arc<S>,
pub state: Arc<State>,
pub ws: Arc<ws::WsState>, pub ws: Arc<ws::WsState>,
pub metrics: Arc<metrics_middleware::Metrics>, pub metrics: Arc<metrics_middleware::Metrics>,
pub update_tx: Sender<AggregationEvent>, pub update_tx: Sender<AggregationEvent>,
} }
impl ApiState { /// Manually implement `Clone` as the derive macro will try and slap `Clone` on
/// `State` which should not be Clone.
impl<S> Clone for ApiState<S> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
ws: self.ws.clone(),
metrics: self.metrics.clone(),
update_tx: self.update_tx.clone(),
}
}
}
impl ApiState<State> {
pub fn new( pub fn new(
state: Arc<State>, state: Arc<State>,
ws_whitelist: Vec<IpNet>, ws_whitelist: Vec<IpNet>,

View File

@ -6,8 +6,9 @@ use {
AssetType, AssetType,
PriceFeedMetadata, PriceFeedMetadata,
}, },
ApiState,
}, },
price_feeds_metadata::get_price_feeds_metadata, price_feeds_metadata::PriceFeedMeta,
}, },
anyhow::Result, anyhow::Result,
axum::{ axum::{
@ -46,12 +47,16 @@ pub struct PriceFeedsMetadataQueryParams {
PriceFeedsMetadataQueryParams PriceFeedsMetadataQueryParams
) )
)] )]
pub async fn price_feeds_metadata( pub async fn price_feeds_metadata<S>(
State(state): State<crate::api::ApiState>, State(state): State<ApiState<S>>,
QsQuery(params): QsQuery<PriceFeedsMetadataQueryParams>, QsQuery(params): QsQuery<PriceFeedsMetadataQueryParams>,
) -> Result<Json<Vec<PriceFeedMetadata>>, RestError> { ) -> Result<Json<Vec<PriceFeedMetadata>>, RestError>
let price_feeds_metadata = where
get_price_feeds_metadata(&state.state, params.query, params.asset_type) S: PriceFeedMeta,
{
let state = &state.state;
let price_feeds_metadata = state
.get_price_feeds_metadata(params.query, params.asset_type)
.await .await
.map_err(|e| { .map_err(|e| {
tracing::warn!("RPC connection error: {}", e); tracing::warn!("RPC connection error: {}", e);

View File

@ -17,7 +17,7 @@ use {
GuardianSetData, GuardianSetData,
}, },
price_feeds_metadata::{ price_feeds_metadata::{
store_price_feeds_metadata, PriceFeedMeta,
DEFAULT_PRICE_FEEDS_CACHE_UPDATE_INTERVAL, DEFAULT_PRICE_FEEDS_CACHE_UPDATE_INTERVAL,
}, },
state::State, state::State,
@ -353,13 +353,18 @@ pub async fn spawn(opts: RunOptions, state: Arc<State>) -> Result<()> {
} }
pub async fn fetch_and_store_price_feeds_metadata( pub async fn fetch_and_store_price_feeds_metadata<S>(
state: &State, state: &S,
mapping_address: &Pubkey, mapping_address: &Pubkey,
rpc_client: &RpcClient, rpc_client: &RpcClient,
) -> Result<Vec<PriceFeedMetadata>> { ) -> Result<Vec<PriceFeedMetadata>>
where
S: PriceFeedMeta,
{
let price_feeds_metadata = fetch_price_feeds_metadata(mapping_address, rpc_client).await?; let price_feeds_metadata = fetch_price_feeds_metadata(mapping_address, rpc_client).await?;
store_price_feeds_metadata(state, &price_feeds_metadata).await?; state
.store_price_feeds_metadata(&price_feeds_metadata)
.await?;
Ok(price_feeds_metadata) Ok(price_feeds_metadata)
} }

View File

@ -7,31 +7,69 @@ use {
state::State, state::State,
}, },
anyhow::Result, anyhow::Result,
tokio::sync::RwLock,
}; };
pub const DEFAULT_PRICE_FEEDS_CACHE_UPDATE_INTERVAL: u64 = 600; pub const DEFAULT_PRICE_FEEDS_CACHE_UPDATE_INTERVAL: u64 = 600;
pub async fn retrieve_price_feeds_metadata(state: &State) -> Result<Vec<PriceFeedMetadata>> { pub struct PriceFeedMetaState {
let price_feeds_metadata = state.price_feeds_metadata.read().await; pub data: RwLock<Vec<PriceFeedMetadata>>,
}
impl PriceFeedMetaState {
pub fn new() -> Self {
Self {
data: RwLock::new(Vec::new()),
}
}
}
/// Allow downcasting State into CacheState for functions that depend on the `Cache` service.
impl<'a> From<&'a State> for &'a PriceFeedMetaState {
fn from(state: &'a State) -> &'a PriceFeedMetaState {
&state.price_feed_meta
}
}
pub trait PriceFeedMeta {
async fn retrieve_price_feeds_metadata(&self) -> Result<Vec<PriceFeedMetadata>>;
async fn store_price_feeds_metadata(
&self,
price_feeds_metadata: &[PriceFeedMetadata],
) -> Result<()>;
async fn get_price_feeds_metadata(
&self,
query: Option<String>,
asset_type: Option<AssetType>,
) -> Result<Vec<PriceFeedMetadata>>;
}
impl<T> PriceFeedMeta for T
where
for<'a> &'a T: Into<&'a PriceFeedMetaState>,
T: Sync,
{
async fn retrieve_price_feeds_metadata(&self) -> Result<Vec<PriceFeedMetadata>> {
let price_feeds_metadata = self.into().data.read().await;
Ok(price_feeds_metadata.clone()) Ok(price_feeds_metadata.clone())
} }
pub async fn store_price_feeds_metadata( async fn store_price_feeds_metadata(
state: &State, &self,
price_feeds_metadata: &[PriceFeedMetadata], price_feeds_metadata: &[PriceFeedMetadata],
) -> Result<()> { ) -> Result<()> {
let mut price_feeds_metadata_write_guard = state.price_feeds_metadata.write().await; let mut price_feeds_metadata_write_guard = self.into().data.write().await;
*price_feeds_metadata_write_guard = price_feeds_metadata.to_vec(); *price_feeds_metadata_write_guard = price_feeds_metadata.to_vec();
Ok(()) Ok(())
} }
pub async fn get_price_feeds_metadata( async fn get_price_feeds_metadata(
state: &State, &self,
query: Option<String>, query: Option<String>,
asset_type: Option<AssetType>, asset_type: Option<AssetType>,
) -> Result<Vec<PriceFeedMetadata>> { ) -> Result<Vec<PriceFeedMetadata>> {
let mut price_feeds_metadata = retrieve_price_feeds_metadata(state).await?; let mut price_feeds_metadata = self.retrieve_price_feeds_metadata().await?;
// Filter by query if provided // Filter by query if provided
if let Some(query_str) = &query { if let Some(query_str) = &query {
@ -53,3 +91,4 @@ pub async fn get_price_feeds_metadata(
Ok(price_feeds_metadata) Ok(price_feeds_metadata)
} }
}

View File

@ -10,8 +10,8 @@ use {
AggregateState, AggregateState,
AggregationEvent, AggregationEvent,
}, },
api::types::PriceFeedMetadata,
network::wormhole::GuardianSet, network::wormhole::GuardianSet,
price_feeds_metadata::PriceFeedMetaState,
}, },
prometheus_client::registry::Registry, prometheus_client::registry::Registry,
reqwest::Url, reqwest::Url,
@ -38,6 +38,9 @@ pub struct State {
/// State for the `Benchmarks` service for looking up historical updates. /// State for the `Benchmarks` service for looking up historical updates.
pub benchmarks: BenchmarksState, pub benchmarks: BenchmarksState,
/// State for the `PriceFeedMeta` service for looking up metadata related to Pyth price feeds.
pub price_feed_meta: PriceFeedMetaState,
/// Sequence numbers of lately observed Vaas. Store uses this set /// Sequence numbers of lately observed Vaas. Store uses this set
/// to ignore the previously observed Vaas as a performance boost. /// to ignore the previously observed Vaas as a performance boost.
pub observed_vaa_seqs: RwLock<BTreeSet<u64>>, pub observed_vaa_seqs: RwLock<BTreeSet<u64>>,
@ -53,9 +56,6 @@ pub struct State {
/// Metrics registry /// Metrics registry
pub metrics_registry: RwLock<Registry>, pub metrics_registry: RwLock<Registry>,
/// Price feeds metadata
pub price_feeds_metadata: RwLock<Vec<PriceFeedMetadata>>,
} }
impl State { impl State {
@ -68,12 +68,12 @@ impl State {
Arc::new(Self { Arc::new(Self {
cache: CacheState::new(cache_size), cache: CacheState::new(cache_size),
benchmarks: BenchmarksState::new(benchmarks_endpoint), benchmarks: BenchmarksState::new(benchmarks_endpoint),
price_feed_meta: PriceFeedMetaState::new(),
observed_vaa_seqs: RwLock::new(Default::default()), observed_vaa_seqs: RwLock::new(Default::default()),
guardian_set: RwLock::new(Default::default()), guardian_set: RwLock::new(Default::default()),
api_update_tx: update_tx, api_update_tx: update_tx,
aggregate_state: RwLock::new(AggregateState::new(&mut metrics_registry)), aggregate_state: RwLock::new(AggregateState::new(&mut metrics_registry)),
metrics_registry: RwLock::new(metrics_registry), metrics_registry: RwLock::new(metrics_registry),
price_feeds_metadata: RwLock::new(Default::default()),
}) })
} }
} }