/* * 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.io.sstable.metadata; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.cassandra.db.TypeSizes; import org.apache.cassandra.db.commitlog.ReplayPosition; import org.apache.cassandra.io.sstable.Descriptor; import org.apache.cassandra.utils.ByteBufferUtil; import org.apache.cassandra.utils.EstimatedHistogram; import org.apache.cassandra.utils.StreamingHistogram; /** * SSTable metadata that always stay on heap. */ public class StatsMetadata extends MetadataComponent { public static final IMetadataComponentSerializer serializer = new StatsMetadataSerializer(); public final EstimatedHistogram estimatedRowSize; public final EstimatedHistogram estimatedColumnCount; public final ReplayPosition replayPosition; public final long minTimestamp; public final long maxTimestamp; public final int maxLocalDeletionTime; public final double compressionRatio; public final StreamingHistogram estimatedTombstoneDropTime; public final int sstableLevel; public final List<ByteBuffer> maxColumnNames; public final List<ByteBuffer> minColumnNames; public StatsMetadata(EstimatedHistogram estimatedRowSize, EstimatedHistogram estimatedColumnCount, ReplayPosition replayPosition, long minTimestamp, long maxTimestamp, int maxLocalDeletionTime, double compressionRatio, StreamingHistogram estimatedTombstoneDropTime, int sstableLevel, List<ByteBuffer> minColumnNames, List<ByteBuffer> maxColumnNames) { this.estimatedRowSize = estimatedRowSize; this.estimatedColumnCount = estimatedColumnCount; this.replayPosition = replayPosition; this.minTimestamp = minTimestamp; this.maxTimestamp = maxTimestamp; this.maxLocalDeletionTime = maxLocalDeletionTime; this.compressionRatio = compressionRatio; this.estimatedTombstoneDropTime = estimatedTombstoneDropTime; this.sstableLevel = sstableLevel; this.minColumnNames = minColumnNames; this.maxColumnNames = maxColumnNames; } public MetadataType getType() { return MetadataType.STATS; } /** * @param gcBefore gc time in seconds * @return estimated droppable tombstone ratio at given gcBefore time. */ public double getEstimatedDroppableTombstoneRatio(int gcBefore) { long estimatedColumnCount = this.estimatedColumnCount.mean() * this.estimatedColumnCount.count(); if (estimatedColumnCount > 0) { double droppable = getDroppableTombstonesBefore(gcBefore); return droppable / estimatedColumnCount; } return 0.0f; } /** * @param gcBefore gc time in seconds * @return amount of droppable tombstones */ public double getDroppableTombstonesBefore(int gcBefore) { return estimatedTombstoneDropTime.sum(gcBefore); } public StatsMetadata mutateLevel(int newLevel) { return new StatsMetadata(estimatedRowSize, estimatedColumnCount, replayPosition, minTimestamp, maxTimestamp, maxLocalDeletionTime, compressionRatio, estimatedTombstoneDropTime, newLevel, maxColumnNames, minColumnNames); } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; StatsMetadata that = (StatsMetadata) o; return new EqualsBuilder() .append(estimatedRowSize, that.estimatedRowSize) .append(estimatedColumnCount, that.estimatedColumnCount) .append(replayPosition, that.replayPosition) .append(minTimestamp, that.minTimestamp) .append(maxTimestamp, that.maxTimestamp) .append(maxLocalDeletionTime, that.maxLocalDeletionTime) .append(compressionRatio, that.compressionRatio) .append(estimatedTombstoneDropTime, that.estimatedTombstoneDropTime) .append(sstableLevel, that.sstableLevel) .append(maxColumnNames, that.maxColumnNames) .append(minColumnNames, that.minColumnNames) .build(); } @Override public int hashCode() { return new HashCodeBuilder() .append(estimatedRowSize) .append(estimatedColumnCount) .append(replayPosition) .append(minTimestamp) .append(maxTimestamp) .append(maxLocalDeletionTime) .append(compressionRatio) .append(estimatedTombstoneDropTime) .append(sstableLevel) .append(maxColumnNames) .append(minColumnNames) .build(); } public static class StatsMetadataSerializer implements IMetadataComponentSerializer<StatsMetadata> { public int serializedSize(StatsMetadata component) throws IOException { int size = 0; size += EstimatedHistogram.serializer.serializedSize(component.estimatedRowSize, TypeSizes.NATIVE); size += EstimatedHistogram.serializer.serializedSize(component.estimatedColumnCount, TypeSizes.NATIVE); size += ReplayPosition.serializer.serializedSize(component.replayPosition, TypeSizes.NATIVE); size += 8 + 8 + 4 + 8; // mix/max timestamp(long), maxLocalDeletionTime(int), compressionRatio(double) size += StreamingHistogram.serializer.serializedSize(component.estimatedTombstoneDropTime, TypeSizes.NATIVE); size += TypeSizes.NATIVE.sizeof(component.sstableLevel); // min column names size += 4; for (ByteBuffer columnName : component.minColumnNames) size += 2 + columnName.remaining(); // with short length // max column names size += 4; for (ByteBuffer columnName : component.maxColumnNames) size += 2 + columnName.remaining(); // with short length return size; } public void serialize(StatsMetadata component, DataOutput out) throws IOException { EstimatedHistogram.serializer.serialize(component.estimatedRowSize, out); EstimatedHistogram.serializer.serialize(component.estimatedColumnCount, out); ReplayPosition.serializer.serialize(component.replayPosition, out); out.writeLong(component.minTimestamp); out.writeLong(component.maxTimestamp); out.writeInt(component.maxLocalDeletionTime); out.writeDouble(component.compressionRatio); StreamingHistogram.serializer.serialize(component.estimatedTombstoneDropTime, out); out.writeInt(component.sstableLevel); out.writeInt(component.minColumnNames.size()); for (ByteBuffer columnName : component.minColumnNames) ByteBufferUtil.writeWithShortLength(columnName, out); out.writeInt(component.maxColumnNames.size()); for (ByteBuffer columnName : component.maxColumnNames) ByteBufferUtil.writeWithShortLength(columnName, out); } public StatsMetadata deserialize(Descriptor.Version version, DataInput in) throws IOException { EstimatedHistogram rowSizes = EstimatedHistogram.serializer.deserialize(in); EstimatedHistogram columnCounts = EstimatedHistogram.serializer.deserialize(in); ReplayPosition replayPosition = ReplayPosition.serializer.deserialize(in); long minTimestamp = in.readLong(); long maxTimestamp = in.readLong(); int maxLocalDeletionTime = version.tracksMaxLocalDeletionTime ? in.readInt() : Integer.MAX_VALUE; double compressionRatio = in.readDouble(); StreamingHistogram tombstoneHistogram = StreamingHistogram.serializer.deserialize(in); int sstableLevel = in.readInt(); List<ByteBuffer> minColumnNames; List<ByteBuffer> maxColumnNames; if (version.tracksMaxMinColumnNames) { int colCount = in.readInt(); minColumnNames = new ArrayList<>(colCount); for (int i = 0; i < colCount; i++) { minColumnNames.add(ByteBufferUtil.readWithShortLength(in)); } colCount = in.readInt(); maxColumnNames = new ArrayList<>(colCount); for (int i = 0; i < colCount; i++) { maxColumnNames.add(ByteBufferUtil.readWithShortLength(in)); } } else { minColumnNames = Collections.emptyList(); maxColumnNames = Collections.emptyList(); } return new StatsMetadata(rowSizes, columnCounts, replayPosition, minTimestamp, maxTimestamp, maxLocalDeletionTime, compressionRatio, tombstoneHistogram, sstableLevel, minColumnNames, maxColumnNames); } } }