private String rangesAsString() { assert !ranges.isEmpty(); StringBuilder sb = new StringBuilder(); AbstractType at = (AbstractType) ranges.comparator(); assert at != null; for (RangeTombstone i : ranges) { sb.append("["); sb.append(at.getString(i.min)).append("-"); sb.append(at.getString(i.max)).append(", "); sb.append(i.data); sb.append("]"); } return sb.toString(); }
public DeletionInfo(long markedForDeleteAt, int localDeletionTime) { // Pre-1.1 node may return MIN_VALUE for non-deleted container, but the new default is MAX_VALUE // (see CASSANDRA-3872) this( new DeletionTime( markedForDeleteAt, localDeletionTime == Integer.MIN_VALUE ? Integer.MAX_VALUE : localDeletionTime), IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); }
/** * Return a new DeletionInfo correspond to purging every tombstones that are older than {@code * gcbefore}. * * @param gcBefore timestamp (in seconds) before which tombstones should be purged * @return a new DeletionInfo with the purged info remove. Should return DeletionInfo.LIVE if no * tombstones remain. */ public DeletionInfo purge(int gcBefore) { if (ranges.isEmpty()) { return topLevel.localDeletionTime < gcBefore ? LIVE : this; } else { // We rebuild a new intervalTree that contains only non expired range tombstones List<RangeTombstone> nonExpired = new ArrayList<RangeTombstone>(); for (RangeTombstone range : ranges) { if (range.data.localDeletionTime >= gcBefore) nonExpired.add(range); } IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> newRanges = nonExpired.size() == ranges.intervalCount() ? ranges : IntervalTree.build(nonExpired, ranges.comparator()); return topLevel.localDeletionTime < gcBefore ? new DeletionInfo(DeletionTime.LIVE, newRanges) : new DeletionInfo(topLevel, newRanges); } }
public boolean isDeleted(ByteBuffer name, long timestamp) { if (isLive()) return false; if (timestamp <= topLevel.markedForDeleteAt) return true; for (DeletionTime d : ranges.search(name)) { if (timestamp <= d.markedForDeleteAt) return true; } return false; }
static { try { Constructor<RangeTombstone> constructor = RangeTombstone.class.getConstructor( ByteBuffer.class, ByteBuffer.class, DeletionTime.class); itSerializer = IntervalTree.serializer(bbSerializer, DeletionTime.serializer, constructor); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }
public DeletionInfo deserialize(DataInput in, int version, Comparator<ByteBuffer> comparator) throws IOException { assert comparator != null; DeletionTime topLevel = DeletionTime.serializer.deserialize(in); if (version < MessagingService.VERSION_12) return new DeletionInfo( topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges = itSerializer.deserialize(in, version, comparator); return new DeletionInfo(topLevel, ranges); }
/** Returns a new DeletionInfo containing of this plus the provided {@code newInfo}. */ public DeletionInfo add(DeletionInfo newInfo) { if (ranges.isEmpty()) { return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? newInfo : newInfo.ranges.isEmpty() ? this : new DeletionInfo(topLevel, newInfo.ranges); } else { if (newInfo.ranges.isEmpty()) { return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? new DeletionInfo(newInfo.topLevel, ranges) : this; } else { // Need to merge both ranges Set<RangeTombstone> merged = new HashSet<RangeTombstone>(); Iterables.addAll(merged, Iterables.concat(ranges, newInfo.ranges)); return new DeletionInfo( topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? newInfo.topLevel : topLevel, IntervalTree.build(merged, ranges.comparator())); } } }
/** Returns whether this DeletionInfo is live, that is deletes no columns. */ public boolean isLive() { return topLevel.markedForDeleteAt == Long.MIN_VALUE && topLevel.localDeletionTime == Integer.MAX_VALUE && ranges.isEmpty(); }
public DeletionInfo(DeletionTime topLevel) { this(topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); }
public DeletionInfo(RangeTombstone rangeTombstone, Comparator<ByteBuffer> comparator) { this( DeletionTime.LIVE, IntervalTree.build(Collections.<RangeTombstone>singletonList(rangeTombstone), comparator)); assert comparator != null; }
public class DeletionInfo { private static final Serializer serializer = new Serializer(); // We don't have way to represent the full interval of keys (Interval don't support the minimum // token as the right bound), // so we keep the topLevel deletion info separatly. This also slightly optimize the case of full // row deletion which is rather common. private final DeletionTime topLevel; private final IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges; public static final DeletionInfo LIVE = new DeletionInfo( DeletionTime.LIVE, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); public DeletionInfo(long markedForDeleteAt, int localDeletionTime) { // Pre-1.1 node may return MIN_VALUE for non-deleted container, but the new default is MAX_VALUE // (see CASSANDRA-3872) this( new DeletionTime( markedForDeleteAt, localDeletionTime == Integer.MIN_VALUE ? Integer.MAX_VALUE : localDeletionTime), IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); } public DeletionInfo( ByteBuffer start, ByteBuffer end, Comparator<ByteBuffer> comparator, long markedForDeleteAt, int localDeletionTime) { this( new RangeTombstone(start, end, new DeletionTime(markedForDeleteAt, localDeletionTime)), comparator); } public DeletionInfo(RangeTombstone rangeTombstone, Comparator<ByteBuffer> comparator) { this( DeletionTime.LIVE, IntervalTree.build(Collections.<RangeTombstone>singletonList(rangeTombstone), comparator)); assert comparator != null; } public DeletionInfo(DeletionTime topLevel) { this(topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); } private DeletionInfo( DeletionTime topLevel, IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges) { this.topLevel = topLevel; this.ranges = ranges; } public static Serializer serializer() { return serializer; } /** Returns whether this DeletionInfo is live, that is deletes no columns. */ public boolean isLive() { return topLevel.markedForDeleteAt == Long.MIN_VALUE && topLevel.localDeletionTime == Integer.MAX_VALUE && ranges.isEmpty(); } /** * Return whether a given column is deleted by the container having this deletion info. * * @param column the column to check. * @return true if the column is deleted, false otherwise */ public boolean isDeleted(IColumn column) { return isDeleted(column.name(), column.mostRecentLiveChangeAt()); } public boolean isDeleted(ByteBuffer name, long timestamp) { if (isLive()) return false; if (timestamp <= topLevel.markedForDeleteAt) return true; for (DeletionTime d : ranges.search(name)) { if (timestamp <= d.markedForDeleteAt) return true; } return false; } /** * Return a new DeletionInfo correspond to purging every tombstones that are older than {@code * gcbefore}. * * @param gcBefore timestamp (in seconds) before which tombstones should be purged * @return a new DeletionInfo with the purged info remove. Should return DeletionInfo.LIVE if no * tombstones remain. */ public DeletionInfo purge(int gcBefore) { if (ranges.isEmpty()) { return topLevel.localDeletionTime < gcBefore ? LIVE : this; } else { // We rebuild a new intervalTree that contains only non expired range tombstones List<RangeTombstone> nonExpired = new ArrayList<RangeTombstone>(); for (RangeTombstone range : ranges) { if (range.data.localDeletionTime >= gcBefore) nonExpired.add(range); } IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> newRanges = nonExpired.size() == ranges.intervalCount() ? ranges : IntervalTree.build(nonExpired, ranges.comparator()); return topLevel.localDeletionTime < gcBefore ? new DeletionInfo(DeletionTime.LIVE, newRanges) : new DeletionInfo(topLevel, newRanges); } } /** Returns a new DeletionInfo containing of this plus the provided {@code newInfo}. */ public DeletionInfo add(DeletionInfo newInfo) { if (ranges.isEmpty()) { return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? newInfo : newInfo.ranges.isEmpty() ? this : new DeletionInfo(topLevel, newInfo.ranges); } else { if (newInfo.ranges.isEmpty()) { return topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? new DeletionInfo(newInfo.topLevel, ranges) : this; } else { // Need to merge both ranges Set<RangeTombstone> merged = new HashSet<RangeTombstone>(); Iterables.addAll(merged, Iterables.concat(ranges, newInfo.ranges)); return new DeletionInfo( topLevel.markedForDeleteAt < newInfo.topLevel.markedForDeleteAt ? newInfo.topLevel : topLevel, IntervalTree.build(merged, ranges.comparator())); } } } public long minTimestamp() { long minTimestamp = topLevel.markedForDeleteAt; for (RangeTombstone i : ranges) { minTimestamp = Math.min(minTimestamp, i.data.markedForDeleteAt); } return minTimestamp; } /** The maximum timestamp mentioned by this DeletionInfo. */ public long maxTimestamp() { long maxTimestamp = topLevel.markedForDeleteAt; for (RangeTombstone i : ranges) { maxTimestamp = Math.max(maxTimestamp, i.data.markedForDeleteAt); } return maxTimestamp; } public DeletionTime getTopLevelDeletion() { return topLevel; } public Iterator<RangeTombstone> rangeIterator() { return ranges.iterator(); } public int dataSize() { int size = TypeSizes.NATIVE.sizeof(topLevel.markedForDeleteAt); for (RangeTombstone r : ranges) { size += r.min.remaining() + r.max.remaining(); size += TypeSizes.NATIVE.sizeof(r.data.markedForDeleteAt); } return size; } @Override public String toString() { if (ranges.isEmpty()) return String.format("{%s}", topLevel); else return String.format("{%s, ranges=%s}", topLevel, rangesAsString()); } private String rangesAsString() { assert !ranges.isEmpty(); StringBuilder sb = new StringBuilder(); AbstractType at = (AbstractType) ranges.comparator(); assert at != null; for (RangeTombstone i : ranges) { sb.append("["); sb.append(at.getString(i.min)).append("-"); sb.append(at.getString(i.max)).append(", "); sb.append(i.data); sb.append("]"); } return sb.toString(); } @Override public boolean equals(Object o) { if (!(o instanceof DeletionInfo)) return false; DeletionInfo that = (DeletionInfo) o; return topLevel.equals(that.topLevel) && ranges.equals(that.ranges); } @Override public final int hashCode() { return Objects.hashCode(topLevel, ranges); } public static class Serializer implements IVersionedSerializer<DeletionInfo>, ISSTableSerializer<DeletionInfo> { private static final ISerializer<ByteBuffer> bbSerializer = new ISerializer<ByteBuffer>() { public void serialize(ByteBuffer bb, DataOutput dos) throws IOException { ByteBufferUtil.writeWithShortLength(bb, dos); } public ByteBuffer deserialize(DataInput dis) throws IOException { return ByteBufferUtil.readWithShortLength(dis); } public long serializedSize(ByteBuffer bb, TypeSizes typeSizes) { int bbSize = bb.remaining(); return typeSizes.sizeof((short) bbSize) + bbSize; } }; private static final IntervalTree.Serializer<ByteBuffer, DeletionTime, RangeTombstone> itSerializer; static { try { Constructor<RangeTombstone> constructor = RangeTombstone.class.getConstructor( ByteBuffer.class, ByteBuffer.class, DeletionTime.class); itSerializer = IntervalTree.serializer(bbSerializer, DeletionTime.serializer, constructor); } catch (NoSuchMethodException e) { throw new RuntimeException(e); } } public void serialize(DeletionInfo info, DataOutput out, int version) throws IOException { DeletionTime.serializer.serialize(info.topLevel, out); // Pre-1.2 version don't know about range tombstones and thus users should upgrade all // nodes before using them. If they didn't, better fail early that propagating bad info if (version < MessagingService.VERSION_12) { if (!info.ranges.isEmpty()) throw new RuntimeException( "Cannot send range tombstone to pre-1.2 node. You should upgrade all node to Cassandra 1.2+ before using range tombstone."); // Otherwise we're done } else { itSerializer.serialize(info.ranges, out, version); } } public void serializeForSSTable(DeletionInfo info, DataOutput out) throws IOException { DeletionTime.serializer.serialize(info.topLevel, out); } /* * Range tombstones internally depend on the column family serializer, but it is not serialized. * Thus deserialize(DataInput, int, Comparator<ByteBuffer>) should be used instead of this method. */ public DeletionInfo deserialize(DataInput in, int version) throws IOException { throw new UnsupportedOperationException(); } public DeletionInfo deserialize(DataInput in, int version, Comparator<ByteBuffer> comparator) throws IOException { assert comparator != null; DeletionTime topLevel = DeletionTime.serializer.deserialize(in); if (version < MessagingService.VERSION_12) return new DeletionInfo( topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); IntervalTree<ByteBuffer, DeletionTime, RangeTombstone> ranges = itSerializer.deserialize(in, version, comparator); return new DeletionInfo(topLevel, ranges); } public DeletionInfo deserializeFromSSTable(DataInput in, Descriptor.Version version) throws IOException { DeletionTime topLevel = DeletionTime.serializer.deserialize(in); return new DeletionInfo( topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); } public long serializedSize(DeletionInfo info, TypeSizes typeSizes, int version) { long size = DeletionTime.serializer.serializedSize(info.topLevel, typeSizes); if (version < MessagingService.VERSION_12) return size; return size + itSerializer.serializedSize(info.ranges, typeSizes, version); } public long serializedSize(DeletionInfo info, int version) { return serializedSize(info, TypeSizes.NATIVE, version); } } }
public DeletionInfo deserializeFromSSTable(DataInput in, Descriptor.Version version) throws IOException { DeletionTime topLevel = DeletionTime.serializer.deserialize(in); return new DeletionInfo( topLevel, IntervalTree.<ByteBuffer, DeletionTime, RangeTombstone>emptyTree()); }
@Override public boolean equals(Object o) { if (!(o instanceof DeletionInfo)) return false; DeletionInfo that = (DeletionInfo) o; return topLevel.equals(that.topLevel) && ranges.equals(that.ranges); }
@Override public String toString() { if (ranges.isEmpty()) return String.format("{%s}", topLevel); else return String.format("{%s, ranges=%s}", topLevel, rangesAsString()); }
public Iterator<RangeTombstone> rangeIterator() { return ranges.iterator(); }