Fix issue where queries can fail or omit OOO samples if OOO head compaction occurs between creating a querier and reading chunks - #13115
Conversation
adca2de to
cc23822
Compare
06fa048 to
e3d12ed
Compare
There was a problem hiding this comment.
Thank you for your contribution @charleskorn
I clarified in a comment above that ULIDs for the head blocks should now be more readable, hopefully providing a better UX to the administrator when this kind of bugs come up. I bet it was hard to find this...
I've given a first pass of feedback with some small nits, I'm fairly confident that this will work since your tests are comprehensive and nothing else broke.
Unfortunately our prombench does not test the OOO head so we wont be able to catch any possible issues on CI, we won't know if something breaks until this runs in prod for some time, this scares me a bit. I'll make sure whomever is crafting the next release is aware about this PR in case issues comes up we know what to revert.
I've asked @fionaliao to also review the changes since she is working on OOO compaction and her feedback might be useful.
While you rebase + address my comments I'll read a bit more on isolation just to make sure I did not miss anything.
|
/prombench main |
|
@charleskorn there is a notable difference in memory usage in the benchmark But I have seen the same behavior in a benchmark I'm running for another PR. Could you please rebase your branch with the latest changes in main so I can run a benchmark that is more up to date? We need to check for regressions because this PR is changing a few things. |
|
/prombench cancel |
|
Benchmark cancel is in progress. |
Signed-off-by: Charles Korn <charles.korn@grafana.com>
Signed-off-by: Charles Korn <charles.korn@grafana.com>
Signed-off-by: Charles Korn <charles.korn@grafana.com>
…ils. Signed-off-by: Charles Korn <charles.korn@grafana.com>
Signed-off-by: Charles Korn <charles.korn@grafana.com>
…ange()` to make it clearer Signed-off-by: Charles Korn <charles.korn@grafana.com>
…collection forever. Signed-off-by: Charles Korn <charles.korn@grafana.com>
Co-authored-by: Jesus Vazquez <jesusvazquez@users.noreply.github.com> Signed-off-by: Charles Korn <charleskorn@users.noreply.github.com>
Signed-off-by: Charles Korn <charles.korn@grafana.com>
4a14a44 to
ced4401
Compare
Done, just rebased it now. |
…an OOO querier fails Signed-off-by: Charles Korn <charles.korn@grafana.com>
|
Looks like the CI failure is due to linting issues affecting unrelated files. #13178 will fix the linting issues. |
|
/prombench main |
|
/prombench cancel |
|
Benchmark cancel is in progress. |
|
Benchmarks results looking good 🎉 |
jesusvazquez
left a comment
There was a problem hiding this comment.
LGTM, thanks @charleskorn for addressing this issue and improving the code in between.
| // If NewBlockQuerier() failed, make sure to clean up the pending read created by NewOOORangeHead. | ||
| rh.isoState.Close() |
There was a problem hiding this comment.
Do we need a similar Close in blockChunkQuerierForRange? (Line 2030 in this PR)

This PR fixes two related issues:
cannot populate chunk XXX from block 2ZBXFNYVVFDXFPGSB1CHFNYQTZ: not founderror if OOO head compaction occurs while the query is being evaluatedcannot populate chunk XXX from block 2ZBXFNYVVFDXFPGSB1CHFNYQTZ: not founderrorThis issue occurs if events happen in this sequence:
DB.ChunksQuerier(), which returns a querierqq.Select(), which returns a series setssitfor a series with samples in the OOO head block (ie. callss.At().Iterator(...))itto read the chunks for the seriesEventually,
it.Next()will returnfalse, andit.Err()will report acannot populate chunk XXX from block 2ZBXFNYVVFDXFPGSB1CHFNYQTZ: not founderror.2ZBXFNYVVFDXFPGSB1CHFNYQTZis the ULID of the OOO head block, defined here.This happens because the garbage collection that occurs during OOO head compaction does not wait for pending reads to complete, and so the iterator created in step 3 has a reference to a chunk that is removed during compaction and can't be read in step 5.
Omitting data
This issue occurs if events happen in this sequence:
DB.ChunksQuerier(), which returns a querierqq.Select(), which returns a series setssitfor a series with samples that were in the OOO head before compaction (ie. callss.At().Iterator(...))itto read the chunks for the series, none of which will include the samples that were in the OOO head before compactionIt also happens if steps 2 and 3 happen in the reverse order (ie. head compaction happens before the call to
q.Select()).In this case, the data that was in the OOO head before compaction is omitted, and no error is returned, so query results are incorrect.
This happens because the list of blocks on disk is captured in step 1, but by the time we get to step 4, which evaluates the list of chunks for the series, the chunk that was in the OOO head has been written to disk and garbage collected from the head, so it isn't returned from either source (disk or head).
Root cause
I believe the reason for this is that OOO compaction doesn't check for pending reads that overlap with the chunks to be garbage collected, unlike what is done for in-order head compaction here.
Under normal circumstances (ie. when
DB.Compact()is used), OOO head compaction only occurs after in-order head compaction. Because of this, most of the time, this issue won't happen in practice because the time range of a query that includes OOO samples will also include time range of the in-order head and therefore be protected by the pending read check done before compacting the in-order head, or be protected by another query running at the same time that includes the in-order head. However, queries that are only for the time range of blocks on disk and OOO head samples (ie. do not overlap with the time range of the in-order head) would be susceptible to this issue.I'd suggest reviewing each commit individually, the key commits are:
I'd suggest reviewing this carefully: while this fix makes the test pass, I suspect there are some edge cases I haven't considered.