Skip to content

fix: annotations with a null time_end or a partial overlap are not shown on time series charts - #10027

Merged
nishantmonu51 merged 2 commits into
mainfrom
nishant/fix-annotation-time-end-filter
Oct 21, 2026
Merged

nishantmonu51 merged 2 commits into
mainfrom
nishant/fix-annotation-time-end-filter

Conversation

@nishantmonu51

Copy link
Copy Markdown
Collaborator
  • When the annotation table has a time_end column, the runtime required both time and time_end to fall inside the queried time range. Point annotations with a null time_end were dropped, and range annotations that start before or end after the range were dropped as well.
  • The filter now selects every annotation that overlaps the range (time < end AND (time >= start OR time_end > start)), and the chart anchors a range that starts before the first data point to it so the visible part of the range is drawn.
  • MetricsViewAnnotations wrote the request offset into limit.
  • Documents that range annotations show whenever they overlap the selected time range and that an empty time_end is a point annotation.

Note on the original report: scheduled refreshes do not run in dev deployments, so in a cloud edit session annotations anchored to now() can fall outside the (stale) preview data and nothing renders even with this fix. That behaviour is unchanged here.

Steps to reproduce

  1. Add a model with a time_end column where some rows have a null time_end:
    SELECT now() - INTERVAL 1 DAY AS time, NULL::TIMESTAMP AS time_end, 'Release' AS description
    UNION ALL
    SELECT now() - INTERVAL 8 HOUR AS time, now() - INTERVAL 5 HOUR AS time_end, 'Spike window' AS description
  2. Reference it from a metrics view under annotations: and open its explore with a time range that covers both rows.
  3. Only "Spike window" gets a marker; "Release" is missing. A range that starts before the selected time range is missing as well.

Closes APP-951

Checklist:

  • Covered by tests
  • Ran it and it works as intended
  • Reviewed the diff before requesting a review
  • Checked for unhandled edge cases
  • Linked the issues it closes
  • Checked if the docs need to be updated. If so, create a separate Linear DOCS issue
  • Intend to cherry-pick into the release branch
  • I'm proud of this work!

…shown on time series charts

When the annotation table has a `time_end` column, the runtime required both `time` and `time_end` to fall inside the queried time range, so point annotations with a null `time_end` and ranges that only partly overlap the range were dropped. The filter now selects every annotation that overlaps the range, the chart anchors a range that starts before the first data point to it, and the `MetricsViewAnnotations` handler no longer writes the offset into the limit.
if (
a.endTime &&
bucketStart.toMillis() < dataStartMs &&
a.endTime.toMillis() >= dataStartMs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Why is the end time check needed? Wouldnt just bucketStart.toMillis() < dataStartMs be enough? (Assuming query already returns annotations that overlap with time range)

If this is still needed as a safeguard then the check doesnt match with backend, end > range_start vs end >= range_start

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The annotations and the time series are fetched independently and both keep their previous data while refetching, so after a time range change the previous annotations can briefly be grouped against the new data. The existing bucket.bucketMs < dataStartMs check below already handles that for point annotations; without the end check, the anchoring would promote a stale range that ended before the new window to the first data point. I changed it to > to match the query and explained this in the comment.

if annotation.HasTimeEnd {
b.WriteString(" AND time_end >= ? AND time_end < ?")
args = append(args, start, end)
b.WriteString("time < ? AND (time >= ? OR time_end > ?)")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

nit: invert to (time >= ? OR time_end > ?) AND time < ? so that it reads similar to the other query.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Done.

bucketStart.toMillis() < dataStartMs &&
a.endTime.toMillis() >= dataStartMs
) {
bucketStart = data[0].ts;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Doesnt this need startOf(unit)?

@nishantmonu51
nishantmonu51 merged commit 855afa8 into main Oct 21, 2026
19 checks passed
@nishantmonu51
nishantmonu51 deleted the nishant/fix-annotation-time-end-filter branch October 21, 2026 13:38
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