Skip to content
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

Merged
merged 16 commits into from
Mar 1, 2023

Conversation

LakshSingla
Copy link
Contributor

@LakshSingla LakshSingla commented Jan 24, 2023

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:

  • been self-reviewed.
  • added documentation for new or modified features or behaviors.
  • a release note entry in the PR description.
  • added Javadocs for most classes and all non-trivial methods. Linked related entities via Javadoc links.
  • added or updated version, license, or notice information in licenses.yaml
  • added comments explaining the "why" and the intent of the code wherever would not be obvious for an unfamiliar reader.
  • added unit tests or modified existing tests to cover new code paths, ensuring the threshold for code coverage is met.
  • added integration tests.
  • been tested in a test Druid cluster.

@clintropolis clintropolis added Area - Ingestion Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262 and removed Area - Ingestion labels Jan 26, 2023
@LakshSingla
Copy link
Contributor Author

LakshSingla commented Jan 31, 2023

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.
Following is the query used to ingest the data

SELECT TIME_PARSE("rtttime") AS __time,* FROM TABLE(
  EXTERN(
    '{"type":"local","files":["/Users/lakshsingla/month_data.json"]}',
    '{"type": "json"}',
    '[{"name":"rtttime","type":"string"}, {"name":"sr_serial_number","type":"string"} , {"name":"drive_duration","type":"long"} ]'
  )
)
PARTITIONED BY MONTH
  1. Ran a replace which partially overlaps the segment and lies completely inside
OVERWRITE WHERE __time >= TIMESTAMP '2022-02-05' AND __time < TIMESTAMP '2022-02-25'
SELECT * FROM "test_table_4"
WHERE __time >= TIMESTAMP '2022-02-05' AND __time < TIMESTAMP '2022-02-07'
PARTITIONED BY DAY

Earlier this used to throw the InsertCannotReplaceExistingSegment error, however now it works as expected. The tombstones for the 2022-02-07-2022-02-25 are generated.

  1. Ran a replace which partially overlaps the segment and lies outside it as well
OVERWRITE WHERE __time >= TIMESTAMP '2022-01-05' AND __time < TIMESTAMP '2022-02-25'
SELECT * FROM "test_table_4"
WHERE __time >= TIMESTAMP '2022-02-05' AND __time < TIMESTAMP '2022-02-07'
PARTITIONED BY DAY

Earlier this used to throw the InsertCannotReplaceExistingSegment error, however now it works as expected. The tombstones for the 2022-02-01- 2022-02-04 2022-02-07-2022-02-25 are generated.

Segments.ONLY_VISIBLE
)
)
if (!intervalsToDrop.isEmpty()) {
Copy link
Member

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?

Copy link
Contributor Author

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

Copy link
Member

@rohangarg rohangarg left a 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

@@ -114,30 +111,6 @@ public void testInsertCannotOrderByDescendingFault()
.verifyResults();
}

@Test
public void testInsertCannotReplaceExistingSegmentFault()
{
Copy link
Contributor

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.

Copy link
Contributor Author

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")))
Copy link
Contributor

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?

Copy link
Contributor Author

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()
Copy link
Contributor

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 ?

Copy link
Contributor Author

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(
Copy link
Contributor

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 ?

Copy link
Contributor Author

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)) {
Copy link
Contributor

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 ?

Copy link
Contributor Author

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.

return (RetType) SegmentPublishResult.ok(segments);
} else {
return null;
}
}

public Set<DataSegment> getPublishedSegments()
Copy link
Member

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?

Copy link
Contributor Author

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)

continue;
}

// Overlap might not be aligned with the granularity if the used interval is not aligned with the granularity
Copy link
Member

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.

Copy link
Contributor Author

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
Copy link
Member

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

Copy link
Contributor Author

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).

Copy link
Contributor

@cryptoe cryptoe left a 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.

@cryptoe cryptoe merged commit ca68fd9 into apache:master Mar 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area - Documentation Area - MSQ For multi stage queries - https://github.com/apache/druid/issues/12262
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants