-
Notifications
You must be signed in to change notification settings - Fork 255
feat(argus): ControllerService update loop, ChainPriceService poll loop #2693
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,23 +7,28 @@ | |
|
||
use anyhow::Result; | ||
use async_trait::async_trait; | ||
use pyth_sdk::Price; | ||
use std::collections::HashMap; | ||
use std::sync::Arc; | ||
use std::time::Duration; | ||
use tokio::sync::watch; | ||
use tokio::time; | ||
use tracing; | ||
|
||
use crate::adapters::contract::GetChainPrices; | ||
use crate::adapters::types::PriceId; | ||
use crate::services::Service; | ||
use crate::state::ChainName; | ||
use crate::state::ChainPriceState; | ||
use crate::state::SubscriptionState; | ||
|
||
pub struct ChainPriceService { | ||
chain_name: ChainName, | ||
name: String, | ||
contract: Arc<dyn GetChainPrices + Send + Sync>, | ||
poll_interval: Duration, | ||
chain_price_state: Arc<ChainPriceState>, | ||
subscription_state: Arc<SubscriptionState>, | ||
} | ||
|
||
impl ChainPriceService { | ||
|
@@ -32,24 +37,70 @@ impl ChainPriceService { | |
contract: Arc<dyn GetChainPrices + Send + Sync>, | ||
poll_interval: Duration, | ||
chain_price_state: Arc<ChainPriceState>, | ||
subscription_state: Arc<SubscriptionState>, | ||
) -> Self { | ||
Self { | ||
chain_name: chain_name.clone(), | ||
name: format!("ChainPriceService-{}", chain_name), | ||
contract, | ||
poll_interval, | ||
chain_price_state, | ||
subscription_state, | ||
} | ||
} | ||
|
||
async fn poll_prices(&self, state: Arc<ChainPriceState>) { | ||
let feed_ids = state.get_feed_ids(); | ||
#[tracing::instrument(skip_all, fields(task = self.name, chain_name = self.chain_name))] | ||
async fn poll_prices(&self) -> Result<()> { | ||
// Get all active subscriptions | ||
let subscriptions = self.subscription_state.get_subscriptions(); | ||
|
||
tracing::debug!( | ||
service = self.name, | ||
feed_count = feed_ids.len(), | ||
"Polled for on-chain price updates" | ||
); | ||
// For each subscription, query the chain for the price of each feed | ||
for item in subscriptions.iter() { | ||
let subscription_id = item.key().clone(); | ||
let subscription_params = item.value().clone(); | ||
|
||
// TODO: do this in parallel using tokio tasks? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah let's do it. |
||
let price_ids = subscription_params | ||
.price_ids | ||
.into_iter() | ||
.map(|id| PriceId::new(id)) | ||
.collect::<Vec<PriceId>>(); | ||
|
||
match self | ||
.contract | ||
.get_prices_for_subscription(subscription_id, &price_ids) | ||
.await | ||
{ | ||
Ok(prices) => { | ||
let prices_map: HashMap<PriceId, Price> = price_ids | ||
.clone() | ||
.into_iter() | ||
.zip(prices.into_iter()) | ||
.collect(); | ||
|
||
tracing::debug!( | ||
price_ids = ?price_ids, | ||
subscription_id = %subscription_id, | ||
"Got prices for subscription" | ||
); | ||
|
||
// Store the latest price feeds for the subscription | ||
self.chain_price_state | ||
.update_prices(subscription_id, prices_map); | ||
} | ||
Err(e) => { | ||
// If we failed to get prices for a subscription, we'll retry on the next poll interval. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this implicitly enforces having a high poll interval |
||
// Continue to the next subscription. | ||
tracing::error!( | ||
subscription_id = %subscription_id, | ||
error = %e, | ||
"Failed to get prices for subscription" | ||
); | ||
continue; | ||
} | ||
} | ||
} | ||
Ok(()) | ||
} | ||
} | ||
|
||
|
@@ -64,7 +115,13 @@ impl Service for ChainPriceService { | |
loop { | ||
tokio::select! { | ||
_ = interval.tick() => { | ||
self.poll_prices(self.chain_price_state.clone()).await; | ||
if let Err(e) = self.poll_prices().await { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. poll_prices doesn't seem to ever fail (maybe change its signature?) |
||
tracing::error!( | ||
service = self.name, | ||
error = %e, | ||
"Failed to poll chain prices" | ||
); | ||
} | ||
} | ||
_ = stop_rx.changed() => { | ||
if *stop_rx.borrow() { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
vec![]
seems wrong.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
actually it seems that it's part of the api. maybe document it in the IScheduler? or Ipulse