-
Notifications
You must be signed in to change notification settings - Fork 179
Fix issues (1), (2) and (3) described in #328 #329
Conversation
Signed-off-by: Alin Sinpalean <[email protected]>
Ping? |
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.
Sorry I took longer to review, was busy with a couple of things. LGTM, except for minor nits.
Thanks for this!
querier.go
Outdated
iCur := it.i | ||
for ; it.i < len(it.chunks); it.i++ { | ||
// Skip chunks with MaxTime < t. | ||
if it.chunks[it.i].MaxTime >= t { |
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 can be simplified to:
if it.chunks[it.i].MaxTime >= t {
continue
}
// Don't reset the iterator unless we've moved on to a different chunk.
if it.i != iCur {
it.cur = it.chunks[it.i].Chunk.Iterator()
if len(it.intervals) > 0 {
it.cur = &deletedIterator{it: it.cur, intervals: it.intervals}
}
}
for it.cur.Next() {
t0, _ := it.cur.At()
if t0 > it.maxt || it.cur.Err() != nil {
return false
}
if t0 >= t {
return true
}
}
This reduces the indentation and makes it easier to read.
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.
Done, thanks.
@@ -987,7 +1026,7 @@ func TestSeriesIterator(t *testing.T) { | |||
{10, 22}, {203, 3493}, | |||
}, | |||
|
|||
seek: 203, | |||
seek: 101, |
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.
Why would this return false
?
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.
Because the [mint, maxt]
range is [2, 202]
and there are no points in the [101, 202]
range.
Previously the test was doing a seek()
past maxt
, which returned immediately because there is an explicit check for that. With the earlier code seek(101)
was returning true
and positioning the iterator at time 203
, which was incorrect.
Signed-off-by: Alin Sinpalean <[email protected]>
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.
All done, PTAL.
querier.go
Outdated
iCur := it.i | ||
for ; it.i < len(it.chunks); it.i++ { | ||
// Skip chunks with MaxTime < t. | ||
if it.chunks[it.i].MaxTime >= t { |
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.
Done, thanks.
@@ -987,7 +1026,7 @@ func TestSeriesIterator(t *testing.T) { | |||
{10, 22}, {203, 3493}, | |||
}, | |||
|
|||
seek: 203, | |||
seek: 101, |
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.
Because the [mint, maxt]
range is [2, 202]
and there are no points in the [101, 202]
range.
Previously the test was doing a seek()
past maxt
, which returned immediately because there is an explicit check for that. With the earlier code seek(101)
was returning true
and positioning the iterator at time 203
, which was incorrect.
Ping? |
@gouthamve ^ can you take a look at this? |
@free thanks for all the analysis in the issue – seems we've missed quite a few things. |
[Apologies for dropping this, GitHub (or some mail server) decided I'm probably not interested in email notifications from this PR.]
Seems like that particular test was actually only confusing rather than actually broken: the test code at line 1066 actually filters out samples that fall outside I'd rather not go down the rabbit hole of rewriting the unit tests, though. I do have a day job that doesn't involve as much Prometheus as I might otherwise like. |
@gouthamve @fabxc: Ping? |
return false | ||
} | ||
// Return early if already past t. | ||
if t0, _ := it.cur.At(); t0 >= t { |
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 realized that this might call At()
on an iterator that Next()
or Seek()
might not have been called on. It works if one assumes all timestamps to be strictly positive and that the iterator will return a timestamp of 0 when uninitialized.
Should I remove it?
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.
Well, crap. This is the thing that makes Seek()
not advance the iterator when seeking for a timestamp that's in the past (compared to the iterator's position).
So it looks to me like we need to change the contract of either chunkenc.Iterator.At()
to return something like math.MinInt64
; or that of tsdb.SeriesIterator.Seek()
to always advance the iterator. The latter seems more radical.
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.
I've changed chunkenc.Iterator.At()
to always return math.MinInt64
before it has advanced (and documented it as such). Everything is good in the world again.
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.
Yep, before the contract used to be that At()
shouldn't be called before Next()
or Seek()
and it shouldn't be called after they return false
.
I like the newer contract of returning math.MinInt64
for non-initialised Iterator
s.
Signed-off-by: Alin Sinpalean <[email protected]>
querier_test.go
Outdated
@@ -1116,6 +1155,24 @@ func TestChunkSeriesIterator_DoubleSeek(t *testing.T) { | |||
testutil.Equals(t, float64(2), v) | |||
} | |||
|
|||
// Regression for: https://github.com/prometheus/tsdb/pull/327 |
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.
I think the test itself explains the desired behaviour so don't think we even need to link it to the issue.
/pull/327 was closed so If you prefer to keep it there maybe point this to https://github.com/prometheus/tsdb/issues/328
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.
Removed.
@@ -783,6 +783,9 @@ func newChunkSeriesIterator(cs []chunks.Meta, dranges Intervals, mint, maxt int6 | |||
|
|||
func (it *chunkSeriesIterator) Seek(t int64) (ok bool) { | |||
if t > it.maxt { | |||
it.i = len(it.chunks) | |||
} | |||
if it.cur.Err() != nil || it.i >= len(it.chunks) { | |||
return false | |||
} |
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.
so here we are first advancing and than failing. Normally my logic would be that if an operation failed it shouldn't change the internal state.
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.
We're not failing, we're simply advancing to the requested timestamp and returning false
, meaning there are no more values.
If we would not advance, then a Seek
before maxt
following a Seek
past maxt
would incorrectly position the iterator on a valid value.
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.
Signed-off-by: Alin Sinpalean <[email protected]>
…anced. Signed-off-by: Alin Sinpalean <[email protected]>
Any chance of getting this merged? Although please note that due to the requirement that |
querier.go
Outdated
} | ||
} | ||
return false | ||
} | ||
|
||
func (it *chunkSeriesIterator) At() (t int64, v float64) { | ||
// Should it panic if it.cur.Err() != nil || it.i >= len(it.chunks)? |
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 comment can be removed.
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.
Right, it is now OK to call this at any time. Done.
return false | ||
} | ||
// Return early if already past t. | ||
if t0, _ := it.cur.At(); t0 >= t { |
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.
Yep, before the contract used to be that At()
shouldn't be called before Next()
or Seek()
and it shouldn't be called after they return false
.
I like the newer contract of returning math.MinInt64
for non-initialised Iterator
s.
Signed-off-by: Alin Sinpalean <[email protected]>
On Wed, Sep 12, 2018 at 11:47 AM Ganesh Vernekar ***@***.***> wrote:
***@***.**** commented on this pull request.
------------------------------
In querier_test.go
<#329 (comment)>:
> @@ -448,6 +458,18 @@ func TestBlockQuerier(t *testing.T) {
),
}),
},
+ {
This test seems to be working even without this PR. Any reason for adding?
[Replying by email because I cant, for the life of me, find this comment on
GitHub.]
I don't remember specifically, but if I had to guess it's (a) because of a
bug I had introduced during my change and wanted to verify whether I had
then fixed it; and (b) because it's a use case that's not already covered
by the other test cases.
… —
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#329 (review)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AelZ0hvWsL9JgPWPBq6ij7CpGZ2zaWS3ks5uaNhCgaJpZM4T4hnj>
.
|
@@ -658,7 +658,7 @@ func (s *chunkSeries) Iterator() SeriesIterator { | |||
type SeriesIterator interface { | |||
// Seek advances the iterator forward to the given timestamp. | |||
// If there's no value exactly at t, it advances to the first value | |||
// after t. | |||
// after t. Has no effect if already past t. |
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.
// Seek advances the iterator forward to the given timestamp.
// If there's no value exactly at t, it advances to the first value after t.
// Seek has no effect if already past t.
something like this reads nicer.
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.
Done.
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.
Added to #665 as well
}, | ||
chunks: [][]sample{ | ||
{ | ||
{1, 2}, {10, 11}, |
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.
{1, 2}, {10, 11}, | |
{1, 1}, {10, 10}, |
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.
Done.
} | ||
|
||
// Seek for 3, then 2. Iterator should remain positioned on 3. | ||
res := newChunkSeriesIterator(chkMetas, nil, 2, 8) |
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.
instead of this can you use the createBlock
to create an actual block than just open a db with this block and run the querier and seek against that.
I prefer to use only exported API methods to be as close as possible the expected behaviour for anyone using the library.
the same for TestChunkSeriesIterator_SeekWithMinTime
I know many tests use internal implementation, but I am trying to change that slowly to make these easier to maintain and understand so for any new tests I want to use only exported methods to ensure the correct behaviour.
@@ -64,6 +65,7 @@ type Appender interface { | |||
|
|||
// Iterator is a simple iterator that can only get the next value. | |||
type Iterator interface { | |||
// At returns (math.MinInt64, 0.0) before the iterator has advanced. |
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.
should this also be mentioned for the SeriesIterator interface
?
also we need a test to ensure that calling At before Next or Seek returns math.MinInt64
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.
should this also be mentioned for the
SeriesIterator interface
?
Done.
also we need a test to ensure that calling At before Next or Seek returns
math.MinInt64
Added checks (not separate tests) for all Iterator
and SeriesIterator
implementations. Let me know if you prefer separate tests or e.g. extracting the check into expandSeriesIterator()
instead.
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.
adding this to #665
@@ -756,7 +795,7 @@ func TestSeriesIterator(t *testing.T) { | |||
b: []tsdbutil.Sample{}, | |||
c: []tsdbutil.Sample{}, | |||
|
|||
seek: 0, | |||
seek: 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.
do you remember why you needed to change that?
the test passes even with seek: 0
t0, _ := it.cur.At() | ||
if t0 >= t { | ||
return true | ||
// Don't reset the iterator unless we've moved on to a different chunk. |
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.
// Don't reset the iterator unless we've moved on to a different chunk. | |
// Reset the iterator when moved to a different chunk. |
I find this a tiny more clear.
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.
Done.
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.
adjuste #665 as well, although code looks different (less indents).
1: Intervals{{1, 3}}, | ||
2: Intervals{{1, 3}, {6, 10}}, | ||
1: Intervals{{1, 2}}, | ||
2: Intervals{{2, 3}, {6, 10}}, |
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.
Why is this change required?
ping @free |
@krasi-georgiev, as things stand right now, it looks like I have about one more week of heads-down effort on my work project. But I will get back to this as soon as I'm done with 14 hour days. Apologies. |
@free no problem , thanks for letting me know. |
@free is this ready for another review? Not sure if the comments I left are still relevant after the latest changes. |
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.
@free is this ready for another review? Not sure if the comments I left are still relevant after the latest changes.
Nope, not yet, I've only addressed some of the comments. And, as it turns out, I haven't pushed my changes yet.
But since this will submit all my comments (without the underlying code changes, which are on my home computer) I might as well ask what's your preference for "a test to ensure that calling At before Next or Seek returns math.MinInt64
": currently I've added explicit checks wherever an Iterator
or SeriesIterator
is created, just before it is unrolled by expandSeriesIterator()
. Would you prefer for the test to be within expandSeriesIterator()
(and thus for a *testing.T
parameter to be added to it)? Or just for single, separate tests for every iterator implementation?
@@ -64,6 +65,7 @@ type Appender interface { | |||
|
|||
// Iterator is a simple iterator that can only get the next value. | |||
type Iterator interface { | |||
// At returns (math.MinInt64, 0.0) before the iterator has advanced. |
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.
should this also be mentioned for the
SeriesIterator interface
?
Done.
also we need a test to ensure that calling At before Next or Seek returns
math.MinInt64
Added checks (not separate tests) for all Iterator
and SeriesIterator
implementations. Let me know if you prefer separate tests or e.g. extracting the check into expandSeriesIterator()
instead.
t0, _ := it.cur.At() | ||
if t0 >= t { | ||
return true | ||
// Don't reset the iterator unless we've moved on to a different chunk. |
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.
Done.
@@ -658,7 +658,7 @@ func (s *chunkSeries) Iterator() SeriesIterator { | |||
type SeriesIterator interface { | |||
// Seek advances the iterator forward to the given timestamp. | |||
// If there's no value exactly at t, it advances to the first value | |||
// after t. | |||
// after t. Has no effect if already past t. |
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.
Done.
}, | ||
chunks: [][]sample{ | ||
{ | ||
{1, 2}, {10, 11}, |
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.
Done.
ping me when ready for a review and I spend some time for a proper review. |
Ok, I added all the improvements from here to #665 so closing this. Also we are merging prometheus/prometheus#5805 so I need to redo my PR anyway as well on Prometheus later on. |
Fix issues (1), (2) and (3) described in #328
closes #328
Signed-off-by: Alin Sinpalean [email protected]