-
Notifications
You must be signed in to change notification settings - Fork 93
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
[EASY] Add metrics for logging native price requests #3006
Changes from 4 commits
28b38c9
2fb58e4
62838b7
4e3ecee
3f137fc
0f40ce7
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 |
---|---|---|
@@ -1,21 +1,28 @@ | ||
use { | ||
crate::price_estimation::{PriceEstimating, PriceEstimationError, Query}, | ||
crate::price_estimation::{ | ||
native::{NativePriceEstimateResult, NativePriceEstimating}, | ||
PriceEstimating, | ||
PriceEstimationError, | ||
Query, | ||
}, | ||
ethcontract::jsonrpc::futures_util::future::BoxFuture, | ||
futures::future::FutureExt, | ||
primitive_types::H160, | ||
prometheus::{HistogramVec, IntCounterVec}, | ||
std::{sync::Arc, time::Instant}, | ||
tracing::Instrument, | ||
}; | ||
|
||
/// An instrumented price estimator. | ||
pub struct InstrumentedPriceEstimator { | ||
inner: Box<dyn PriceEstimating>, | ||
pub struct InstrumentedPriceEstimator<T> { | ||
inner: T, | ||
name: String, | ||
metrics: &'static Metrics, | ||
} | ||
|
||
impl InstrumentedPriceEstimator { | ||
impl<T> InstrumentedPriceEstimator<T> { | ||
/// Wraps an existing price estimator in an instrumented one. | ||
pub fn new(inner: Box<dyn PriceEstimating>, name: String) -> Self { | ||
pub fn new(inner: T, name: String) -> Self { | ||
let metrics = Metrics::instance(observe::metrics::get_storage_registry()).unwrap(); | ||
for result in ["success", "failure"] { | ||
metrics | ||
|
@@ -29,9 +36,21 @@ impl InstrumentedPriceEstimator { | |
metrics, | ||
} | ||
} | ||
|
||
/// Determines the result of a price estimate as either "success" or | ||
/// "failure". | ||
fn estimate_result<B>(&self, estimate: Result<&B, &PriceEstimationError>) -> &str { | ||
// Count as a successful request if the answer is ok (no error) or if the error | ||
// is No Liquidity | ||
if estimate.is_ok() || matches!(estimate, Err(PriceEstimationError::NoLiquidity)) { | ||
"success" | ||
} else { | ||
"failure" | ||
} | ||
} | ||
} | ||
|
||
impl PriceEstimating for InstrumentedPriceEstimator { | ||
impl<T: PriceEstimating> PriceEstimating for InstrumentedPriceEstimator<T> { | ||
fn estimate( | ||
&self, | ||
query: Arc<Query>, | ||
|
@@ -43,9 +62,7 @@ impl PriceEstimating for InstrumentedPriceEstimator { | |
.price_estimation_times | ||
.with_label_values(&[self.name.as_str()]) | ||
.observe(start.elapsed().as_secs_f64()); | ||
|
||
let success = !matches!(&estimate, Err(PriceEstimationError::EstimatorInternal(_))); | ||
let result = if success { "success" } else { "failure" }; | ||
let result = self.estimate_result(estimate.as_ref()); | ||
self.metrics | ||
.price_estimates | ||
.with_label_values(&[self.name.as_str(), result]) | ||
|
@@ -58,6 +75,32 @@ impl PriceEstimating for InstrumentedPriceEstimator { | |
} | ||
} | ||
|
||
impl<T: NativePriceEstimating> NativePriceEstimating for InstrumentedPriceEstimator<T> { | ||
fn estimate_native_price(&self, token: H160) -> BoxFuture<'_, NativePriceEstimateResult> { | ||
async move { | ||
let start = Instant::now(); | ||
let estimate = self.inner.estimate_native_price(token).await; | ||
self.metrics | ||
.native_price_estimation_times | ||
.with_label_values(&[self.name.as_str()]) | ||
.observe(start.elapsed().as_secs_f64()); | ||
let result = self.estimate_result(estimate.as_ref()); | ||
self.metrics | ||
.native_price_estimates | ||
.with_label_values(&[self.name.as_str(), result]) | ||
.inc(); | ||
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. Do we really need 2 metrics? Can we use only the 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. @squadgazzz in the end, because of this comment: #3006 (comment) |
||
|
||
estimate | ||
} | ||
.instrument(tracing::info_span!("native estimator", name = &self.name,)) | ||
.boxed() | ||
} | ||
} | ||
|
||
/// Most native estimators are regular estimators with a wrapper. In | ||
/// those cases, the native price requests will get counted as regular | ||
/// price_estimates. Therefore, native_price_estimates only counts requests | ||
/// for estimators which are exclusively native price estimators. | ||
#[derive(prometheus_metric_storage::MetricStorage)] | ||
struct Metrics { | ||
/// price estimates | ||
|
@@ -67,6 +110,14 @@ struct Metrics { | |
/// price estimation times | ||
#[metric(labels("estimator_type"))] | ||
price_estimation_times: HistogramVec, | ||
|
||
/// number of native price requests sent to each estimator | ||
m-lord-renkse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
#[metric(labels("estimator_type", "result"))] | ||
native_price_estimates: IntCounterVec, | ||
|
||
/// native price estimation times | ||
#[metric(labels("estimator_type"))] | ||
native_price_estimation_times: HistogramVec, | ||
} | ||
|
||
#[cfg(test)] | ||
|
@@ -117,7 +168,7 @@ mod tests { | |
async { Err(PriceEstimationError::EstimatorInternal(anyhow!(""))) }.boxed() | ||
}); | ||
|
||
let instrumented = InstrumentedPriceEstimator::new(Box::new(estimator), "foo".to_string()); | ||
let instrumented = InstrumentedPriceEstimator::new(estimator, "foo".to_string()); | ||
|
||
let _ = instrumented.estimate(queries[0].clone()).await; | ||
let _ = instrumented.estimate(queries[1].clone()).await; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ use { | |
|
||
/// A `TradeFinding`-based price estimator with request sharing and rate | ||
/// limiting. | ||
#[derive(Clone)] | ||
pub struct TradeEstimator { | ||
inner: Arc<Inner>, | ||
sharing: RequestSharing<Arc<Query>, BoxFuture<'static, Result<Estimate, PriceEstimationError>>>, | ||
|
@@ -104,16 +105,6 @@ impl Inner { | |
} | ||
} | ||
|
||
impl Clone for TradeEstimator { | ||
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. I guess this was done because it required in the pass a specific |
||
fn clone(&self) -> Self { | ||
Self { | ||
inner: self.inner.clone(), | ||
sharing: self.sharing.clone(), | ||
rate_limiter: self.rate_limiter.clone(), | ||
} | ||
} | ||
} | ||
|
||
impl PriceEstimating for TradeEstimator { | ||
fn estimate(&self, query: Arc<Query>) -> futures::future::BoxFuture<'_, PriceEstimateResult> { | ||
self.estimate(query).boxed() | ||
|
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.
UpperCamelCase should be avoided since
driver.name
uses flatcase. We should use a similar format for other names as well. If not flatcase everywhere, maybe snake_case.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.
Yeah, I agree with you, but this is something systematic within the natice prices, we should change that in another PR alongside the configuration, because this happens also in the configuration of the services :/