/*
 * 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.cassandra.db;

import java.util.Collections;
import java.util.Iterator;

import com.google.common.base.Objects;

import org.apache.cassandra.db.rows.*;
import org.apache.cassandra.db.rows.EncodingStats;
import org.apache.cassandra.utils.ObjectSizes;
import org.apache.cassandra.utils.memory.AbstractAllocator;

A mutable implementation of DeletionInfo.
/** * A mutable implementation of {@code DeletionInfo}. */
public class MutableDeletionInfo implements DeletionInfo { private static final long EMPTY_SIZE = ObjectSizes.measure(new MutableDeletionInfo(0, 0));
This represents a deletion of the entire partition. We can't represent this within the RangeTombstoneList, so it's kept separately. This also slightly optimizes the common case of a full partition deletion.
/** * This represents a deletion of the entire partition. We can't represent this within the RangeTombstoneList, so it's * kept separately. This also slightly optimizes the common case of a full partition deletion. */
private DeletionTime partitionDeletion;
A list of range tombstones within the partition. This is left as null if there are no range tombstones (to save an allocation (since it's a common case).
/** * A list of range tombstones within the partition. This is left as null if there are no range tombstones * (to save an allocation (since it's a common case). */
private RangeTombstoneList ranges;
Creates a DeletionInfo with only a top-level (row) tombstone.
Params:
  • markedForDeleteAt – the time after which the entire row should be considered deleted
  • localDeletionTime – what time the deletion write was applied locally (for purposes of purging the tombstone after gc_grace_seconds).
/** * Creates a DeletionInfo with only a top-level (row) tombstone. * @param markedForDeleteAt the time after which the entire row should be considered deleted * @param localDeletionTime what time the deletion write was applied locally (for purposes of * purging the tombstone after gc_grace_seconds). */
public MutableDeletionInfo(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)); } public MutableDeletionInfo(DeletionTime partitionDeletion) { this(partitionDeletion, null); } public MutableDeletionInfo(DeletionTime partitionDeletion, RangeTombstoneList ranges) { this.partitionDeletion = partitionDeletion; this.ranges = ranges; }
Returns a new DeletionInfo that has no top-level tombstone or any range tombstones.
/** * Returns a new DeletionInfo that has no top-level tombstone or any range tombstones. */
public static MutableDeletionInfo live() { return new MutableDeletionInfo(DeletionTime.LIVE); } public MutableDeletionInfo mutableCopy() { return new MutableDeletionInfo(partitionDeletion, ranges == null ? null : ranges.copy()); } public MutableDeletionInfo copy(AbstractAllocator allocator) { RangeTombstoneList rangesCopy = null; if (ranges != null) rangesCopy = ranges.copy(allocator); return new MutableDeletionInfo(partitionDeletion, rangesCopy); }
Returns whether this DeletionInfo is live, that is deletes no columns.
/** * Returns whether this DeletionInfo is live, that is deletes no columns. */
public boolean isLive() { return partitionDeletion.isLive() && (ranges == null || ranges.isEmpty()); }
Potentially replaces the top-level tombstone with another, keeping whichever has the higher markedForDeleteAt timestamp.
Params:
  • newInfo – the deletion time to add to this deletion info.
/** * Potentially replaces the top-level tombstone with another, keeping whichever has the higher markedForDeleteAt * timestamp. * @param newInfo the deletion time to add to this deletion info. */
public void add(DeletionTime newInfo) { if (newInfo.supersedes(partitionDeletion)) partitionDeletion = newInfo; } public void add(RangeTombstone tombstone, ClusteringComparator comparator) { if (ranges == null) ranges = new RangeTombstoneList(comparator, 1); ranges.add(tombstone); }
Combines another DeletionInfo with this one and returns the result. Whichever top-level tombstone has the higher markedForDeleteAt timestamp will be kept, along with its localDeletionTime. The range tombstones will be combined.
Returns:this object.
/** * Combines another DeletionInfo with this one and returns the result. Whichever top-level tombstone * has the higher markedForDeleteAt timestamp will be kept, along with its localDeletionTime. The * range tombstones will be combined. * * @return this object. */
public DeletionInfo add(DeletionInfo newInfo) { add(newInfo.getPartitionDeletion()); // We know MutableDeletionInfo is the only impelementation and we're not mutating it, it's just to get access to the // RangeTombstoneList directly. assert newInfo instanceof MutableDeletionInfo; RangeTombstoneList newRanges = ((MutableDeletionInfo)newInfo).ranges; if (ranges == null) ranges = newRanges == null ? null : newRanges.copy(); else if (newRanges != null) ranges.addAll(newRanges); return this; } public DeletionTime getPartitionDeletion() { return partitionDeletion; } // Use sparingly, not the most efficient thing public Iterator<RangeTombstone> rangeIterator(boolean reversed) { return ranges == null ? Collections.emptyIterator() : ranges.iterator(reversed); } public Iterator<RangeTombstone> rangeIterator(Slice slice, boolean reversed) { return ranges == null ? Collections.emptyIterator() : ranges.iterator(slice, reversed); } public RangeTombstone rangeCovering(Clustering name) { return ranges == null ? null : ranges.search(name); } public int dataSize() { int size = TypeSizes.sizeof(partitionDeletion.markedForDeleteAt()); return size + (ranges == null ? 0 : ranges.dataSize()); } public boolean hasRanges() { return ranges != null && !ranges.isEmpty(); } public int rangeCount() { return hasRanges() ? ranges.size() : 0; } public long maxTimestamp() { return ranges == null ? partitionDeletion.markedForDeleteAt() : Math.max(partitionDeletion.markedForDeleteAt(), ranges.maxMarkedAt()); }
Whether this deletion info may modify the provided one if added to it.
/** * Whether this deletion info may modify the provided one if added to it. */
public boolean mayModify(DeletionInfo delInfo) { return partitionDeletion.compareTo(delInfo.getPartitionDeletion()) > 0 || hasRanges(); } @Override public String toString() { if (ranges == null || ranges.isEmpty()) return String.format("{%s}", partitionDeletion); else return String.format("{%s, ranges=%s}", partitionDeletion, rangesAsString()); } private String rangesAsString() { assert !ranges.isEmpty(); StringBuilder sb = new StringBuilder(); ClusteringComparator cc = ranges.comparator(); Iterator<RangeTombstone> iter = rangeIterator(false); while (iter.hasNext()) { RangeTombstone i = iter.next(); sb.append(i.deletedSlice().toString(cc)); sb.append('@'); sb.append(i.deletionTime()); } return sb.toString(); } // Updates all the timestamp of the deletion contained in this DeletionInfo to be {@code timestamp}. public DeletionInfo updateAllTimestamp(long timestamp) { if (partitionDeletion.markedForDeleteAt() != Long.MIN_VALUE) partitionDeletion = new DeletionTime(timestamp, partitionDeletion.localDeletionTime()); if (ranges != null) ranges.updateAllTimestamp(timestamp); return this; } @Override public boolean equals(Object o) { if(!(o instanceof MutableDeletionInfo)) return false; MutableDeletionInfo that = (MutableDeletionInfo)o; return partitionDeletion.equals(that.partitionDeletion) && Objects.equal(ranges, that.ranges); } @Override public final int hashCode() { return Objects.hashCode(partitionDeletion, ranges); } @Override public long unsharedHeapSize() { return EMPTY_SIZE + partitionDeletion.unsharedHeapSize() + (ranges == null ? 0 : ranges.unsharedHeapSize()); } public void collectStats(EncodingStats.Collector collector) { collector.update(partitionDeletion); if (ranges != null) ranges.collectStats(collector); } public static Builder builder(DeletionTime partitionLevelDeletion, ClusteringComparator comparator, boolean reversed) { return new Builder(partitionLevelDeletion, comparator, reversed); }
Builds DeletionInfo object from (in order) range tombstone markers.
/** * Builds DeletionInfo object from (in order) range tombstone markers. */
public static class Builder { private final MutableDeletionInfo deletion; private final ClusteringComparator comparator; private final boolean reversed; private RangeTombstoneMarker openMarker; private Builder(DeletionTime partitionLevelDeletion, ClusteringComparator comparator, boolean reversed) { this.deletion = new MutableDeletionInfo(partitionLevelDeletion); this.comparator = comparator; this.reversed = reversed; } public void add(RangeTombstoneMarker marker) { // We need to start by the close case in case that's a boundary if (marker.isClose(reversed)) { DeletionTime openDeletion = openMarker.openDeletionTime(reversed); assert marker.closeDeletionTime(reversed).equals(openDeletion); ClusteringBound open = openMarker.openBound(reversed); ClusteringBound close = marker.closeBound(reversed); Slice slice = reversed ? Slice.make(close, open) : Slice.make(open, close); deletion.add(new RangeTombstone(slice, openDeletion), comparator); } if (marker.isOpen(reversed)) { openMarker = marker; } } public MutableDeletionInfo build() { return deletion; } } }