From 452fdd0d8cb14b30d7fd1b1bb61d582c8f35a1ed Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Tue, 16 Feb 2021 17:12:15 +0530 Subject: [PATCH 1/9] Avoid expensive findEntry call in segment metadata query --- .../apache/druid/timeline/TimelineLookup.java | 4 +++ .../timeline/VersionedIntervalTimeline.java | 28 +++++++++++++++++++ .../druid/client/CachingClusteredClient.java | 14 +++++----- 3 files changed, 39 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java b/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java index fb9e577b83cf..319a995c17a6 100644 --- a/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java +++ b/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java @@ -19,6 +19,7 @@ package org.apache.druid.timeline; +import org.apache.druid.timeline.partition.PartitionChunk; import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; @@ -52,4 +53,7 @@ public interface TimelineLookup> lookupWithIncompletePartitions(Interval interval); @Nullable PartitionHolder findEntry(Interval interval, VersionType version); + + @Nullable + PartitionChunk findChunk(Interval interval, VersionType version, int partitionNum); } diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index 376a97ffee7a..c49861353735 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -304,6 +304,34 @@ public PartitionHolder findEntry(Interval interval, VersionType vers } } + /** + * It may be cheaper to use this method instead of {@link #findEntry(Interval, Object)} since creating an immutable + * view of {@link PartitionHolder} is an expensive operation. This methods avoids that expensive operation by + * returning {@link PartitionChunk} directly. The effect is particularly visible when number of segments being + * operated on is very large. + */ + @Override + @Nullable + public PartitionChunk findChunk(Interval interval, VersionType version, int partitionNum) + { + lock.readLock().lock(); + try { + for (Entry> entry : allTimelineEntries.entrySet()) { + if (entry.getKey().equals(interval) || entry.getKey().contains(interval)) { + TimelineEntry foundEntry = entry.getValue().get(version); + if (foundEntry != null) { + return foundEntry.getPartitionHolder().getChunk(partitionNum); + } + } + } + + return null; + } + finally { + lock.readLock().unlock(); + } + } + /** * Does a lookup for the objects representing the given time interval. Will *only* return * PartitionHolders that are {@linkplain PartitionHolder#isComplete() complete}. diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index d1adc82cebb9..c8fb1feae5ea 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -85,7 +85,6 @@ import org.apache.druid.timeline.TimelineObjectHolder; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; import javax.annotation.Nullable; @@ -233,12 +232,13 @@ public Sequence run(final QueryPlus queryPlus, final ResponseContext respo final VersionedIntervalTimeline timeline2 = new VersionedIntervalTimeline<>(Ordering.natural()); for (SegmentDescriptor spec : specs) { - final PartitionHolder entry = timeline.findEntry(spec.getInterval(), spec.getVersion()); - if (entry != null) { - final PartitionChunk chunk = entry.getChunk(spec.getPartitionNumber()); - if (chunk != null) { - timeline2.add(spec.getInterval(), spec.getVersion(), chunk); - } + final PartitionChunk chunk = timeline.findChunk( + spec.getInterval(), + spec.getVersion(), + spec.getPartitionNumber() + ); + if (chunk != null) { + timeline2.add(spec.getInterval(), spec.getVersion(), chunk); } } return timeline2; From 4e6d14358f2fbb85605fc1e6e6f0a614bcc75c9b Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Tue, 16 Feb 2021 17:44:13 +0530 Subject: [PATCH 2/9] other places --- .../realtime/appenderator/SinkQuerySegmentWalker.java | 10 +++------- .../java/org/apache/druid/server/SegmentManager.java | 8 ++++---- .../druid/server/coordination/ServerManager.java | 11 +++-------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java index 2c3d38f0a36b..31d82f70f2c7 100644 --- a/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java +++ b/server/src/main/java/org/apache/druid/segment/realtime/appenderator/SinkQuerySegmentWalker.java @@ -66,7 +66,6 @@ import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; import java.io.Closeable; @@ -185,15 +184,12 @@ public QueryRunner getQueryRunnerForSegments(final Query query, final Iterable> perSegmentRunners = Iterables.transform( specs, descriptor -> { - final PartitionHolder holder = sinkTimeline.findEntry( + final PartitionChunk chunk = sinkTimeline.findChunk( descriptor.getInterval(), - descriptor.getVersion() + descriptor.getVersion(), + descriptor.getPartitionNumber() ); - if (holder == null) { - return new ReportTimelineMissingSegmentQueryRunner<>(descriptor); - } - final PartitionChunk chunk = holder.getChunk(descriptor.getPartitionNumber()); if (chunk == null) { return new ReportTimelineMissingSegmentQueryRunner<>(descriptor); } diff --git a/server/src/main/java/org/apache/druid/server/SegmentManager.java b/server/src/main/java/org/apache/druid/server/SegmentManager.java index c3636d3a9780..93b16a317b25 100644 --- a/server/src/main/java/org/apache/druid/server/SegmentManager.java +++ b/server/src/main/java/org/apache/druid/server/SegmentManager.java @@ -40,7 +40,6 @@ import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.apache.druid.timeline.partition.ShardSpec; import org.apache.druid.utils.CollectionUtils; @@ -234,12 +233,13 @@ public boolean loadSegment(final DataSegment segment, boolean lazy, SegmentLazyL final DataSourceState dataSourceState = v == null ? new DataSourceState() : v; final VersionedIntervalTimeline loadedIntervals = dataSourceState.getTimeline(); - final PartitionHolder entry = loadedIntervals.findEntry( + final PartitionChunk entry = loadedIntervals.findChunk( segment.getInterval(), - segment.getVersion() + segment.getVersion(), + segment.getShardSpec().getPartitionNum() ); - if ((entry != null) && (entry.getChunk(segment.getShardSpec().getPartitionNum()) != null)) { + if (entry != null) { log.warn("Told to load an adapter for segment[%s] that already exists", segment.getId()); resultSupplier.set(false); } else { diff --git a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java index da4fc59b3928..da20739bc2c6 100644 --- a/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java +++ b/server/src/main/java/org/apache/druid/server/coordination/ServerManager.java @@ -67,7 +67,6 @@ import org.apache.druid.timeline.SegmentId; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; import java.util.Collections; @@ -249,16 +248,12 @@ QueryRunner buildQueryRunnerForSegment( Optional cacheKeyPrefix ) { - final PartitionHolder entry = timeline.findEntry( + final PartitionChunk chunk = timeline.findChunk( descriptor.getInterval(), - descriptor.getVersion() + descriptor.getVersion(), + descriptor.getPartitionNumber() ); - if (entry == null) { - return new ReportTimelineMissingSegmentQueryRunner<>(descriptor); - } - - final PartitionChunk chunk = entry.getChunk(descriptor.getPartitionNumber()); if (chunk == null) { return new ReportTimelineMissingSegmentQueryRunner<>(descriptor); } From aef9f17905f83098dbfb49f46dcfb6153a55f82a Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Thu, 18 Feb 2021 15:04:10 +0530 Subject: [PATCH 3/9] Remove findEntry --- .../apache/druid/timeline/TimelineLookup.java | 6 +- .../timeline/VersionedIntervalTimeline.java | 28 ---------- ...ionedIntervalTimelineSpecificDataTest.java | 55 ++++++++++++++----- .../VersionedIntervalTimelineTest.java | 8 +-- .../VersionedIntervalTimelineTestBase.java | 10 ++++ .../druid/client/BrokerServerViewTest.java | 17 +++--- .../client/CoordinatorServerViewTest.java | 9 ++- .../server/TestClusterQuerySegmentWalker.java | 8 +-- 8 files changed, 76 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java b/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java index 319a995c17a6..4d85a1d040a6 100644 --- a/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java +++ b/core/src/main/java/org/apache/druid/timeline/TimelineLookup.java @@ -20,7 +20,6 @@ package org.apache.druid.timeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; import javax.annotation.Nullable; @@ -52,8 +51,9 @@ public interface TimelineLookup> lookupWithIncompletePartitions(Interval interval); - @Nullable PartitionHolder findEntry(Interval interval, VersionType version); - + /** + * Finds the {@link PartitionChunk} for the given time interval, version and chunk number. + */ @Nullable PartitionChunk findChunk(Interval interval, VersionType version, int partitionNum); } diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index c49861353735..0c6a9a5c8d0d 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -282,34 +282,6 @@ public PartitionChunk remove(Interval interval, VersionType version, } } - @Override - @Nullable - public PartitionHolder findEntry(Interval interval, VersionType version) - { - lock.readLock().lock(); - try { - for (Entry> entry : allTimelineEntries.entrySet()) { - if (entry.getKey().equals(interval) || entry.getKey().contains(interval)) { - TimelineEntry foundEntry = entry.getValue().get(version); - if (foundEntry != null) { - return foundEntry.getPartitionHolder().asImmutable(); - } - } - } - - return null; - } - finally { - lock.readLock().unlock(); - } - } - - /** - * It may be cheaper to use this method instead of {@link #findEntry(Interval, Object)} since creating an immutable - * view of {@link PartitionHolder} is an expensive operation. This methods avoids that expensive operation by - * returning {@link PartitionChunk} directly. The effect is particularly visible when number of segments being - * operated on is very large. - */ @Override @Nullable public PartitionChunk findChunk(Interval interval, VersionType version, int partitionNum) diff --git a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineSpecificDataTest.java b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineSpecificDataTest.java index 0bb2354177bf..a41eda2fc360 100644 --- a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineSpecificDataTest.java +++ b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineSpecificDataTest.java @@ -25,7 +25,6 @@ import org.apache.druid.java.util.common.Intervals; import org.apache.druid.timeline.partition.IntegerPartitionChunk; import org.apache.druid.timeline.partition.OvershadowableInteger; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.DateTime; import org.joda.time.Days; import org.joda.time.Hours; @@ -221,36 +220,64 @@ public void testRemove() } @Test - public void testFindEntry() + public void testFindChunk() { - Assert.assertEquals( - new PartitionHolder<>(makeSingle("1", 1)).asImmutable(), - timeline.findEntry(Intervals.of("2011-10-01/2011-10-02"), "1") + assertSingleElementChunks( + makeSingle("1", 1), + timeline.findChunk(Intervals.of("2011-10-01/2011-10-02"), "1", 0) ); - Assert.assertEquals( - new PartitionHolder<>(makeSingle("1", 1)).asImmutable(), - timeline.findEntry(Intervals.of("2011-10-01/2011-10-01T10"), "1") + assertSingleElementChunks( + makeSingle("1", 1), + timeline.findChunk(Intervals.of("2011-10-01/2011-10-01T10"), "1", 0) + ); + + assertSingleElementChunks( + makeSingle("1", 1), + timeline.findChunk(Intervals.of("2011-10-01T02/2011-10-02"), "1", 0) + ); + + assertSingleElementChunks( + makeSingle("1", 1), + timeline.findChunk(Intervals.of("2011-10-01T04/2011-10-01T17"), "1", 0) + ); + + IntegerPartitionChunk expected = IntegerPartitionChunk.make( + 10, + null, + 1, + new OvershadowableInteger( + "3", + 1, + 21 + ) ); + IntegerPartitionChunk actual = (IntegerPartitionChunk) timeline.findChunk( + Intervals.of("2011-10-02/2011-10-03"), + "3", + 1 + ); + Assert.assertEquals(expected, actual); + Assert.assertEquals(expected.getObject(), actual.getObject()); Assert.assertEquals( - new PartitionHolder<>(makeSingle("1", 1)).asImmutable(), - timeline.findEntry(Intervals.of("2011-10-01T02/2011-10-02"), "1") + null, + timeline.findChunk(Intervals.of("2011-10-01T04/2011-10-01T17"), "1", 1) ); Assert.assertEquals( - new PartitionHolder<>(makeSingle("1", 1)).asImmutable(), - timeline.findEntry(Intervals.of("2011-10-01T04/2011-10-01T17"), "1") + null, + timeline.findChunk(Intervals.of("2011-10-01T04/2011-10-01T17"), "2", 0) ); Assert.assertEquals( null, - timeline.findEntry(Intervals.of("2011-10-01T04/2011-10-01T17"), "2") + timeline.findChunk(Intervals.of("2011-10-01T04/2011-10-02T17"), "1", 0) ); Assert.assertEquals( null, - timeline.findEntry(Intervals.of("2011-10-01T04/2011-10-02T17"), "1") + timeline.findChunk(Intervals.of("2011-10-01T04/2011-10-02T17"), "1", 0) ); } diff --git a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTest.java b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTest.java index 57abe7a81086..de3c5eeccaf6 100644 --- a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTest.java +++ b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTest.java @@ -51,14 +51,14 @@ public void setUp() } @Test - public void testFindEntryWithOverlap() + public void testFindChunkWithOverlap() { add("2011-01-01/2011-01-10", "1", 1); add("2011-01-02/2011-01-05", "2", 1); - Assert.assertEquals( - new PartitionHolder<>(makeSingle("1", 1)).asImmutable(), - timeline.findEntry(Intervals.of("2011-01-02T02/2011-01-04"), "1") + assertSingleElementChunks( + makeSingle("1", 1), + timeline.findChunk(Intervals.of("2011-01-02T02/2011-01-04"), "1", 0) ); } diff --git a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTestBase.java b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTestBase.java index 3d8c5a349252..53ab4db3cc5e 100644 --- a/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTestBase.java +++ b/core/src/test/java/org/apache/druid/timeline/VersionedIntervalTimelineTestBase.java @@ -98,6 +98,16 @@ static void assertValues( Assert.assertEquals(expected, actualSet); } + static void assertSingleElementChunks( + PartitionChunk expected, + PartitionChunk actual + ) + { + SingleElementPartitionChunk expectedSingle = (SingleElementPartitionChunk) expected; + SingleElementPartitionChunk actualSingle = (SingleElementPartitionChunk) actual; + Assert.assertEquals(expectedSingle.getObject(), actualSingle.getObject()); + } + static VersionedIntervalTimeline makeStringIntegerTimeline() { return new VersionedIntervalTimeline<>(Ordering.natural()); diff --git a/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java b/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java index 7878e6afdc99..9558133f294f 100644 --- a/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java +++ b/server/src/test/java/org/apache/druid/client/BrokerServerViewTest.java @@ -110,22 +110,20 @@ public void testSingleServerAddedRemovedSegment() throws Exception setupZNodeForServer(druidServer, zkPathsConfig, jsonMapper); final DataSegment segment = dataSegmentWithIntervalAndVersion("2014-10-20T00:00:00Z/P1D", "v1"); + final int partition = segment.getShardSpec().getPartitionNum(); + final Interval intervals = Intervals.of("2014-10-20T00:00:00Z/P1D"); announceSegmentForServer(druidServer, segment, zkPathsConfig, jsonMapper); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentViewInitLatch)); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentAddedLatch)); - TimelineLookup timeline = brokerServerView.getTimeline( + TimelineLookup timeline = brokerServerView.getTimeline( DataSourceAnalysis.forDataSource(new TableDataSource("test_broker_server_view")) ).get(); - List serverLookupRes = (List) timeline.lookup( - Intervals.of( - "2014-10-20T00:00:00Z/P1D" - ) - ); + List> serverLookupRes = timeline.lookup(intervals); Assert.assertEquals(1, serverLookupRes.size()); TimelineObjectHolder actualTimelineObjectHolder = serverLookupRes.get(0); - Assert.assertEquals(Intervals.of("2014-10-20T00:00:00Z/P1D"), actualTimelineObjectHolder.getInterval()); + Assert.assertEquals(intervals, actualTimelineObjectHolder.getInterval()); Assert.assertEquals("v1", actualTimelineObjectHolder.getVersion()); PartitionHolder actualPartitionHolder = actualTimelineObjectHolder.getObject(); @@ -136,15 +134,16 @@ public void testSingleServerAddedRemovedSegment() throws Exception Assert.assertFalse(selector.isEmpty()); Assert.assertEquals(segment, selector.getSegment()); Assert.assertEquals(druidServer, selector.pick(null).getServer()); + Assert.assertNotNull(timeline.findChunk(intervals, "v1", partition)); unannounceSegmentForServer(druidServer, segment, zkPathsConfig); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentRemovedLatch)); Assert.assertEquals( 0, - ((List) timeline.lookup(Intervals.of("2014-10-20T00:00:00Z/P1D"))).size() + timeline.lookup(intervals).size() ); - Assert.assertNull(timeline.findEntry(Intervals.of("2014-10-20T00:00:00Z/P1D"), "v1")); + Assert.assertNull(timeline.findChunk(intervals, "v1", partition)); } @Test diff --git a/server/src/test/java/org/apache/druid/client/CoordinatorServerViewTest.java b/server/src/test/java/org/apache/druid/client/CoordinatorServerViewTest.java index f6e26989eadc..961303ca57b1 100644 --- a/server/src/test/java/org/apache/druid/client/CoordinatorServerViewTest.java +++ b/server/src/test/java/org/apache/druid/client/CoordinatorServerViewTest.java @@ -101,18 +101,20 @@ public void testSingleServerAddedRemovedSegment() throws Exception setupZNodeForServer(druidServer, zkPathsConfig, jsonMapper); final DataSegment segment = dataSegmentWithIntervalAndVersion("2014-10-20T00:00:00Z/P1D", "v1"); + final int partition = segment.getShardSpec().getPartitionNum(); + final Interval intervals = Intervals.of("2014-10-20T00:00:00Z/P1D"); announceSegmentForServer(druidServer, segment, zkPathsConfig, jsonMapper); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentViewInitLatch)); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentAddedLatch)); TimelineLookup timeline = overlordServerView.getTimeline(new TableDataSource("test_overlord_server_view")); List serverLookupRes = (List) timeline.lookup( - Intervals.of("2014-10-20T00:00:00Z/P1D") + intervals ); Assert.assertEquals(1, serverLookupRes.size()); TimelineObjectHolder actualTimelineObjectHolder = serverLookupRes.get(0); - Assert.assertEquals(Intervals.of("2014-10-20T00:00:00Z/P1D"), actualTimelineObjectHolder.getInterval()); + Assert.assertEquals(intervals, actualTimelineObjectHolder.getInterval()); Assert.assertEquals("v1", actualTimelineObjectHolder.getVersion()); PartitionHolder actualPartitionHolder = actualTimelineObjectHolder.getObject(); @@ -125,6 +127,7 @@ public void testSingleServerAddedRemovedSegment() throws Exception druidServer.getMetadata(), Iterables.getOnlyElement(segmentLoadInfo.toImmutableSegmentLoadInfo().getServers()) ); + Assert.assertNotNull(timeline.findChunk(intervals, "v1", partition)); unannounceSegmentForServer(druidServer, segment); Assert.assertTrue(timing.forWaiting().awaitLatch(segmentRemovedLatch)); @@ -133,7 +136,7 @@ public void testSingleServerAddedRemovedSegment() throws Exception 0, ((List) timeline.lookup(Intervals.of("2014-10-20T00:00:00Z/P1D"))).size() ); - Assert.assertNull(timeline.findEntry(Intervals.of("2014-10-20T00:00:00Z/P1D"), "v1")); + Assert.assertNull(timeline.findChunk(intervals, "v1", partition)); } @Test diff --git a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java index da6d89f75b05..3f79ce0a4dce 100644 --- a/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java +++ b/server/src/test/java/org/apache/druid/server/TestClusterQuerySegmentWalker.java @@ -52,7 +52,6 @@ import org.apache.druid.timeline.TimelineObjectHolder; import org.apache.druid.timeline.VersionedIntervalTimeline; import org.apache.druid.timeline.partition.PartitionChunk; -import org.apache.druid.timeline.partition.PartitionHolder; import org.joda.time.Interval; import javax.annotation.Nullable; @@ -259,11 +258,12 @@ private List getSegmentsForTable(final String dataSource, final final List retVal = new ArrayList<>(); for (SegmentDescriptor spec : specs) { - final PartitionHolder entry = timeline.findEntry( + final PartitionChunk entry = timeline.findChunk( spec.getInterval(), - spec.getVersion() + spec.getVersion(), + spec.getPartitionNumber() ); - retVal.add(new WindowedSegment(entry.getChunk(spec.getPartitionNumber()).getObject(), spec.getInterval())); + retVal.add(new WindowedSegment(entry.getObject(), spec.getInterval())); } return retVal; From 386fed6d91f3ce8721a3b9e054f5bffc93aa07a0 Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Fri, 19 Feb 2021 01:20:08 +0530 Subject: [PATCH 4/9] Fix add cost --- .../timeline/VersionedIntervalTimeline.java | 2 +- .../druid/client/CachingClusteredClient.java | 51 +++++++++++++------ 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index 0c6a9a5c8d0d..0e7bd110f438 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -186,7 +186,7 @@ public void add(final Interval interval, VersionType version, PartitionChunk interval, o -> version); } - private void addAll( + public void addAll( final Iterator> objects, final Function intervalFunction, final Function versionFunction diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index c8fb1feae5ea..2de4995ce878 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -26,6 +26,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; import com.google.common.collect.Maps; import com.google.common.collect.Ordering; import com.google.common.collect.RangeSet; @@ -97,6 +98,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.SortedMap; @@ -228,21 +230,7 @@ public Sequence run(final QueryPlus queryPlus, final ResponseContext respo return CachingClusteredClient.this.run( queryPlus, responseContext, - timeline -> { - final VersionedIntervalTimeline timeline2 = - new VersionedIntervalTimeline<>(Ordering.natural()); - for (SegmentDescriptor spec : specs) { - final PartitionChunk chunk = timeline.findChunk( - spec.getInterval(), - spec.getVersion(), - spec.getPartitionNumber() - ); - if (chunk != null) { - timeline2.add(spec.getInterval(), spec.getVersion(), chunk); - } - } - return timeline2; - }, + new TimelineConverter(specs), true ); } @@ -856,4 +844,37 @@ private byte[] computeQueryCacheKeyWithJoin() return strategy.computeCacheKey(query); } } + + private static class TimelineConverter implements UnaryOperator> + { + private final Iterable specs; + + TimelineConverter(final Iterable specs) + { + this.specs = specs; + } + + @Override + public TimelineLookup apply(TimelineLookup timeline) + { + final VersionedIntervalTimeline timeline2 = + new VersionedIntervalTimeline<>(Ordering.natural()); + Iterator> unfilteredIterator = + Iterators.transform(specs.iterator(), spec -> timeline.findChunk( + spec.getInterval(), + spec.getVersion(), + spec.getPartitionNumber() + )); + Iterator> iterator = Iterators.filter( + unfilteredIterator, + Objects::nonNull + ); + timeline2.addAll( + iterator, + input -> input.getSegment().getInterval(), + input -> input.getSegment().getVersion() + ); + return timeline2; + } + } } From b2efed250368629f56ccb6a3b59c802e7bed8b5f Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Fri, 19 Feb 2021 13:01:20 +0530 Subject: [PATCH 5/9] Refactor a bit --- .../timeline/VersionedIntervalTimeline.java | 59 +++++++++++++++---- .../druid/client/CachingClusteredClient.java | 35 +++++++---- 2 files changed, 70 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index 0e7bd110f438..f82bf2fbff16 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -20,7 +20,6 @@ package org.apache.druid.timeline; import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Function; import com.google.common.base.Preconditions; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterators; @@ -117,10 +116,14 @@ public static void addSegments( ) { timeline.addAll( - Iterators.transform(segments, segment -> segment.getShardSpec().createChunk(segment)), - DataSegment::getInterval, - DataSegment::getVersion - ); + Iterators.transform( + segments, + segment -> new PartitionChunkEntry<>( + segment.getInterval(), + segment.getVersion(), + segment.getShardSpec().createChunk(segment) + ) + )); } public Map> getAllTimelineEntries() @@ -183,13 +186,11 @@ public Set findNonOvershadowedObjectsInInterval(Interval interval, P public void add(final Interval interval, VersionType version, PartitionChunk object) { - addAll(Iterators.singletonIterator(object), o -> interval, o -> version); + addAll(Iterators.singletonIterator(new PartitionChunkEntry<>(interval, version, object))); } public void addAll( - final Iterator> objects, - final Function intervalFunction, - final Function versionFunction + final Iterator> objects ) { lock.writeLock().lock(); @@ -198,9 +199,10 @@ public void addAll( final IdentityHashMap allEntries = new IdentityHashMap<>(); while (objects.hasNext()) { - PartitionChunk object = objects.next(); - Interval interval = intervalFunction.apply(object.getObject()); - VersionType version = versionFunction.apply(object.getObject()); + PartitionChunkEntry chunkEntry = objects.next(); + PartitionChunk object = chunkEntry.getChunk(); + Interval interval = chunkEntry.getInterval(); + VersionType version = chunkEntry.getVersion(); Map exists = allTimelineEntries.get(interval); TimelineEntry entry; @@ -849,4 +851,37 @@ public int hashCode() return Objects.hash(trueInterval, version, partitionHolder); } } + + public static class PartitionChunkEntry + { + private final Interval interval; + private final VersionType version; + private final PartitionChunk chunk; + + public PartitionChunkEntry( + Interval interval, + VersionType version, + PartitionChunk chunk + ) + { + this.interval = interval; + this.version = version; + this.chunk = chunk; + } + + public Interval getInterval() + { + return interval; + } + + public VersionType getVersion() + { + return version; + } + + public PartitionChunk getChunk() + { + return chunk; + } + } } diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index 2de4995ce878..12291c181903 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -85,6 +85,7 @@ import org.apache.druid.timeline.TimelineLookup; import org.apache.druid.timeline.TimelineObjectHolder; import org.apache.druid.timeline.VersionedIntervalTimeline; +import org.apache.druid.timeline.VersionedIntervalTimeline.PartitionChunkEntry; import org.apache.druid.timeline.partition.PartitionChunk; import org.joda.time.Interval; @@ -859,22 +860,32 @@ public TimelineLookup apply(TimelineLookup timeline2 = new VersionedIntervalTimeline<>(Ordering.natural()); - Iterator> unfilteredIterator = - Iterators.transform(specs.iterator(), spec -> timeline.findChunk( - spec.getInterval(), - spec.getVersion(), - spec.getPartitionNumber() - )); - Iterator> iterator = Iterators.filter( + Iterator> unfilteredIterator = + Iterators.transform(specs.iterator(), spec -> toChunkEntry(timeline, spec)); + Iterator> iterator = Iterators.filter( unfilteredIterator, Objects::nonNull ); - timeline2.addAll( - iterator, - input -> input.getSegment().getInterval(), - input -> input.getSegment().getVersion() - ); + timeline2.addAll(iterator); return timeline2; } + + @Nullable + private PartitionChunkEntry toChunkEntry( + TimelineLookup timeline, + SegmentDescriptor spec + ) + { + PartitionChunk chunk = timeline.findChunk( + spec.getInterval(), + spec.getVersion(), + spec.getPartitionNumber() + ); + if (null == chunk) { + return null; + } + return new PartitionChunkEntry<>(spec.getInterval(), spec.getVersion(), chunk); + + } } } From 659c1de52327eda472cd6d54af0f407e92a2e1ed Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Mon, 1 Mar 2021 23:27:31 +0530 Subject: [PATCH 6/9] Add performance test --- .../CachingClusteredClientPerfTest.java | 255 ++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 server/src/test/java/org/apache/druid/client/CachingClusteredClientPerfTest.java diff --git a/server/src/test/java/org/apache/druid/client/CachingClusteredClientPerfTest.java b/server/src/test/java/org/apache/druid/client/CachingClusteredClientPerfTest.java new file mode 100644 index 000000000000..f5f262e4fd6c --- /dev/null +++ b/server/src/test/java/org/apache/druid/client/CachingClusteredClientPerfTest.java @@ -0,0 +1,255 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.druid.client; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Iterators; +import com.google.common.collect.Ordering; +import org.apache.druid.client.cache.CacheConfig; +import org.apache.druid.client.cache.CachePopulator; +import org.apache.druid.client.cache.MapCache; +import org.apache.druid.client.selector.HighestPriorityTierSelectorStrategy; +import org.apache.druid.client.selector.QueryableDruidServer; +import org.apache.druid.client.selector.RandomServerSelectorStrategy; +import org.apache.druid.client.selector.ServerSelector; +import org.apache.druid.guice.http.DruidHttpClientConfig; +import org.apache.druid.java.util.common.Intervals; +import org.apache.druid.java.util.common.guava.Sequence; +import org.apache.druid.java.util.common.guava.TestSequence; +import org.apache.druid.query.BaseQuery; +import org.apache.druid.query.DataSource; +import org.apache.druid.query.DruidProcessingConfig; +import org.apache.druid.query.Query; +import org.apache.druid.query.QueryPlus; +import org.apache.druid.query.QueryRunner; +import org.apache.druid.query.QueryToolChest; +import org.apache.druid.query.QueryToolChestWarehouse; +import org.apache.druid.query.SegmentDescriptor; +import org.apache.druid.query.TableDataSource; +import org.apache.druid.query.context.ResponseContext; +import org.apache.druid.query.filter.DimFilter; +import org.apache.druid.query.spec.MultipleIntervalSegmentSpec; +import org.apache.druid.query.spec.MultipleSpecificSegmentSpec; +import org.apache.druid.query.spec.QuerySegmentSpec; +import org.apache.druid.segment.TestHelper; +import org.apache.druid.segment.join.NoopJoinableFactory; +import org.apache.druid.server.QueryScheduler; +import org.apache.druid.server.coordination.ServerManagerTest; +import org.apache.druid.server.coordination.ServerType; +import org.apache.druid.timeline.DataSegment; +import org.apache.druid.timeline.VersionedIntervalTimeline; +import org.apache.druid.timeline.partition.LinearShardSpec; +import org.joda.time.Interval; +import org.junit.Assert; +import org.junit.Test; +import org.mockito.Mockito; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ForkJoinPool; + +import static org.mockito.ArgumentMatchers.any; + +/** + * Performance tests for {@link CachingClusteredClient} can be added here. There is one test for a scenario + * where a single interval has large number of segments. + */ +public class CachingClusteredClientPerfTest +{ + + @Test(timeout = 10_000) + public void testGetQueryRunnerForSegments_singleIntervalLargeSegments() + { + final int segmentCount = 30_000; + final Interval interval = Intervals.of("2021-02-13/2021-02-14"); + final List segmentDescriptors = new ArrayList<>(segmentCount); + final List dataSegments = new ArrayList<>(segmentCount); + final VersionedIntervalTimeline timeline = new VersionedIntervalTimeline<>(Ordering.natural()); + final DruidServer server = new DruidServer( + "server", + "localhost:9000", + null, + Long.MAX_VALUE, + ServerType.HISTORICAL, + DruidServer.DEFAULT_TIER, + DruidServer.DEFAULT_PRIORITY + ); + + for (int ii = 0; ii < segmentCount; ii++) { + segmentDescriptors.add(new SegmentDescriptor(interval, "1", ii)); + DataSegment segment = makeDataSegment("test", interval, "1", ii); + dataSegments.add(segment); + } + timeline.addAll( + Iterators.transform(dataSegments.iterator(), segment -> { + ServerSelector ss = new ServerSelector( + segment, + new HighestPriorityTierSelectorStrategy(new RandomServerSelectorStrategy()) + ); + ss.addServerAndUpdateSegment(new QueryableDruidServer( + server, + new MockQueryRunner() + ), segment); + return new VersionedIntervalTimeline.PartitionChunkEntry<>( + segment.getInterval(), + segment.getVersion(), + segment.getShardSpec().createChunk(ss) + ); + }) + ); + + TimelineServerView serverView = Mockito.mock(TimelineServerView.class); + QueryScheduler queryScheduler = Mockito.mock(QueryScheduler.class); + // mock scheduler to return same sequence as argument + Mockito.when(queryScheduler.run(any(), any())).thenAnswer(i -> i.getArgument(1)); + Mockito.when(queryScheduler.prioritizeAndLaneQuery(any(), any())) + .thenAnswer(i -> ((QueryPlus) i.getArgument(0)).getQuery()); + + Mockito.doReturn(Optional.of(timeline)).when(serverView).getTimeline(any()); + Mockito.doReturn(new MockQueryRunner()).when(serverView).getQueryRunner(any()); + CachingClusteredClient cachingClusteredClient = new CachingClusteredClient( + new MockQueryToolChestWareHouse(), + serverView, + MapCache.create(1024), + TestHelper.makeJsonMapper(), + Mockito.mock(CachePopulator.class), + new CacheConfig(), + Mockito.mock(DruidHttpClientConfig.class), + Mockito.mock(DruidProcessingConfig.class), + ForkJoinPool.commonPool(), + queryScheduler, + NoopJoinableFactory.INSTANCE + ); + + Query fakeQuery = makeFakeQuery(interval); + QueryRunner queryRunner = cachingClusteredClient.getQueryRunnerForSegments( + fakeQuery, + segmentDescriptors + ); + Sequence sequence = queryRunner.run(QueryPlus.wrap(fakeQuery)); + Assert.assertEquals(segmentDescriptors, sequence.toList()); + } + + private Query makeFakeQuery(Interval interval) + { + return new TestQuery( + new TableDataSource("test"), + new MultipleIntervalSegmentSpec(Collections.singletonList(interval)), + false, + ImmutableMap.of(BaseQuery.QUERY_ID, "testQuery") + ); + } + + private DataSegment makeDataSegment(String dataSource, Interval interval, String version, int partition) + { + return DataSegment.builder() + .dataSource(dataSource) + .interval(interval) + .version(version) + .shardSpec(new LinearShardSpec(partition)) + .size(1) + .build(); + } + + private static class MockQueryToolChestWareHouse implements QueryToolChestWarehouse + { + + @Override + public > QueryToolChest getToolChest(QueryType query) + { + return new ServerManagerTest.NoopQueryToolChest<>(); + } + } + + private static class MockQueryRunner implements QueryRunner + { + + @Override + public Sequence run( + QueryPlus queryPlus, + ResponseContext responseContext + ) + { + TestQuery query = (TestQuery) queryPlus.getQuery(); + return TestSequence.create(((MultipleSpecificSegmentSpec) query.getSpec()).getDescriptors()); + } + } + + private static class TestQuery extends BaseQuery + { + private QuerySegmentSpec spec; + + public TestQuery( + DataSource dataSource, + QuerySegmentSpec querySegmentSpec, + boolean descending, + Map context + ) + { + super(dataSource, querySegmentSpec, descending, context); + } + + @Override + public boolean hasFilters() + { + return false; + } + + @Override + public DimFilter getFilter() + { + return null; + } + + @Override + public String getType() + { + return "string"; + } + + @Override + public Query withOverriddenContext(Map contextOverride) + { + return this; + } + + @Override + public Query withQuerySegmentSpec(QuerySegmentSpec spec) + { + this.spec = spec; + return this; + } + + @Override + public Query withDataSource(DataSource dataSource) + { + return this; + } + + public QuerySegmentSpec getSpec() + { + return spec; + } + } + +} From 675a958bc2b84f95b2b7fc84598489e9618b7870 Mon Sep 17 00:00:00 2001 From: Abhishek Agarwal <1477457+abhishekagarwal87@users.noreply.github.com> Date: Mon, 1 Mar 2021 23:37:46 +0530 Subject: [PATCH 7/9] Add comment --- .../java/org/apache/druid/client/CachingClusteredClient.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index 12291c181903..046e57edae0d 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -866,6 +866,9 @@ public TimelineLookup apply(TimelineLookup Date: Mon, 8 Mar 2021 12:46:13 +0530 Subject: [PATCH 8/9] Review comments --- .../timeline/VersionedIntervalTimeline.java | 4 ++ .../partition/ImmutablePartitionHolder.java | 44 ------------------- .../timeline/partition/PartitionHolder.java | 5 --- .../SingleElementPartitionChunk.java | 1 + .../druid/client/CachingClusteredClient.java | 5 +-- .../CachingClusteredClientPerfTest.java | 4 +- 6 files changed, 9 insertions(+), 54 deletions(-) delete mode 100644 core/src/main/java/org/apache/druid/timeline/partition/ImmutablePartitionHolder.java diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index f82bf2fbff16..7d8656c15233 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -852,6 +852,10 @@ public int hashCode() } } + /** + * Stores a {@link org.apache.druid.timeline.partition.PartitionChunk} for a given interval and version. The + * interval corresponds to the {@link LogicalSegment#getInterval()} + */ public static class PartitionChunkEntry { private final Interval interval; diff --git a/core/src/main/java/org/apache/druid/timeline/partition/ImmutablePartitionHolder.java b/core/src/main/java/org/apache/druid/timeline/partition/ImmutablePartitionHolder.java deleted file mode 100644 index 9a3bf115cbb4..000000000000 --- a/core/src/main/java/org/apache/druid/timeline/partition/ImmutablePartitionHolder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.druid.timeline.partition; - -import org.apache.druid.timeline.Overshadowable; - -/** - */ -public class ImmutablePartitionHolder> extends PartitionHolder -{ - protected ImmutablePartitionHolder(OvershadowableManager overshadowableManager) - { - super(overshadowableManager); - } - - @Override - public PartitionChunk remove(PartitionChunk tPartitionChunk) - { - throw new UnsupportedOperationException(); - } - - @Override - public boolean add(PartitionChunk tPartitionChunk) - { - throw new UnsupportedOperationException(); - } -} diff --git a/core/src/main/java/org/apache/druid/timeline/partition/PartitionHolder.java b/core/src/main/java/org/apache/druid/timeline/partition/PartitionHolder.java index 289ac9348253..df133f02af6c 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/PartitionHolder.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/PartitionHolder.java @@ -65,11 +65,6 @@ protected PartitionHolder(OvershadowableManager overshadowableManager) this.overshadowableManager = overshadowableManager; } - public ImmutablePartitionHolder asImmutable() - { - return new ImmutablePartitionHolder<>(OvershadowableManager.copyVisible(overshadowableManager)); - } - public boolean add(PartitionChunk chunk) { return overshadowableManager.addChunk(chunk); diff --git a/core/src/main/java/org/apache/druid/timeline/partition/SingleElementPartitionChunk.java b/core/src/main/java/org/apache/druid/timeline/partition/SingleElementPartitionChunk.java index 2567fe617552..90f1023bfb37 100644 --- a/core/src/main/java/org/apache/druid/timeline/partition/SingleElementPartitionChunk.java +++ b/core/src/main/java/org/apache/druid/timeline/partition/SingleElementPartitionChunk.java @@ -21,6 +21,7 @@ /** */ +@Deprecated public class SingleElementPartitionChunk implements PartitionChunk { private final T element; diff --git a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java index 046e57edae0d..369ba2aa20ba 100644 --- a/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java +++ b/server/src/main/java/org/apache/druid/client/CachingClusteredClient.java @@ -866,9 +866,8 @@ public TimelineLookup apply(TimelineLookup Date: Tue, 9 Mar 2021 00:22:10 +0530 Subject: [PATCH 9/9] intellij --- .../org/apache/druid/timeline/VersionedIntervalTimeline.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java index 7d8656c15233..3b8d4129d777 100644 --- a/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java +++ b/core/src/main/java/org/apache/druid/timeline/VersionedIntervalTimeline.java @@ -853,7 +853,7 @@ public int hashCode() } /** - * Stores a {@link org.apache.druid.timeline.partition.PartitionChunk} for a given interval and version. The + * Stores a {@link PartitionChunk} for a given interval and version. The * interval corresponds to the {@link LogicalSegment#getInterval()} */ public static class PartitionChunkEntry