-
Notifications
You must be signed in to change notification settings - Fork 3.7k
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
Generate tombstones when running MSQ's replace #13706
Conversation
Tried it out with some of the overlapping segment cases. Test data consists of a datasource partitioned by month (2022-02-01/2022-03-01), 28 rows, containing one row of data for each day.
Earlier this used to throw the
Earlier this used to throw the |
Segments.ONLY_VISIBLE | ||
) | ||
) | ||
if (!intervalsToDrop.isEmpty()) { |
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 was trying to compare this code to the one present in the native ingestion - I couldn't understand the reason for not using TombstoneHelper
class to compute the tombstone intervals and the segments.
Is there a specific reason that both the code paths can be common?
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.
There are some minute differences between how the TombstoneHelper expects the arguments v/s as to how the MSQ is generating the segments:
The TombstoneHelper is using the DataSchema and its granularitySpec to compute the empty segments, v/s here we have the empty intervals for which we know that the segments corresponding to it should be empty, due to which I wasn't able to reconcile the code paths cleanly. One way was to create a dummy data schema corresponding to the empty intervals.
Also, the pushedSegments
argument in the helper was of no use since we know the empty intervals in replace, therefore we would also need to dummy that to something which would never overlap. Due to these, I decided to drop the usage of TombstoneHelper
...vice/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/TombstoneHelper.java
Fixed
Show fixed
Hide fixed
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.
Thanks for moving the changes to TombstoneHelper
. Left some comments
extensions-core/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
Show resolved
Hide resolved
...vice/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/TombstoneHelper.java
Outdated
Show resolved
Hide resolved
...vice/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/TombstoneHelper.java
Outdated
Show resolved
Hide resolved
extensions-core/multi-stage-query/src/test/java/org/apache/druid/msq/test/MSQTestBase.java
Outdated
Show resolved
Hide resolved
@@ -114,30 +111,6 @@ public void testInsertCannotOrderByDescendingFault() | |||
.verifyResults(); | |||
} | |||
|
|||
@Test | |||
public void testInsertCannotReplaceExistingSegmentFault() | |||
{ |
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 should have a test case which tests tombstone segments. This would give us more confidence in the PR.
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 moved the same test case to the replace tests, and ensured that it passes. Changed the granularity of the query a bit so as to not blow up the tombstone segments that are generated.
"test", | ||
0 | ||
))) | ||
.setExpectedTombstoneIntervals(ImmutableSet.of(Intervals.of("2001-01-01/2001-02-01"))) |
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.
How is this tombstone since we are generating data for this interval?
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.
Updated the test cases, and moved this line along with the destination segments.
@@ -1127,6 +1136,35 @@ public void verifyResults() | |||
Assert.assertTrue(segmentIdVsOutputRowsMap.get(diskSegment).contains(Arrays.asList(row))); | |||
} | |||
} | |||
if (!testTaskActionClient.getPublishedSegments().isEmpty()) { | |||
Set<SegmentId> expectedPublishedSegmentIds = segmentManager.getAllDataSegments() |
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't we substract segments which are present in segmentIdVsOutputRowMap.keys() when we are asserting against tombstone segments ?
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.
Thanks for this, the previous logic was flawed where a duplicate segment id can be present in the tombstone interval and the test case would still pass. This is a better way of testing out the tombstone segments, updated it.
|
||
public MSQTestTaskActionClient(ObjectMapper mapper) | ||
public MSQTestTaskActionClient( |
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 like we have state now in this client. Might want to mention that somewhere. Does it work with the calciteTests for MSQ ?
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.
Yes, CalciteTests for MSQ only work on testing the SELECT engine of MSQ (since the original Calcite tests do not have any analog for ingestion). Changing this doesn't affect the test cases for MSQ.
for (Interval tombstoneInterval : tombstoneIntervals) { | ||
String version = null; | ||
for (final TaskLock lock : locks) { | ||
if (lock.getInterval().contains(tombstoneInterval)) { |
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.
Shouldn't we do a data source filter here ?
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.
Since we only fetch the locks corresponding to the ones acquired by the task, we should be good to go without filtering the locks on the data source.
I checked the usage of this in ControllerImpl and rest of the places in the ingestion code, and we aren't filtering on the data source in other places as well.
extensions-core/multi-stage-query/src/main/java/org/apache/druid/msq/exec/ControllerImpl.java
Outdated
Show resolved
Hide resolved
extensions-core/multi-stage-query/src/test/java/org/apache/druid/msq/exec/MSQReplaceTest.java
Outdated
Show resolved
Hide resolved
return (RetType) SegmentPublishResult.ok(segments); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
public Set<DataSegment> getPublishedSegments() |
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.
can we maintain this state in MSQTestBase
instead? The action client can update the state on test base state. Also can the MSQTestSegmentManager
be used to fetch the segments?
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.
MSQTestSegmentManager
cannot be used because we are publishing the segments from the Controller where we also compute and publish the tombstone segments, but we are generating the segments in the SegmentGeneratorFrameProcessor
that takes the MSQTestSegmentManager
. Since tombstones are not generated (they don't contain any data), we cannot fetch the segments (as there are none). We have to get it from the published segments.
Regarding maintaining the state in the MSQTestBase, I thought it was cleaner that we do it directly because the CalciteSelectQueryTests also use this class for MSQ, which won't utilize the published segments (so it would need to provide an extra dummy argument)
...vice/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/TombstoneHelper.java
Show resolved
Hide resolved
...vice/src/main/java/org/apache/druid/indexing/common/task/batch/parallel/TombstoneHelper.java
Outdated
Show resolved
Hide resolved
continue; | ||
} | ||
|
||
// Overlap might not be aligned with the granularity if the used interval is not aligned with the granularity |
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 we add the whole intervalToDrop
in the set of intervals? I didn't quite understand the part with overlap interval + iterator over it - an example would be great if possible.
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.
Updated with the comment in the code
} | ||
|
||
@Test | ||
public void tombstonesCreatedForReplaceWhenUsedIntervalsDonotAlign() throws Exception |
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 these tests should either be about tombstoneIntervals
or if they are about tombstoneSegments
, then they should check the segments as well
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.
Renamed the tests to specify that we are testing intervals only (since that's the valuable and variable part in the generated segments).
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.
LGTM!!
Will merge POST CICD is green.
Thanks @LakshSingla for this contribution.
One thing to mention in the release notes is that we can only allow downgrades till tombstones were introduced post this change.
Description
When running
REPLACE
queries, the segments which contain no data are dropped (marked as unused). This PR aims to generate tombstones in place of segments which contain no data to mark their deletion, as is the behavior with the native ingestion.This will cause
InsertCannotReplaceExistingSegmentFault
to be removed since it was generated if the interval to be marked unused didn't fully overlap one of the existing segments to replace.Release note
REPLACE
in MSQ now generates tombstones instead of marking segment as unused.Key changed/added classes in this PR
ControllerImpl
TombstoneHelper
This PR has: