Skip to content
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

Add support for native histograms in OM parser #1040

Merged
merged 11 commits into from
Sep 20, 2024

Conversation

vesari
Copy link
Contributor

@vesari vesari commented Jun 18, 2024

This PR adds an OM parser for native histograms, as a first step towards the implementation of the following proposal prometheus/proposals#32 . The next steps (as far as this repo is concerned) would be the OM exposition and then obviously the implementation of native histograms generation/writing logic.

Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick read through, but I was thinking maybe we limit the changes in the initial PR to just the parser changes? We could get that in and start iterating on it, and it will then be useful for testing the exposition changes.

@@ -595,6 +595,14 @@ def __init__(self,
registry: Optional[CollectorRegistry] = REGISTRY,
_labelvalues: Optional[Sequence[str]] = None,
buckets: Sequence[Union[float, str]] = DEFAULT_BUCKETS,
# native_hist_schema: Optional[int] = None, # create this dynamically?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the internal code should create the schema, and may even change the schema value on occassion.

Signed-off-by: Arianna Vespri <[email protected]>
@vesari
Copy link
Contributor Author

vesari commented Jul 7, 2024

Just a quick read through, but I was thinking maybe we limit the changes in the initial PR to just the parser changes? We could get that in and start iterating on it, and it will then be useful for testing the exposition changes.

I totally agree!

…nd adapt logic accordigly

Signed-off-by: Arianna Vespri <[email protected]>
@vesari vesari marked this pull request as ready for review July 8, 2024 13:17
@vesari vesari requested a review from csmarchbanks July 8, 2024 13:17
@vesari vesari changed the title WIP: Native histogram support Add support for native histograms in OM parser Jul 9, 2024
Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! I left a handful of comments, they probably don't all need to be addressed in this PR, but could be refactorings in future PRs as well.

Mainly I would like to see as few changes to other files, and especially function signatures, as possible. Even if that means we do some things like skipping values that are histograms for now. That way hopefully no one depends on some code/type changes we might update soon.

Comment on lines 92 to 94
# using a safe float convert on s.value as a temporary workaround while figuring out what to do
# in case value is a native histogram structured value, if that's ever a possibility
output.append(f'{prefixstr}{_sanitize(s.name)}{labelstr} {safe_float_convert(s.value)} {now}\n')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Graphite doesn't have support for native histograms, if for some reason one shows up in a registry we would probably need to drop it with a warning. For now I would say let's not make any changes here as native histograms will be experimental anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're absolutely right. The thing is the mypy linter fails without this change. That's due to the native histogram value being a union. I noticed that only when I pushed the PR for the first time

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved after separating fields

prometheus_client/openmetrics/parser.py Show resolved Hide resolved
Comment on lines 422 to 426
else:
elems = pos_spans_text.split(',')
arg1 = [int(x) for x in elems[0].split(':')]
arg2 = [int(x) for x in elems[1].split(':')]
pos_spans = (BucketSpan(arg1[0], arg1[1]), BucketSpan(arg2[0], arg2[1]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid the else block and move these into the try block. That keeps all the happy path/text parsing code together.

@@ -128,7 +129,7 @@ def _target_info_metric(self):
m.add_sample('target_info', self._target_info, 1)
return m

def get_sample_value(self, name: str, labels: Optional[Dict[str, str]] = None) -> Optional[float]:
def get_sample_value(self, name: str, labels: Optional[Dict[str, str]] = None) -> Optional[Union[float, NativeHistStructValue]]:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to change yet? It would be nice to have no/minimal public interface changes since we won't be using these in the registry quite yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as for the Graphite bridge file. mypy linting fails otherwise.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved after separating fields

@@ -48,6 +65,6 @@ class Exemplar(NamedTuple):
class Sample(NamedTuple):
name: str
labels: Dict[str, str]
value: float
value: Union[float, NativeHistStructValue]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Trying to decide if this should be a union or if we should have a separate field for histogram_value or something like that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the union's "side-effects" in files like registry etc, maybe it should be a separate field? My initial thinking with the union was to possibly avoid function signature changes I guess, but maybe it wouldn't be that problematic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, let's try having a separate Optional field for the native histogram and see what it looks like. It also makes it nice to just check if the value is none or not for if a value is a native histogram.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I separated them, and added a field for native histogram. I added it for last to minimize the risk of breaking something.

has_gsum = True
if s.value < 0:
has_negative_gsum = True
if len(suffix) != 0:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than nesting everything further in what about a continue if len(suffix) is 0?

prometheus_client/openmetrics/parser.py Show resolved Hide resolved
Comment on lines 598 to 605
# native_hist_schema: Optional[int] = None,
# native_hist_bucket_fact: Optional[float] = None,
# native_hist_zero_threshold: Optional[float] = None,
# native_hist_max_bucket_num: Optional[int] = None,
# native_hist_min_reset_dur: Optional[timedelta] = None,
# native_hist_max_zero_threshold: Optional[float] = None,
# native_hist_max_exemplars: Optional[int] = None,
# native_hist_exemplar_TTL: Optional[timedelta] = None,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove these commented out lines until they are actually used.

@vesari
Copy link
Contributor Author

vesari commented Jul 15, 2024

Thanks for the suggested changes, I'll be working on it :)

length: int


class NativeHistStructValue(NamedTuple):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a thought, we could probably just call this NativeHistogram rather than adding StructValue. For initial implementation we might want to prefix with _ as well to these new types to make it clear they are internal only and may change. Or add a comment to that effect.

Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple of nits/removals and then I think this iteration is ready to merge! Thanks a bunch!

@@ -92,3 +92,4 @@ def start(self, interval: float = 60.0, prefix: str = '') -> None:
t = _RegularPush(self, interval, prefix)
t.daemon = True
t.start()

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit, could you just remove this line so we don't have a needless diff/history entry?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done!

@@ -236,6 +236,7 @@ def __init__(self,
sum_value: Optional[float] = None,
labels: Optional[Sequence[str]] = None,
unit: str = '',
native_hist_bucket_factor: Optional[float] = None
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Not sure if we need this here at all (it will need to be in metrics.py). This function is for custom collectors, and I think if someone is creating a native histogram for that case they will end up needing to define all the spans themself. For now I would say just leave it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I deleted it.

prometheus_client/openmetrics/parser.py Show resolved Hide resolved
# check if it's a native histogram with labels
re_nh_without_labels = re.compile(r'^[^{} ]+ {[^{}]+}$')
re_nh_with_labels = re.compile(r'[^{} ]+{[^{}]+} {[^{}]+}$')
print('we are matching \'{}\''.format(text))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should remove the debug printing before merging, there are a couple other lines in this function as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could see just another one, I hope I removed them all now XD

prometheus_client/openmetrics/parser.py Show resolved Hide resolved
prometheus_client/samples.py Show resolved Hide resolved
Copy link
Member

@csmarchbanks csmarchbanks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for all the work on this!

@csmarchbanks csmarchbanks merged commit d7c9cd8 into prometheus:master Sep 20, 2024
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants