-
Notifications
You must be signed in to change notification settings - Fork 270
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
refactor(http/prom): Simplify record_response
middleware
#3242
base: main
Are you sure you want to change the base?
Conversation
fn init_response<B>(&mut self, rsp: &http::Response<B>); | ||
fn init_response(&mut self, rsp: &http::response::Parts); |
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.
this is a core insight i want to highlight.
we don't need or want to see the response body in our implementations of this hook. we do want to e.g. record the start time, but for now, using Parts
gives us most of the same information without having to lug around that darn B
type parameter.
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.
ditto for MkStreamLabel
. inspecting Parts
means that we can have object-safe versions of these traits, crucial if we want to lean on type erasure and box these up over in our application code.
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.
This is awesome! Getting rid of the parameter simplified a lot by the looks of it.
nb: this isn't expected to compile right now, i'm refraining from updating app code until the |
0b475f7
to
a642395
Compare
pub struct ResponseMetrics<DurL, StatL> { | ||
duration: DurationFamily<DurL>, | ||
statuses: Family<StatL, Counter>, | ||
pub struct ResponseMetrics<L: StreamLabel> { | ||
duration: DurationFamily<L::DurationLabels>, | ||
statuses: Family<L::StatusLabels, Counter>, |
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.
here's another core idea in this branch i'd like to highlight:
in the outbound proxy code today, we define an alias tying DurL
and StatL
to the same L: StreamLabel
implementation. so, the two parameters here aren't giving us any benefit or serving any need, and we can simplify the code in this submodule (and the equivalent request.rs
sibling) by making this generic across L
rather than its underlying label types.
impl<DurL, StatL> Default for ResponseMetrics<DurL, StatL> | ||
where | ||
StatL: EncodeLabelSet + Clone + Eq + std::fmt::Debug + std::hash::Hash + Send + Sync + 'static, | ||
DurL: EncodeLabelSet + Clone + Eq + std::fmt::Debug + std::hash::Hash + Send + Sync + 'static, | ||
{ | ||
impl<L: StreamLabel> Default for ResponseMetrics<L> { |
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.
👌 👩🍳 💋 voila!
26975e4
to
80de30c
Compare
Signed-off-by: katelyn martin <[email protected]>
Signed-off-by: katelyn martin <[email protected]>
`MkStreamLabel` is, in short, a generic `(&Request) -> Option<StreamLabel>` function. we use it to inspect a request, and potentially provide the caller with an object that can provide relevant labels. the `StreamLabel` interface includes associated types for the labels used for metrics related to request/response duration, and counting status codes. we do not however, actually need to separately define these associated types in the `MkStreamLabel` contract. instead, we can return a generic `StreamLabel` of some sort, and leave the responsibility of the (admittedly baroque) associated type access to our type aliases like `RecordResponseDuration` and `RecordRequestDuration`. this change has a pleasant knock-on effect of leaving a number of the labels submodule's type aliases unused. this commit accordingly removes aliases like `HttpRouteRsp`, `GrpcRouteRsp`, `HttpRouteBackendRsp`, and `GrpcRouteBackendRsp`. this is a small initial step towards simplifying code that must interact with the `MkStreamLabel` interface. Signed-off-by: katelyn martin <[email protected]>
our north star is making our metrics middleware easier to work with. we want `StreamLabel` to be boxable. this will let us avoid needing to thread baroque generics throughout our layer / service code, and instead rely on dynamic dispatch instead of static `L: StreamLabel` bounds. in order to move in that direction, we must take a small step back first. these traits must be object-safe in order to do that. for a trait to be object safe, per the Rust Reference, we may not use any function-level generics. accordingly, this moves the `<B>` bounds up to the top-level traits. this makes some of our type aliases icky, and introduces new generic parameters to various types/interfaces. we will be able to walk those back in subsequent commits, now that these traits can be placed in e.g. `Box<dyn StreamLabel>` values. NB: as this is an "Zwischenzug"¹ commit, we don't update the app crates. this commit will fail to compile if the entire workspace is built. Signed-off-by: katelyn martin <[email protected]> 1 - https://en.wikipedia.org/wiki/Zwischenzug Signed-off-by: katelyn martin <[email protected]>
this commit updates the signature of `mk_stream_labeler()` and `init_response()` to instead examine the `http::request::Parts` and `http::response::Parts`, respectively. this is another step to simplify our label traits. no implementation of `MkStreamLabel` examines the body of the request. moreover, none even uses the `req` parameter at all! the intent of this signature seems reasonable however, we may want to look at certain requests and opt to ignore them. one could imagine an `l5d-*` header to refrain from installing telemetry on the response body, for example. however, as the last commit showed, we are subject to plenty of pain related to body generics. let's remove the need for them. Signed-off-by: katelyn martin <[email protected]>
now that we have switched over to inspect the `Parts` head material of the requests and responses, we can remove these type parameters, whilst remaining object-safe. nice! Signed-off-by: katelyn martin <[email protected]>
`L` and `M` are not contained within `NewRecordResponse`. this PhantomData marker exists to make this generic over the metric and label types it is generating. strictly speaking, there isn't really a `() -> (L, M)` function in play, though `ExtractParams` is _close_. this is a little bit subjective, but we can constrain this just as well with a `PhantomData<(L, M)>`. Signed-off-by: katelyn martin <[email protected]>
this commit updates the `MkStreamLabel` and `StreamLabel` trait implementations in the outbound proxy code to reflect their latest signatures. Signed-off-by: katelyn martin <[email protected]>
there is a lot of redirection caused by reëxporting `MkStreamLabel` from various `metrics.rs` places. this commit adds `MkStreamLabel` to the list of symbols exposed by the top-level namespace of `linkerd_http_prom`, and consistently imports that trait from there, instead of various local reëxports. Signed-off-by: katelyn martin <[email protected]>
this is another slightly subjective change. i had a fair amount of difficulty learning how the different pieces of the metrics infrastructure fit together. a personal roadblock to getting oriented is that many of the label and metrics structures are reëxported from various places, as are some of the traits provided by the underlying `linkerd-http-prom` library. this removes the `pub use` in the backend metrics submodule. this makes imports in tests mildly more involved, but has the upside that types live in, and are imported from, a single place. Signed-off-by: katelyn martin <[email protected]>
Signed-off-by: katelyn martin <[email protected]>
this removes a type parameter on `RequestMetrics` and `ResponseMetrics`, which in practice were being tied to the same `L: StreamLabel` implementation. this greatly simplifies code in the `record_response` middleware, and removes some type aliases from the route- and backend-level metrics submodules. see the diff sections below shaped like this: ```diff -impl<DurL, StatL> RequestMetrics<DurL, StatL> -where - DurL: EncodeLabelSet + Clone + Eq + std::fmt::Debug + std::hash::Hash + Send + Sync + 'static, - StatL: EncodeLabelSet + Clone + Eq + std::fmt::Debug + std::hash::Hash + Send + Sync + 'static, -{ +impl<L: StreamLabel> RequestMetrics<L> { ``` Signed-off-by: katelyn martin <[email protected]>
this adds some documentation, mostly pointing out that this submodule exists to provide trait implementations to encode label sets, for use in accessing metrics from a `Family`. Signed-off-by: katelyn martin <[email protected]>
this is a bit of a self-contained system, with `ResponseFuture` acting as the public face of it. let's codify that by making it its own submodule. Signed-off-by: katelyn martin <[email protected]>
80de30c
to
f80f790
Compare
record_response
middleware
type DurationLabels: EncodeLabelSet | ||
+ Clone | ||
+ Eq | ||
+ std::fmt::Debug | ||
+ std::hash::Hash | ||
+ Send | ||
+ Sync | ||
+ 'static; | ||
type StatusLabels: EncodeLabelSet | ||
+ Clone | ||
+ Eq | ||
+ std::fmt::Debug | ||
+ std::hash::Hash | ||
+ Send | ||
+ Sync | ||
+ 'static; | ||
|
||
type StreamLabel: StreamLabel< | ||
DurationLabels = Self::DurationLabels, | ||
StatusLabels = Self::StatusLabels, | ||
>; | ||
type StreamLabel: StreamLabel; |
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.
much simpler! 🙂
Signed-off-by: katelyn martin <[email protected]>
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.
One minor nit, everything else looks great!
fn init_response<B>(&mut self, rsp: &http::Response<B>); | ||
fn init_response(&mut self, rsp: &http::response::Parts); |
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.
This is awesome! Getting rid of the parameter simplified a lot by the looks of it.
This reverts commit 9ea61d6. > I believe `fn() -> T` allows covariance over T without triggering `dropck`. I _think_ we'd want to keep that, since this produces `(L, M)` via the extract instead of storing it. @sfleen makes the astute point that the marker type has some covariance properties we should preserve. accordingly, this reverts the previous commit changing that `PhantomData`. #3242 (comment) Co-Authored-By: Scott Fleemer <[email protected]> Signed-off-by: katelyn martin <[email protected]>
this branch makes a variety of small, non-breaking changes to the
linkerd-http-prom
crate, and the outbound proxy code related to route- and backend-level metrics.this branch does not make particularly significant breaking changes to the
StreamLabel
orMkStreamLabel
traits. this branch is focused on minimizing the type complexity of these traits, and consequently in dependent code.these changes are provided in distinct, atomic commits. readers are encourages to review this branch commit-by-commit. the primary changes that are worth highlighting, mentioned in comments below are:
StreamLabel
is now object-safe, making use ofdyn StreamLabel
possible. we now userequest::Parts
andresponse::Parts
parameters in our trait methods. this means that we no longer need to be generic across request/response bodies, but retain the ability to inspect e.g. status codes.the
StreamLabel
interface includes associated types for the labelsused for metrics related to request/response duration, and counting
status codes. we do not however, actually need to separately define these associated
types in the
MkStreamLabel
contract. these types are removed.ResponseMetrics
andRequestMetrics
are generic overL: StreamLabel
. these were previously generic overDurationLabels
andStatusLabels
, even though the same parentL
was used in practice.documentation to various interfaces is added.