-
Notifications
You must be signed in to change notification settings - Fork 184
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
Switch from protobuf to prost #387
base: master
Are you sure you want to change the base?
Conversation
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
…t the code having to be checked into git. Signed-off-by: Folyd <lyshuhow@gmail.com>
Signed-off-by: Folyd <lyshuhow@gmail.com>
Looks good, but you may need to rebase. |
@breeswish I think it's in your hands now 😅 |
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.
I am in favor of moving to prost
. Thanks for putting in all the work.
With the move to prost
we seem to loose a lot of type safety, moving many checks, e.g. whether a counter is Some
when type Counter
is set, from compile time to runtime. The approach taken in this pull request, is to gracefully use the default value. I don't think this is a valid approach as it sacrifices a lot of safety we gain through the type system.
I see two ways forward:
- Handle each check at runtime properly, returning a
Result:Err
in case there is a mismatch. While a valid option, I expect this to be very noisy. - Introduce an intermediate data representation like suggested in the issue which represents all constraints in the data model itself, thus checked at compile time. This intermediary data representation would be created through
Collector::collect
and then be used inencoder/pb.rs
andencoder/text.rs
with the former going from intermediate data representation to the current representation generated byprost
.
@Folyd does the above make sense to you? If not, I am happy to extend my expatiation. If it does, let me know what you think.
@@ -31,7 +31,9 @@ impl Encoder for ProtobufEncoder { | |||
for mf in metric_families { | |||
// Fail-fast checks. | |||
check_metric_family(mf)?; | |||
mf.write_length_delimited_to_writer(writer)?; | |||
let mut buf = vec![]; |
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.
Bummer that we need this intermediary buffer. We could change the Encoder
trait, requiring writer
to also implement BufMut
. But that would require a new dependency bytes
on our public interface.
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.
Exactly. Actually, I did change to the BufMut
trait in my local branch. I'm haven't submit that commit right now. What do you think? @breeswish @hdost
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.
Let's keep non-protobuf feature clean.. This immediate buffer only allocate once each encode call and I think the performance seems to be fine.
if !help.is_empty() { | ||
let name = match &mf.name { | ||
Some(v) => &**v, | ||
None => "", |
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.
Why default to an empty string here? I don't think a metric without a name is valid. I would expect to return an error instead. What do you think?
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 just a simple migration from mf.get_name()
.
pub fn get_name(&self) -> &str {
match self.name.as_ref() {
Some(v) => &v,
None => "",
}
}
I'm in favor of returning an error. 👌
.r#type | ||
.map(MetricType::from_i32) | ||
.flatten() | ||
.unwrap_or_default(); |
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.
I don't think we should default here, but instead skip encoding the TYPE
line, or return an error, as we would fail in the match metric_type
later on anyways, what do you think?
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.
Good point. Thanks.
MetricType::Counter => { | ||
let value = match &m.counter { | ||
Some(v) => v.value.unwrap_or_default(), | ||
None => 0.0, |
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.
If metric type is Counter
, but m.counter
is None
, I would expect an error to be thrown instead of silently using 0.0
. Am I missing something?
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.
Simply migration too:
pub fn get_counter(&self) -> &Counter {
self.counter.as_ref().unwrap_or_else(|| Counter::default_instance())
}
Also, I think it's a good choice to return an error.
Hi @mxinden. Thanks for reviewing.
I'm afraid I'm not 100% get what the |
The below summarizes the data flow used on
The My suggestion is to introduce our own data layout which each metric type would return in their implementation of Does the above make sense to you @Folyd? If so, what do you think? |
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.
About the intermediate data type, I have another idea. Can we implement a MetricFamily
that is simply a Vec of all metrics and implement two Encoder
(text, protobuf) for this simple MetricFamily
? In this way, we no longer need to transform from a memory layout to a plain modal when we use the text format. For the protobuf format, protobuf structs still need to be filled (so that the workflow is memory layouot -> protobuf struct -> serialize). What do you think @mxinden
Related issue: #384