This is an automated email from the ASF dual-hosted git repository.
gian pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/druid.git
The following commit(s) were added to refs/heads/master by this push:
new 8fbf92e047 SqlSegmentsMetadataQuery: Fix OVERLAPS for wide target
segments. (#12600)
8fbf92e047 is described below
commit 8fbf92e047f792ff1c69bf67d14784ac55eee88f
Author: Gian Merlino <[email protected]>
AuthorDate: Tue Jun 7 11:33:46 2022 -0700
SqlSegmentsMetadataQuery: Fix OVERLAPS for wide target segments. (#12600)
* SqlSegmentsMetadataQuery: Fix OVERLAPS for wide target segments.
Segments with endpoints prior to year 0 or after year 9999 may overlap
the search intervals but not match the generated SQL conditions. So, we
need to add an additional OR condition to catch these.
I checked a real, live MySQL metadata store to confirm that the query
still uses metadata store indexes. It does.
* Add comments.
---
.../druid/metadata/SqlSegmentsMetadataQuery.java | 35 +++++--
.../IndexerSQLMetadataStorageCoordinatorTest.java | 104 +++++++++++++++++++++
2 files changed, 131 insertions(+), 8 deletions(-)
diff --git
a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
index 4ecf7bebe6..737b5c6d36 100644
---
a/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
+++
b/server/src/main/java/org/apache/druid/metadata/SqlSegmentsMetadataQuery.java
@@ -233,12 +233,25 @@ public class SqlSegmentsMetadataQuery
)
);
- if (i == intervals.size() - 1) {
- sb.append(")");
- } else {
+ if (i != intervals.size() - 1) {
sb.append(" OR ");
}
}
+
+ if (matchMode == IntervalMode.OVERLAPS) {
+ // Segments with both endpoints outside 0000/10000 may overlap the
search intervals but not match the
+ // generated SQL conditions. We need to add one more OR condition to
catch all of these.
+ sb.append(
+ StringUtils.format(
+ " OR start < %2$s OR %1$send%1$s >= %3$s",
+ connector.getQuoteString(),
+ ":minmatch",
+ ":maxmatch"
+ )
+ );
+ }
+
+ sb.append(")");
}
final Query<Map<String, Object>> sql = handle
@@ -247,12 +260,17 @@ public class SqlSegmentsMetadataQuery
.bind("used", used)
.bind("dataSource", dataSource);
- if (compareAsString) {
+ if (compareAsString && !intervals.isEmpty()) {
final Iterator<Interval> iterator = intervals.iterator();
for (int i = 0; iterator.hasNext(); i++) {
- Interval interval = iterator.next();
- sql.bind(StringUtils.format("start%d", i),
interval.getStart().toString())
- .bind(StringUtils.format("end%d", i), interval.getEnd().toString());
+ final Interval interval = iterator.next();
+ sql.bind(StringUtils.format("start%d", i),
interval.getStart().toString());
+ sql.bind(StringUtils.format("end%d", i), interval.getEnd().toString());
+ }
+
+ if (matchMode == IntervalMode.OVERLAPS) {
+ sql.bind("minmatch", "0000-"); // '-' is lexicographically lower than
'0' so this catches negative-year starts
+ sql.bind("maxmatch", "10000-"); // Catches end points at 10000 or after
}
}
@@ -269,7 +287,8 @@ public class SqlSegmentsMetadataQuery
} else {
// Must re-check that the interval matches, even if comparing
as string, because the *segment interval*
// might not be string-comparable. (Consider a query interval
like "2000-01-01/3000-01-01" and a
- // segment interval like "20010/20011".)
+ // segment interval like "20010/20011". Consider also a
segment interval with endpoints prior to
+ // year 0000 or after year 9999.)
for (Interval interval : intervals) {
if (matchMode.apply(interval, dataSegment.getInterval())) {
return true;
diff --git
a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
index fca3366b88..e42478b933 100644
---
a/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
+++
b/server/src/test/java/org/apache/druid/metadata/IndexerSQLMetadataStorageCoordinatorTest.java
@@ -254,6 +254,42 @@ public class IndexerSQLMetadataStorageCoordinatorTest
100
);
+ private final DataSegment eternityRangeSegment1 = new DataSegment(
+ "eternity1",
+ Intervals.ETERNITY,
+ "zversion",
+ ImmutableMap.of(),
+ ImmutableList.of("dim1"),
+ ImmutableList.of("m1"),
+ new NumberedShardSpec(0, 1),
+ 9,
+ 100
+ );
+
+ private final DataSegment halfEternityRangeSegment1 = new DataSegment(
+ "halfEternity",
+ new Interval(DateTimes.MIN, DateTimes.of("1970")),
+ "zversion",
+ ImmutableMap.of(),
+ ImmutableList.of("dim1"),
+ ImmutableList.of("m1"),
+ new NumberedShardSpec(0, 1),
+ 9,
+ 100
+ );
+
+ private final DataSegment halfEternityRangeSegment2 = new DataSegment(
+ "halfEternity",
+ new Interval(DateTimes.of("1970"), DateTimes.MAX),
+ "zversion",
+ ImmutableMap.of(),
+ ImmutableList.of("dim1"),
+ ImmutableList.of("m1"),
+ new NumberedShardSpec(0, 1),
+ 9,
+ 100
+ );
+
private final Set<DataSegment> SEGMENTS = ImmutableSet.of(defaultSegment,
defaultSegment2);
private final AtomicLong metadataUpdateCounter = new AtomicLong();
private final AtomicLong segmentTableDropUpdateCounter = new AtomicLong();
@@ -1133,6 +1169,74 @@ public class IndexerSQLMetadataStorageCoordinatorTest
);
}
+ @Test
+ public void testUsedEternitySegmentEternityFilter() throws IOException
+ {
+
coordinator.announceHistoricalSegments(ImmutableSet.of(eternityRangeSegment1));
+
+ Assert.assertEquals(
+ ImmutableSet.of(eternityRangeSegment1),
+ ImmutableSet.copyOf(
+ coordinator.retrieveUsedSegmentsForIntervals(
+ eternityRangeSegment1.getDataSource(),
+ Intervals.ONLY_ETERNITY,
+ Segments.ONLY_VISIBLE
+ )
+ )
+ );
+ }
+
+ @Test
+ public void testUsedEternitySegment2000Filter() throws IOException
+ {
+
coordinator.announceHistoricalSegments(ImmutableSet.of(eternityRangeSegment1));
+
+ Assert.assertEquals(
+ ImmutableSet.of(eternityRangeSegment1),
+ ImmutableSet.copyOf(
+ coordinator.retrieveUsedSegmentsForIntervals(
+ eternityRangeSegment1.getDataSource(),
+ Collections.singletonList(Intervals.of("2000/2030")),
+ Segments.ONLY_VISIBLE
+ )
+ )
+ );
+ }
+
+ @Test
+ public void testUsedHalfEternitySegmentEternityFilter() throws IOException
+ {
+
coordinator.announceHistoricalSegments(ImmutableSet.of(halfEternityRangeSegment1,
halfEternityRangeSegment2));
+
+ Assert.assertEquals(
+ ImmutableSet.of(halfEternityRangeSegment1, halfEternityRangeSegment2),
+ ImmutableSet.copyOf(
+ coordinator.retrieveUsedSegmentsForIntervals(
+ halfEternityRangeSegment1.getDataSource(),
+ Intervals.ONLY_ETERNITY,
+ Segments.ONLY_VISIBLE
+ )
+ )
+ );
+ }
+
+ @Test
+ public void testUsedHalfEternitySegment2000Filter() throws IOException
+ {
+
coordinator.announceHistoricalSegments(ImmutableSet.of(halfEternityRangeSegment1,
halfEternityRangeSegment2));
+
+ Assert.assertEquals(
+ ImmutableSet.of(halfEternityRangeSegment2),
+ ImmutableSet.copyOf(
+ coordinator.retrieveUsedSegmentsForIntervals(
+ halfEternityRangeSegment1.getDataSource(),
+ Collections.singletonList(Intervals.of("2000/2030")),
+ Segments.ONLY_VISIBLE
+ )
+ )
+ );
+ }
+
@Test
public void testUsedHugeTimeRangeEternityFilter() throws IOException
{
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]