feat: Multi instance metrics#31
Conversation
jvanbuel
left a comment
There was a problem hiding this comment.
To be quite honest, I don't find the list of measurements that elegant (especially that you now always need to index with zero to get the default behaviour of one measurement per metric), but I do understand the reasoning. The requirement that downstream metrics need to decide what to do with multiple measurements also makes sense to. The utility function to assert there is only one measurement seems like a good way to make dealing with this easier.
Maybe one idea: what if instead of list of measurements, the signature of measure is an iterator of measurements? Than the default behaviour becomes calling next. I don't know why, but to me that feels a bit better. But that's a feeling, which is not really sound for making decisions.
| logger.debug("Executing provider: %s", provider.name) | ||
| data = provider.provide() | ||
| context[provider.name] = data | ||
| if provider.is_tag_provider(): |
There was a problem hiding this comment.
The tag provider is treated as a special provider, but I'm not sure if that's absolutely necessary? What prevents the tag provider from just enriching the context, and the metrics trying to fetch data from the context to populate their tags?
On the other hand, we do have a tag attribute for each metric, so tags are kind of special. Not blocking, just wanted to hear your thoughts on this.
There was a problem hiding this comment.
Yeah I agree, I see no real reason why we should treat it as special when executing the providers, using the context is much more consistent and simplifies the code a bit too. In the MetricCalculator we can then just access the tags from the context to add it to the tags attribute of the measurements.
I refactored it here: #33
| if len(instances) > 1 | ||
| } | ||
| if duplicates: | ||
| # Report the first duplicate found |
There was a problem hiding this comment.
Why only report the first duplicate?
There was a problem hiding this comment.
No reason I think. I changed it so it reports all duplicates.
d22049a
| name, classes = next(iter(duplicates.items())) | ||
| raise DuplicateMetricNameError(name, classes) | ||
| name, instances = next(iter(duplicates.items())) | ||
| raise DuplicateMetricNameError(name, [type(m) for m in instances]) |
There was a problem hiding this comment.
doesn't the iterator return a list of the same types? Or do I misinterpret this?
There was a problem hiding this comment.
It gives a list of Metric (classes) that share the same name, so it could be different types if you gave the same name to 2 different metrics I think.
@jvanbuel I agree it does not feel super intuitive and is a bit ugly with the list, especially the long type hint. I propose an alternative using a |
This PR adds the ability to configure multiple instances for the same Metric class.
Note: also includes the following:
Context
#29 made it possible to configure metrics using the constructor, which is more intuitive than subclassing.
It is however not possible yet to have more than one instance for the same metric. This is a common use case for for example the
GitTrackedFileCountMetric:Before, you would define multiple metric classes that track certain files like this:
This is however possible now because of #29 by just creating multiple instances of the
GitTrackedFileCountMetric:except that the calculation of metrics still assumed one instance per metric class which would only keep a single
Measurementout of the 3 configured metrics above.Summary
calculatemethod of a metric to handle multiple Measurements of a dependent metric. Metric dependencies keep being defined using Metric classes.