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 😅 |
mxinden
left a comment
There was a problem hiding this comment.
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:Errin 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::collectand then be used inencoder/pb.rsandencoder/text.rswith 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.
| // 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.
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.
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.
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.
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.
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.
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?
| MetricType::Counter => { | ||
| let value = match &m.counter { | ||
| Some(v) => v.value.unwrap_or_default(), | ||
| None => 0.0, |
There was a problem hiding this comment.
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.
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.
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