-
Notifications
You must be signed in to change notification settings - Fork 12.1k
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
Prometheus: Fill missing steps with null values #43622
Conversation
@@ -73,8 +73,8 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query | |||
timeRange := apiv1.Range{ | |||
Step: query.Step, | |||
// Align query range to step. It rounds start and end down to a multiple of step. | |||
Start: time.Unix(int64(math.Floor((float64(query.Start.Unix()+query.UtcOffsetSec)/query.Step.Seconds()))*query.Step.Seconds()-float64(query.UtcOffsetSec)), 0), | |||
End: time.Unix(int64(math.Floor((float64(query.End.Unix()+query.UtcOffsetSec)/query.Step.Seconds()))*query.Step.Seconds()-float64(query.UtcOffsetSec)), 0), | |||
Start: alignTimeRange(query.Start, query.Step, query.UtcOffsetSec), |
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.
Just a refactoring. As we want to use aligning at multiple places, we created function for this.
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 an awesome improvement.
timeField := data.NewFieldFromFieldType(data.FieldTypeTime, len(v.Values)) | ||
valueField := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, len(v.Values)) | ||
baseTimestamp := alignTimeRange(query.Start, query.Step, query.UtcOffsetSec).UnixMilli() | ||
datapointsCount := int((query.End.UnixMilli()-query.Start.UnixMilli())/query.Step.Milliseconds()) + 1 |
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.
Number of data points that we use inNewFieldFromFieldType
as we no longer can use len(v.Values)
.
value := float64(k.Value) | ||
timeField := data.NewFieldFromFieldType(data.FieldTypeTime, datapointsCount) | ||
valueField := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, datapointsCount) | ||
idx := 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.
Also here, we can't rely on index from for _, pair := range v.Values {
and we have to keep the track ourself.
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 all looks good.
Nit: Now that there's more going on, pulling this out to a function that returns timeField, valueField := ...
might read a little easier.
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.
Yeah, I totally agree. I am going to leave this for now though as @toddtreece is working on improving performance and this part of code might (probably will) change.
value := float64(pair.Value) | ||
|
||
for t := baseTimestamp; t < timestamp; t += query.Step.Milliseconds() { | ||
timeField.Set(idx, time.Unix(0, t*1000000).UTC()) |
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 we set no value in valueField for that index, it default to nil (which is what we want).
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.
Looks good. I think we can improve the readability just a bit.
@@ -73,8 +73,8 @@ func (s *Service) executeTimeSeriesQuery(ctx context.Context, req *backend.Query | |||
timeRange := apiv1.Range{ | |||
Step: query.Step, | |||
// Align query range to step. It rounds start and end down to a multiple of step. | |||
Start: time.Unix(int64(math.Floor((float64(query.Start.Unix()+query.UtcOffsetSec)/query.Step.Seconds()))*query.Step.Seconds()-float64(query.UtcOffsetSec)), 0), | |||
End: time.Unix(int64(math.Floor((float64(query.End.Unix()+query.UtcOffsetSec)/query.Step.Seconds()))*query.Step.Seconds()-float64(query.UtcOffsetSec)), 0), | |||
Start: alignTimeRange(query.Start, query.Step, query.UtcOffsetSec), |
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 an awesome improvement.
value := float64(k.Value) | ||
timeField := data.NewFieldFromFieldType(data.FieldTypeTime, datapointsCount) | ||
valueField := data.NewFieldFromFieldType(data.FieldTypeNullableFloat64, datapointsCount) | ||
idx := 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.
This all looks good.
Nit: Now that there's more going on, pulling this out to a function that returns timeField, valueField := ...
might read a little easier.
require.Equal(t, res[0].Fields[0].At(1), time.Unix(2, 0).UTC()) | ||
require.Equal(t, res[0].Fields[0].At(2), time.Unix(3, 0).UTC()) | ||
require.Equal(t, res[0].Fields[1].Len(), 4) | ||
require.Equal(t, res[0].Fields[1].At(1), nilPointer) |
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.
Nit: These can just be require.Nil(t, ...)
* Prometheus: Add empty points when data points missing * Remove newline * Add comments * Improve/Fix test * Remove unused variable (cherry picked from commit d7d6c10)
* Prometheus: Add empty points when data points missing * Remove newline * Add comments * Improve/Fix test * Remove unused variable (cherry picked from commit d7d6c10) Co-authored-by: Ivana Huckova <30407135+ivanahuckova@users.noreply.github.com>
@ivanahuckova is this the new intended behavior with stacking? Prior to |
@PayBas yes this is a correct behaviour. Before the 8.3 there were gaps but in 8.3. we have introduced bug that removed gaps. So 8.3.4 brings it back. 🙂 |
@ivanahuckova thank you for your reply. I am a little confused however. The bottom 2 images (which are the same graph/panel) shows that "Connect null values: Always" will correctly show uninterrupted lines, whereas if we also turn "Stack series" to either "Normal" or "100%", it will re-introduce gaps. I'm concerned this PR has had an unexpected side-effect on the stacking rendering. Shouldn't stacked graphs render uninterrupted when using "Connect null values: Always"? What I'm saying is: my panel settings were showing correctly all the way from |
@PayBas yes, might be a regression. Could you please create separate issue for this. Thank you! |
What this PR does / why we need it:
Previously (before backend migration) on frontend we filled out missing steps with null values. This PR implements this missing functionality. I've decided to implement this on backend so the results of prometheus queries are same everywhere (Dashboard, Explore, Alerting) and we don't have to rely o frontend implementation.
Which issue(s) this PR fixes:
Fixes #42993
How to test:
devenv sources=prometheus
counters_logins{server="backend-01"}
and time rangeLast 5 min
and step10s
devenv_fake-prometheus-data_1
in docker and wait ~1 min. Then start it againOn current main, the lines are connected (incorrect behaviour/bug):
