blob: fa0fb2c20e109083c99381e5db5bfd43f7e15744 [file] [log] [blame]
/*
* 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;
import java.io.IOException;
import java.util.List;
import org.apache.cassandra.db.ClusteringPrefix;
import org.apache.cassandra.db.DeletionTime;
import org.apache.cassandra.db.RowIndexEntry;
import org.apache.cassandra.db.SerializationHeader;
import org.apache.cassandra.db.TypeSizes;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.io.ISerializer;
import org.apache.cassandra.io.sstable.format.Version;
import org.apache.cassandra.io.util.DataInputPlus;
import org.apache.cassandra.io.util.DataOutputPlus;
import org.apache.cassandra.utils.ObjectSizes;
/**
* {@code IndexInfo} is embedded in the indexed version of {@link RowIndexEntry}.
* Each instance roughly covers a range of {@link org.apache.cassandra.config.Config#column_index_size column_index_size} KiB
* and contains the first and last clustering value (or slice bound), its offset in the data file and width in the data file.
* <p>
* Each {@code IndexInfo} object is serialized as follows.
* </p>
* <p>
* Serialization format changed in 3.0; the {@link #endOpenMarker} has been introduced and integer fields are
* stored using varint encoding.
* </p>
* <p>
* {@code
* (*) IndexInfo.firstName (ClusteringPrefix serializer, either Clustering.serializer.serialize or Slice.Bound.serializer.serialize)
* (*) IndexInfo.lastName (ClusteringPrefix serializer, either Clustering.serializer.serialize or Slice.Bound.serializer.serialize)
* (long) IndexInfo.offset
* (long) IndexInfo.width
* (bool) IndexInfo.endOpenMarker != null (if 3.0)
* (int) IndexInfo.endOpenMarker.localDeletionTime (if 3.0 && IndexInfo.endOpenMarker != null)
* (long) IndexInfo.endOpenMarker.markedForDeletionAt (if 3.0 && IndexInfo.endOpenMarker != null)
* }
* </p>
*/
public class IndexInfo
{
public static final long EMPTY_SIZE = ObjectSizes.measure(new IndexInfo(null, null, 0, 0, null));
public final long offset;
public final long width;
public final ClusteringPrefix<?> firstName;
public final ClusteringPrefix<?> lastName;
// If at the end of the index block there is an open range tombstone marker, this marker
// deletion infos. null otherwise.
public final DeletionTime endOpenMarker;
public IndexInfo(ClusteringPrefix<?> firstName,
ClusteringPrefix<?> lastName,
long offset,
long width,
DeletionTime endOpenMarker)
{
this.firstName = firstName;
this.lastName = lastName;
this.offset = offset;
this.width = width;
this.endOpenMarker = endOpenMarker;
}
public static IndexInfo.Serializer serializer(Version version, SerializationHeader header)
{
return new IndexInfo.Serializer(version, header.clusteringTypes());
}
public static class Serializer implements ISerializer<IndexInfo>
{
// This is the default index size that we use to delta-encode width when serializing so we get better vint-encoding.
// This is imperfect as user can change the index size and ideally we would save the index size used with each index file
// to use as base. However, that's a bit more involved a change that we want for now and very seldom do use change the index
// size so using the default is almost surely better than using no base at all.
public static final long WIDTH_BASE = 64 * 1024;
private final int version;
private final List<AbstractType<?>> clusteringTypes;
public Serializer(Version version, List<AbstractType<?>> clusteringTypes)
{
this.version = version.correspondingMessagingVersion();
this.clusteringTypes = clusteringTypes;
}
public void serialize(IndexInfo info, DataOutputPlus out) throws IOException
{
ClusteringPrefix.serializer.serialize(info.firstName, out, version, clusteringTypes);
ClusteringPrefix.serializer.serialize(info.lastName, out, version, clusteringTypes);
out.writeUnsignedVInt(info.offset);
out.writeVInt(info.width - WIDTH_BASE);
out.writeBoolean(info.endOpenMarker != null);
if (info.endOpenMarker != null)
DeletionTime.serializer.serialize(info.endOpenMarker, out);
}
public void skip(DataInputPlus in) throws IOException
{
ClusteringPrefix.serializer.skip(in, version, clusteringTypes);
ClusteringPrefix.serializer.skip(in, version, clusteringTypes);
in.readUnsignedVInt();
in.readVInt();
if (in.readBoolean())
DeletionTime.serializer.skip(in);
}
public IndexInfo deserialize(DataInputPlus in) throws IOException
{
ClusteringPrefix<byte[]> firstName = ClusteringPrefix.serializer.deserialize(in, version, clusteringTypes);
ClusteringPrefix<byte[]> lastName = ClusteringPrefix.serializer.deserialize(in, version, clusteringTypes);
long offset = in.readUnsignedVInt();
long width = in.readVInt() + WIDTH_BASE;
DeletionTime endOpenMarker = null;
if (in.readBoolean())
endOpenMarker = DeletionTime.serializer.deserialize(in);
return new IndexInfo(firstName, lastName, offset, width, endOpenMarker);
}
public long serializedSize(IndexInfo info)
{
long size = ClusteringPrefix.serializer.serializedSize(info.firstName, version, clusteringTypes)
+ ClusteringPrefix.serializer.serializedSize(info.lastName, version, clusteringTypes)
+ TypeSizes.sizeofUnsignedVInt(info.offset)
+ TypeSizes.sizeofVInt(info.width - WIDTH_BASE)
+ TypeSizes.sizeof(info.endOpenMarker != null);
if (info.endOpenMarker != null)
size += DeletionTime.serializer.serializedSize(info.endOpenMarker);
return size;
}
}
public long unsharedHeapSize()
{
return EMPTY_SIZE
+ firstName.unsharedHeapSize()
+ lastName.unsharedHeapSize()
+ (endOpenMarker == null ? 0 : endOpenMarker.unsharedHeapSize());
}
}