diff --git a/src/java/org/apache/cassandra/db/lifecycle/SSTableIntervalTree.java b/src/java/org/apache/cassandra/db/lifecycle/SSTableIntervalTree.java index 9f28fc5995b9..4d5a87f3991d 100644 --- a/src/java/org/apache/cassandra/db/lifecycle/SSTableIntervalTree.java +++ b/src/java/org/apache/cassandra/db/lifecycle/SSTableIntervalTree.java @@ -25,11 +25,14 @@ import java.util.Collections; import java.util.List; +import org.apache.cassandra.config.DatabaseDescriptor; import org.apache.cassandra.db.PartitionPosition; import org.apache.cassandra.io.sstable.format.SSTableReader; import org.apache.cassandra.utils.Interval; import org.apache.cassandra.utils.IntervalTree; +import static com.google.common.base.Preconditions.checkState; + public class SSTableIntervalTree extends IntervalTree> { private static final SSTableIntervalTree EMPTY = new SSTableIntervalTree(null); @@ -75,8 +78,28 @@ public static Interval[] buildIntervalsArray(C return IntervalTree.EMPTY_ARRAY; Interval[] intervals = new Interval[sstables.size()]; int i = 0; + int missingIntervals = 0; for (SSTableReader sstable : sstables) - intervals[i++] = sstable.getInterval(); + { + Interval interval = sstable.getInterval(); + if (interval == null) + { + missingIntervals++; + continue; + } + intervals[i++] = interval; + } + + // Offline (scrub) tools create SSTableReader without a first and last key and the old interval tree + // built a corrupt tree that couldn't be searched so continue to do that rather than complicate Tracker/View + if (missingIntervals > 0) + { + checkState(DatabaseDescriptor.isToolInitialized(), "Can only safely build an interval tree on sstables with missing first and last for offline tools"); + Interval[] replacementIntervals = new Interval[intervals.length - missingIntervals]; + System.arraycopy(intervals, 0, replacementIntervals, 0, replacementIntervals.length); + return replacementIntervals; + } + return intervals; } diff --git a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java index 1824412d9bbe..75272a0b5f04 100644 --- a/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java +++ b/src/java/org/apache/cassandra/io/sstable/format/SSTableReader.java @@ -475,7 +475,7 @@ protected SSTableReader(Builder builder, Owner owner) this.openReason = builder.getOpenReason(); this.first = builder.getFirst(); this.last = builder.getLast(); - this.interval = Interval.create(first, last, this); + this.interval = first == null || last == null ? null : Interval.create(first, last, this); this.bounds = first == null || last == null || AbstractBounds.strictlyWrapsAround(first.getToken(), last.getToken()) ? null // this will cause the validation to fail, but the reader is opened with no validation, // e.g. for scrubbing, we should accept screwed bounds diff --git a/src/java/org/apache/cassandra/utils/Interval.java b/src/java/org/apache/cassandra/utils/Interval.java index 6e7b8c33a3fb..b84e0a9d8388 100644 --- a/src/java/org/apache/cassandra/utils/Interval.java +++ b/src/java/org/apache/cassandra/utils/Interval.java @@ -19,6 +19,8 @@ import com.google.common.base.Objects; +import static com.google.common.base.Preconditions.checkNotNull; + public class Interval { public final C min; @@ -27,6 +29,8 @@ public class Interval public Interval(C min, C max, D data) { + checkNotNull(min, "min is null"); + checkNotNull(max, "max is null"); this.min = min; this.max = max; this.data = data;