metrics-rs / metrics

A metrics ecosystem for Rust.
MIT License
1.1k stars 151 forks source link

migrate from procedural to declarative macros #386

Closed hlbarber closed 11 months ago

hlbarber commented 1 year ago

Changes

The goal here is to not break users and to also maintain the "fast paths" taken by the procedural macro where literals cause static to be used.

This PR is intended as a place of discussion, once it is sufficiently mature I'll close it and reopen a polished PR.

hlbarber commented 1 year ago

I've picked register_counter to migrate first - from the initial experimentation it feels possible. We're passing cargo t --no-fail-fast with a 2 exceptions.

hlbarber commented 1 year ago

The following seem to fix the failing test cases while maintaining the fast paths where possible. Please tell me if this looks off in any way.

https://github.com/metrics-rs/metrics/pull/386/files/c356678f3bd0bab76ad6932a951374ca5a1e484d..5e82f3e41e5ad2034f03359286d2e1a01eb775cf

hlbarber commented 1 year ago

That's the "counter" cross section of macros replaced. After register_counter it isn't really involved.

Given near perfect symmetry between the metric types I don't foresee any nasty obstructions when transitioning the other macros.

I'll run benchmarks shortly to check for any regressions.

hlbarber commented 1 year ago

After restoring doc tests there's a bit more to do, will look into it tomorrow.

hlbarber commented 1 year ago

https://github.com/metrics-rs/metrics/pull/386/commits/0a32d83394342889f0a5bdb8192b3d2c3216186d has made is considerably more branch-y, but still tractable imo. Rather than making use of ? we must enumerate the combinations.

I think with some tinkering this can be compacted. I think perhaps we can parse the arguments as one (or two?) tt and then pass them onto other branches - some prior art can be found in the tracing macros (info etc).

I can push for compactness now, but given https://github.com/metrics-rs/metrics/issues/369 is around the corner I can see an argument for postponing.

tobz commented 1 year ago

I can push for compactness now, but given #369 is around the corner I can see an argument for postponing.

I'd say let's just get the logic correct now and deal with condensing later.

I think with some tinkering this can be compacted. I think perhaps we can parse the arguments as one (or two?) tt and then pass them onto other branches - some prior art can be found in the tracing macros (info etc).

I'm not against the verbose-ness of having a branch for each possible permutation... so long as there's a viable path to, in the future, generalizing that macro logic across the different metric types. Basically, it'd be a small tragedy to just have to duplicate all of it over and over again when we do gauges and histograms... just ripe for introducing a subtle difference.

Having one register_metric macro or something, for example, that we just call into and pass the metric type-appropriate idents, and so on... and it has all of the various branches? That'd be fine.

hlbarber commented 1 year ago

Having one register_metric macro or something, for example, that we just call into and pass the metric type-appropriate idents, and so on... and it has all of the various branches? That'd be fine.

A reason I haven't re-used register_counter in the current impl is that the branch, in metric_macros::get_register_and_op_code for register_counter (found here) uses metrics::recorder and the counter/absolute_counter/etc branch uses metrics::try_recorder (found here).

If I use metrics::recorder for the counter branch I get a regression on the "uninitialized" benchmarks - this is a little suprising to me, I'd expect the NoopRecorder, after optimizations, to be equal in performance to checking Some(_) = try_recorder. I wonder if this is still the case if we implemented Recorder for Option<T> where T: Recorder and made recorder return it, rather than dyn Recorder.

tobz commented 1 year ago

I'd say switch to try_recorder across the board.

The regression is surprising to me too, but also, thinking about it... I'm not sure I actually give a crap if it's like 5ns vs 20ns or whatever when a recorder isn't installed. If it ever matters enough to me, or someone else, well... we can figure something out then.

hlbarber commented 1 year ago

Rest of the macros are migrated. I've DRY'd it up a little bit with the describe and method macros. I haven't taken the try_recorder change yet because it deduplicated less with method macro in place.

I played around a little bit with $(args: tt),*, trying something like

macro_rules counter {
    ($($args:tt),) => $crate::method(register_counter, increment, $($args),*)
}

similar with what we see in the tracing macros. This doesn't work because of the mix of delimiters we see in the macros (, and =>). You can see the exact problem here https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=def715a3d053a4b933ccb4a821260729. Perhaps this is something the next API will side step. I think there's also an argument to keeping the explicit branches in the "macro signatures" to serve as documentation.

metrics-macro is now empty. I'm guessing I should delete the crate from the workspace entirely so that we don't release an empty crate?

tobz commented 1 year ago

Perhaps this is something the next API will side step. I think there's also an argument to keeping the explicit branches in the "macro signatures" to serve as documentation.

I think things are fine as is. Good to see your example as it makes it clear, but in seeing it and seeing what you chose to do instead, it makes me think that making it perfectly DRY isn't as big a readability win as I thought it might be anyways.

metrics-macros is now empty. I'm guessing I should delete the crate from the workspace entirely so that we don't release an empty crate?

Yessss, nuke it from orbit. 👨🏻‍🚀

tobz commented 1 year ago

I plan on taking a final look at this over the next few days.

hlbarber commented 1 year ago

I can transplant this into a formal PR with a proper description now if you'd like?

tobz commented 1 year ago

Nah, no worries. At most, feel free to update the PR title and description and mark it as ready to review and all of that... but no further cleanup/tidying required.

hlbarber commented 12 months ago

On my machine cargo bench, on main vs this branch, shows no obvious regressions - worth double checking on your machine though.

tobz commented 8 months ago

Released as metrics@v0.22.0.

Thanks again for your contribution. ❤️