Skip to content
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

feat(pruner, metrics): skip attribute for metrics derive macro #4069

Merged
merged 7 commits into from
Aug 9, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
224 changes: 148 additions & 76 deletions crates/metrics/metrics-derive/src/expand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use once_cell::sync::Lazy;
use quote::{quote, ToTokens};
use regex::Regex;
use syn::{
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Lit, LitBool, LitStr,
MetaNameValue, Result, Token,
punctuated::Punctuated, Attribute, Data, DeriveInput, Error, Expr, Field, Lit, LitBool, LitStr,
Meta, MetaNameValue, Result, Token,
};

use crate::{metric::Metric, with_attrs::WithAttrs};
Expand All @@ -17,6 +17,19 @@ static METRIC_NAME_RE: Lazy<Regex> =
/// Supported metrics separators
const SUPPORTED_SEPARATORS: &[&str] = &[".", "_", ":"];

enum MetricField<'a> {
Included(Metric<'a>),
Skipped(&'a Field),
}

impl<'a> MetricField<'a> {
fn field(&self) -> &'a Field {
match self {
MetricField::Included(Metric { field, .. }) | MetricField::Skipped(field) => field,
}
}
}

pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let ty = &node.ident;
let vis = &node.vis;
Expand All @@ -36,32 +49,55 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter()
.map(|metric| {
let field_name = &metric.field.ident;
let metric_name =
format!("{}{}{}", scope.value(), metrics_attr.separator(), metric.name());
let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;
Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
quote! {
#describe(#metric_name, #description);
},
))
let field_name = &metric.field().ident;
match metric {
MetricField::Included(metric) => {
let metric_name = format!(
"{}{}{}",
scope.value(),
metrics_attr.separator(),
metric.name()
);
let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;
Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
Some(quote! {
#describe(#metric_name, #description);
}),
))
}
MetricField::Skipped(_) => Ok((
quote! {
#field_name: Default::default(),
},
quote! {
#field_name: Default::default(),
},
None,
)),
}
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0);
acc.1.push(x.1);
acc.2.push(x.2);
acc
});
.fold(
(vec![], vec![], vec![]),
|(mut defaults, mut labeled_defaults, mut describes),
(default, labeled_default, describe)| {
defaults.push(default);
labeled_defaults.push(labeled_default);
if let Some(describe) = describe {
describes.push(describe);
}
(defaults, labeled_defaults, describes)
},
);

quote! {
impl Default for #ty {
Expand Down Expand Up @@ -93,37 +129,56 @@ pub(crate) fn derive(node: &DeriveInput) -> Result<proc_macro2::TokenStream> {
let (defaults, labeled_defaults, describes): (Vec<_>, Vec<_>, Vec<_>) = metric_fields
.iter()
.map(|metric| {
let name = metric.name();
let separator = metrics_attr.separator();
let metric_name = quote! {
format!("{}{}{}", scope, #separator, #name)
};
let field_name = &metric.field.ident;

let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;

Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
quote! {
#describe(#metric_name, #description);
},
))
let field_name = &metric.field().ident;
match metric {
MetricField::Included(metric) => {
let name = metric.name();
let separator = metrics_attr.separator();
let metric_name = quote! {
format!("{}{}{}", scope, #separator, #name)
};

let registrar = metric.register_stmt()?;
let describe = metric.describe_stmt()?;
let description = &metric.description;

Ok((
quote! {
#field_name: #registrar(#metric_name),
},
quote! {
#field_name: #registrar(#metric_name, labels.clone()),
},
Some(quote! {
#describe(#metric_name, #description);
}),
))
}
MetricField::Skipped(_) => Ok((
quote! {
#field_name: Default::default(),
},
quote! {
#field_name: Default::default(),
},
None,
)),
}
})
.collect::<Result<Vec<_>>>()?
.into_iter()
.fold((vec![], vec![], vec![]), |mut acc, x| {
acc.0.push(x.0);
acc.1.push(x.1);
acc.2.push(x.2);
acc
});
.fold(
(vec![], vec![], vec![]),
|(mut defaults, mut labeled_defaults, mut describes),
(default, labeled_default, describe)| {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

let's avoid using keywords as var names

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah, didn't know it's a keyword from nightly feature, fixed

defaults.push(default);
labeled_defaults.push(labeled_default);
if let Some(describe) = describe {
describes.push(describe);
}
(defaults, labeled_defaults, describes)
},
);

quote! {
impl #ty {
Expand Down Expand Up @@ -246,40 +301,57 @@ fn parse_metrics_attr(node: &DeriveInput) -> Result<MetricsAttr> {
Ok(MetricsAttr { scope, separator })
}

fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<Metric<'_>>> {
fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<MetricField<'_>>> {
let Data::Struct(ref data) = node.data else {
return Err(Error::new_spanned(node, "Only structs are supported."))
};

let mut metrics = Vec::with_capacity(data.fields.len());
for field in data.fields.iter() {
let (mut describe, mut rename) = (None, None);
let (mut describe, mut rename, mut skip) = (None, None, false);
if let Some(metric_attr) = parse_single_attr(field, "metric")? {
let parsed = metric_attr
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)?;
for kv in parsed {
let lit = match kv.value {
Expr::Lit(ref expr) => &expr.lit,
_ => continue,
};
if kv.path.is_ident("describe") {
if describe.is_some() {
return Err(Error::new_spanned(kv, "Duplicate `describe` value provided."))
}
describe = Some(parse_str_lit(lit)?);
} else if kv.path.is_ident("rename") {
if rename.is_some() {
return Err(Error::new_spanned(kv, "Duplicate `rename` value provided."))
let parsed =
metric_attr.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)?;
for meta in parsed {
match meta {
Meta::Path(path) if path.is_ident("skip") => skip = true,
Meta::NameValue(kv) => {
let lit = match kv.value {
Expr::Lit(ref expr) => &expr.lit,
_ => continue,
Comment on lines +309 to +313
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

simple

};
if kv.path.is_ident("describe") {
if describe.is_some() {
return Err(Error::new_spanned(
kv,
"Duplicate `describe` value provided.",
))
}
describe = Some(parse_str_lit(lit)?);
} else if kv.path.is_ident("rename") {
if rename.is_some() {
return Err(Error::new_spanned(
kv,
"Duplicate `rename` value provided.",
))
}
let rename_lit = parse_str_lit(lit)?;
validate_metric_name(&rename_lit)?;
rename = Some(rename_lit)
} else {
return Err(Error::new_spanned(kv, "Unsupported attribute entry."))
}
}
let rename_lit = parse_str_lit(lit)?;
validate_metric_name(&rename_lit)?;
rename = Some(rename_lit)
} else {
return Err(Error::new_spanned(kv, "Unsupported attribute entry."))
_ => return Err(Error::new_spanned(meta, "Unsupported attribute entry.")),
}
}
}

if skip {
metrics.push(MetricField::Skipped(field));
continue
}

let description = match describe {
Some(lit_str) => lit_str.value(),
// Parse docs only if `describe` attribute was not provided
Expand All @@ -294,7 +366,7 @@ fn parse_metric_fields(node: &DeriveInput) -> Result<Vec<Metric<'_>>> {
},
};

metrics.push(Metric::new(field, description, rename));
metrics.push(MetricField::Included(Metric::new(field, description, rename)));
}

Ok(metrics)
Expand Down
14 changes: 5 additions & 9 deletions crates/prune/src/metrics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ use reth_metrics::{metrics, metrics::Histogram, Metrics};
use reth_primitives::PrunePart;
use std::collections::HashMap;

#[derive(Debug, Default)]
#[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct Metrics {
pub(crate) pruner: PrunerMetrics,
/// Pruning duration
pub(crate) duration_seconds: Histogram,
#[metric(skip)]
prune_parts: HashMap<PrunePart, PrunerPartMetrics>,
}

Expand All @@ -21,13 +24,6 @@ impl Metrics {
}
}

#[derive(Metrics)]
#[metrics(scope = "pruner")]
pub(crate) struct PrunerMetrics {
/// Pruning duration
pub(crate) duration_seconds: Histogram,
}

#[derive(Metrics)]
#[metrics(scope = "pruner.parts")]
pub(crate) struct PrunerPartMetrics {
Expand Down
2 changes: 1 addition & 1 deletion crates/prune/src/pruner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ impl<DB: Database> Pruner<DB> {
self.last_pruned_block_number = Some(tip_block_number);

let elapsed = start.elapsed();
self.metrics.pruner.duration_seconds.record(elapsed);
self.metrics.duration_seconds.record(elapsed);

trace!(
target: "pruner",
Expand Down