2626import org .apache .druid .java .util .common .granularity .GranularityType ;
2727import org .apache .druid .timeline .partition .NumberedOverwriteShardSpec ;
2828import org .apache .druid .timeline .partition .NumberedShardSpec ;
29+ import org .apache .druid .timeline .partition .PartitionChunk ;
2930import org .apache .druid .timeline .partition .PartitionIds ;
3031import org .apache .druid .timeline .partition .ShardSpec ;
3132import org .joda .time .DateTime ;
5354import java .util .concurrent .ThreadLocalRandom ;
5455
5556@ State (Scope .Benchmark )
56- @ Fork (value = 1 , jvmArgsAppend = {"-XX:+UseG1GC" })
57+ @ Fork (value = 1 , jvmArgsAppend = {"-XX:+UseG1GC" , "-Xmx4g" })
5758@ Warmup (iterations = 10 )
5859@ Measurement (iterations = 10 )
5960@ BenchmarkMode ({Mode .Throughput })
@@ -73,7 +74,7 @@ public class VersionedIntervalTimelineBenchmark
7374 @ Param ({"false" , "true" })
7475 private boolean useSegmentLock ;
7576
76- @ Param ({"MONTH" , "DAY" })
77+ @ Param ({"MONTH" , "DAY" , "HOUR" , "MINUTE" })
7778 private GranularityType segmentGranularity ;
7879
7980 private List <Interval > intervals ;
@@ -216,16 +217,22 @@ public void benchAdd(Blackhole blackhole)
216217 }
217218 }
218219
220+ /**
221+ * Measures the cost of draining 10% of the timeline in one shot by rebuilding the timeline fresh
222+ * each invocation. This is the "bulk removal" case — use {@link Mode#AverageTime} so the result
223+ * is interpretable as "seconds per full 10%-drain" rather than a near-zero throughput figure.
224+ */
219225 @ Benchmark
226+ @ BenchmarkMode (Mode .AverageTime )
220227 public void benchRemove (Blackhole blackhole )
221228 {
222229 final List <DataSegment > segmentsCopy = new ArrayList <>(segments );
223- final SegmentTimeline timeline = SegmentTimeline .forSegments (segmentsCopy );
230+ final SegmentTimeline localTimeline = SegmentTimeline .forSegments (segmentsCopy );
224231 final int numTests = (int ) (segmentsCopy .size () * 0.1 );
225232 for (int i = 0 ; i < numTests ; i ++) {
226233 final DataSegment segment = segmentsCopy .remove (ThreadLocalRandom .current ().nextInt (segmentsCopy .size ()));
227234 blackhole .consume (
228- timeline .remove (
235+ localTimeline .remove (
229236 segment .getInterval (),
230237 segment .getVersion (),
231238 segment .getShardSpec ().createChunk (segment )
@@ -234,6 +241,27 @@ public void benchRemove(Blackhole blackhole)
234241 }
235242 }
236243
244+ /**
245+ * Measures per-remove throughput against the pre-built timeline. One invocation = one remove + one
246+ * re-add to keep the timeline stable.
247+ */
248+ @ Benchmark
249+ @ BenchmarkMode (Mode .Throughput )
250+ public void benchRemoveWithReplacement (Blackhole blackhole )
251+ {
252+ final DataSegment segment = segments .get (ThreadLocalRandom .current ().nextInt (segments .size ()));
253+ final PartitionChunk <DataSegment > chunk = segment .getShardSpec ().createChunk (segment );
254+ final PartitionChunk <DataSegment > removed = timeline .remove (
255+ segment .getInterval (),
256+ segment .getVersion (),
257+ chunk
258+ );
259+ if (removed != null ) {
260+ timeline .add (segment .getInterval (), segment .getVersion (), chunk );
261+ }
262+ blackhole .consume (removed );
263+ }
264+
237265 @ Benchmark
238266 public void benchLookup (Blackhole blackhole )
239267 {
@@ -245,6 +273,26 @@ public void benchLookup(Blackhole blackhole)
245273 blackhole .consume (timeline .lookup (queryInterval ));
246274 }
247275
276+ /**
277+ * Looks up a single-interval window — the case that benefits most from the O(log N + K) range scan since only a
278+ * tiny fraction of the timeline is touched regardless of overall size.
279+ */
280+ @ Benchmark
281+ public void benchLookupSingleInterval (Blackhole blackhole )
282+ {
283+ final int intervalIndex = ThreadLocalRandom .current ().nextInt (intervals .size ());
284+ blackhole .consume (timeline .lookup (intervals .get (intervalIndex )));
285+ }
286+
287+ /**
288+ * Full-range lookup — scans all segments regardless of optimization; provides an upper-bound baseline.
289+ */
290+ @ Benchmark
291+ public void benchLookupFullRange (Blackhole blackhole )
292+ {
293+ blackhole .consume (timeline .lookup (TOTAL_INTERVAL ));
294+ }
295+
248296 @ Benchmark
249297 public void benchIsOvershadowed (Blackhole blackhole )
250298 {
0 commit comments