Skip to content

Timeseries results are incoherent for case interval is out of range and case false filter. - #5649

Merged
b-slim merged 8 commits into
apache:masterfrom
b-slim:fix_timeseries
Apr 23, 2018
Merged

Timeseries results are incoherent for case interval is out of range and case false filter.#5649
b-slim merged 8 commits into
apache:masterfrom
b-slim:fix_timeseries

Conversation

@b-slim

@b-slim b-slim commented Apr 16, 2018

Copy link
Copy Markdown
Contributor

Issue
Timeseries results are incoherent for case interval is out of range and case filter matching zero rows.
IMO intervals and filters need to behave the same thus if user issue a query with SkipEmptyBuckets=false with count aggregator result must be the similar.
Please look at the unit test to understand the issue (FYI it is not a SQL issue but SQL is a good way to express the issue).
Fix
TBH Am no sure what is the best way to fix this. I have added some add some nasty checks for the broker side that returns an empty query runner that does the trick but it make the code looks odd.
For the Histroical side i was able to added as part of the Timeseries Internal Query processing logic.
Am still thinking about a better way to fix this, the reason i am submitting this PR is to collect feedback/suggestions please let me know if you see a better way to fix this.

Fix V2 I have taken the route suggested by @gianm and add the the check to mergeResutls.
The new patch checks for empty results for case Granularity ALL and NoSkipping of empty buckets.


This change is Reviewable

@gianm

gianm commented Apr 16, 2018

Copy link
Copy Markdown
Contributor

What do the test queries return without the patch?

@b-slim

b-slim commented Apr 16, 2018

Copy link
Copy Markdown
Contributor Author

@gianm thanks for taking a look it returns nothing []

@gianm

gianm commented Apr 16, 2018

Copy link
Copy Markdown
Contributor

If the goal is to get timeseries results to be more SQL-compatible then this patch isn't quite right: we only want it to run for the granularity = all case. SQL queries should only return a "zeroed out" row when there is no grouping. And a timeseries query with granularity != all does have a grouping (something like group by floor(__time to X)), so it should return no rows at all rather than a "zeroed out" row.

It probably also makes more sense for this logic to be in the toolChest mergeResults rather than in the mergeRunners? mergeRunners from what I have seen is mainly concerned with taking care of running the runners in parallel, and mergeResults is mainly concerned about post-processing stuff.

@gianm

gianm commented Apr 16, 2018

Copy link
Copy Markdown
Contributor

@b-slim you probably also need to edit the test for testGroupByWithFilterMatchingNothing

@gianm

gianm commented Apr 16, 2018

Copy link
Copy Markdown
Contributor

And if we don't already have one, I'd add a test for a query like:

SELECT COUNT(*) FROM foo WHERE dim1 = 'nonexistent' GROUP BY FLOOR(__time TO DAY)

This one should return no rows.

@b-slim

b-slim commented Apr 16, 2018

Copy link
Copy Markdown
Contributor Author

@gianm yes the main drive is to be more or like SQL standard. But also i though this can be seen as a bugISH case, if you consider interval as a filter then you would expect it to return results like other filter do. Anyway am okay with narrowing this to Granularity ALL only.

b-slim added 3 commits April 16, 2018 20:44
Change-Id: I92180498e2e6695212b286d980e349c136c78c86
Change-Id: I20c83095072bbf3b4a3a57dfc1934d528e2c7a1a
Change-Id: I1d88fab500c615bc46db4f4497ce93089976441f
@b-slim

b-slim commented Apr 20, 2018

Copy link
Copy Markdown
Contributor Author

@gianm i have added the tests can you please take a quick look on this.

QUERY_CONTEXT_DONT_SKIP_EMPTY_BUCKETS,
"SELECT exp(count(*)) + 10, sum(m2) FROM druid.foo WHERE dim2 = 0",
CalciteTests.REGULAR_USER_AUTH_RESULT,
null,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please include the query in these three tests - it really improves the quality of the tests, by making sure the planner is generating the exact query that we want, in addition to the results being correct.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure will do.


final Sequence<Result<TimeseriesResultValue>> finalSequence;

if (query.getGranularity().equals(Granularities.ALL) && !query.isSkipEmptyBuckets() && baseResults.toList()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Calling baseResults.toList() here materializes the results, meaning in the case where it is not empty we will actually compute it twice. And also it won't stream. To fix this, you might be able to do a trick like the one for grandTotal, where the initial sequence is lazily mapped and then concatted with a new sequence that has the extra rows you want to add.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@gianm valid point but since the granularity is all, it is one data point anyway thus was assuming it is not too bad to call to.List(), what you think?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Ah yeah, that's a good point, I forgot about short-circuiting :)

IMO, to make the code clearer & more idiot-proof it would be good to move the toList inside the if and comment that it's ok here since there's at most one granularity. Then nobody will make the same mistake in reading it that I did.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure will do.

b-slim added 2 commits April 20, 2018 08:36
Change-Id: I56cdd980e44f0685806efb45e29031fa2e328ec4
Change-Id: I42fdd28da5471f6ae57d3962f671741b106300cd
@b-slim b-slim added this to the 0.13.0 milestone Apr 20, 2018

@gianm gianm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @b-slim, just a couple minor comments left about the timestamp & testing.

final Sequence<Result<TimeseriesResultValue>> finalSequence;

if (query.getGranularity().equals(Granularities.ALL) && !query.isSkipEmptyBuckets()) {
//Usally it is NOT Okay to materialize results via toList(), but Granularity is ALL thus we have only one record

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hmm. I just realized this means that the query runner will now issue the query and block when run is called, rather when the sequence is actually accumulated. I think this is probably fine, at least, I can't think of a reason why it would break anything…

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

TBH same here and i feel using the merge sequence (streaming fashion) is gonna make code more unreadable for no big gains.

), null));
aggregatorNames[i] = aggregatorSpecs.get(i).getName();
}
TimeseriesResultBuilder bob = new TimeseriesResultBuilder(DateTimes.EPOCH);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Usually timeseries with "all" granularity returns the start of the data interval as the timestamp. Here, there is no data interval (since there is no data), but it would probably be more consistent to return at least the start of the time interval. How about returning the start timestamp of the first interval in query.getIntervals()? And if there are no intervals (maybe the user specified []) then returning DateTimes.EPOCH.

I am not too picky about which way we go. But I do think that either way, the specific timestamp that gets returned can't be tested through the SQL layer (it would ignore the timestamp), so there should be a unit test added to TimeseriesQueryRunnerTest which verifies the timestamp.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@gianm good point, wondering if we should just set it to null since there is no data for that interval?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it should be non-null, since people have come to expect that for granularity: all. It would probably break stuff for it to be null.

b-slim added 2 commits April 23, 2018 09:55
Change-Id: I0bd414d2278e3eddc2810e4f5080e6cf6a117f12
Change-Id: I99a2380934c9ab350ca934c56041dc343c08b99f
@b-slim

b-slim commented Apr 23, 2018

Copy link
Copy Markdown
Contributor Author

@gianm thanks I have added tests and the start intervals

@gianm gianm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Last couple of comments I think. Other than the parts I mentioned the rest looks good.


if (query.getGranularity().equals(Granularities.ALL) && !query.isSkipEmptyBuckets()) {
//Usally it is NOT Okay to materialize results via toList(), but Granularity is ALL thus we have only one record
finalSequence = baseResults.toList().isEmpty() ? Sequences.simple(Collections.singletonList(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I believe this will end up running the query on the cluster twice (once for the toList and then another time, later, when the finalSequence is accumulated). We should avoid this by caching baseResults.toList() and doing a Sequences.simple out of it once it's computed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

sure make sense

)
);

// Must create a toolChest so we can run mergeResults (which applies grand totals).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This comment doesn't make sense, since we aren't doing grand totals. Maybe it should say,

    // Must create a toolChest so we can run mergeResults (which creates the zeroed-out row).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

yeah copy and past form other test will fix it

Change-Id: I726a3b905a9520d8b1db70e4ba17853c65c414a4
@b-slim

b-slim commented Apr 23, 2018

Copy link
Copy Markdown
Contributor Author

@gianm thanks

@gianm gianm left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM, thanks @b-slim

@nishantmonu51 nishantmonu51 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

👍 LGTM post travis

@b-slim
b-slim merged commit 73da742 into apache:master Apr 23, 2018
@b-slim
b-slim deleted the fix_timeseries branch April 26, 2018 01:27
sathishsri88 pushed a commit to sathishs/druid that referenced this pull request May 8, 2018
…nd case false filter. (apache#5649)

* adding some tests

Change-Id: I92180498e2e6695212b286d980e349c136c78c86

* added empty sequence runner

Change-Id: I20c83095072bbf3b4a3a57dfc1934d528e2c7a1a

* treat only granularity ALL

Change-Id: I1d88fab500c615bc46db4f4497ce93089976441f

* moving toList within If and add expected queries

Change-Id: I56cdd980e44f0685806efb45e29031fa2e328ec4

* typo

Change-Id: I42fdd28da5471f6ae57d3962f671741b106300cd

* adding tests and fix logic of intervals

Change-Id: I0bd414d2278e3eddc2810e4f5080e6cf6a117f12

* fix style

Change-Id: I99a2380934c9ab350ca934c56041dc343c08b99f

* comments review

Change-Id: I726a3b905a9520d8b1db70e4ba17853c65c414a4
riovic918data pushed a commit to riovic918data/druid that referenced this pull request Jun 12, 2026
…nd case false filter. (apache#5649)

* adding some tests

Change-Id: I92180498e2e6695212b286d980e349c136c78c86

* added empty sequence runner

Change-Id: I20c83095072bbf3b4a3a57dfc1934d528e2c7a1a

* treat only granularity ALL

Change-Id: I1d88fab500c615bc46db4f4497ce93089976441f

* moving toList within If and add expected queries

Change-Id: I56cdd980e44f0685806efb45e29031fa2e328ec4

* typo

Change-Id: I42fdd28da5471f6ae57d3962f671741b106300cd

* adding tests and fix logic of intervals

Change-Id: I0bd414d2278e3eddc2810e4f5080e6cf6a117f12

* fix style

Change-Id: I99a2380934c9ab350ca934c56041dc343c08b99f

* comments review

Change-Id: I726a3b905a9520d8b1db70e4ba17853c65c414a4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants