mirror of https://github.com/apache/cassandra
Keep persistent row size and column count statistics. Patch by brandonwilliams and jbellis; reviewed by jbellis for CASSANDRA-1155
git-svn-id: https://svn.apache.org/repos/asf/cassandra/trunk@967213 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
7a926e5bff
commit
c00a5c1f58
|
|
@ -44,6 +44,7 @@ dev
|
||||||
* significantly faster reads from row cache (CASSANDRA-1267)
|
* significantly faster reads from row cache (CASSANDRA-1267)
|
||||||
* take advantage of row cache during range queries (CASSANDRA-1302)
|
* take advantage of row cache during range queries (CASSANDRA-1302)
|
||||||
* make GCGraceSeconds a per-ColumnFamily value (CASSANDRA-1276)
|
* make GCGraceSeconds a per-ColumnFamily value (CASSANDRA-1276)
|
||||||
|
* keep persistent row size and column count statistics (CASSANDRA-1155)
|
||||||
|
|
||||||
|
|
||||||
0.6.4
|
0.6.4
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,7 @@ import org.apache.cassandra.db.clock.AbstractReconciler;
|
||||||
import org.apache.cassandra.db.clock.TimestampReconciler;
|
import org.apache.cassandra.db.clock.TimestampReconciler;
|
||||||
import org.apache.cassandra.db.HintedHandOffManager;
|
import org.apache.cassandra.db.HintedHandOffManager;
|
||||||
import org.apache.cassandra.db.SystemTable;
|
import org.apache.cassandra.db.SystemTable;
|
||||||
|
import org.apache.cassandra.db.StatisticsTable;
|
||||||
import org.apache.cassandra.db.Table;
|
import org.apache.cassandra.db.Table;
|
||||||
import org.apache.cassandra.db.marshal.AbstractType;
|
import org.apache.cassandra.db.marshal.AbstractType;
|
||||||
import org.apache.cassandra.db.marshal.BytesType;
|
import org.apache.cassandra.db.marshal.BytesType;
|
||||||
|
|
@ -64,6 +65,7 @@ public final class CFMetaData
|
||||||
public static final CFMetaData HintsCf = new CFMetaData(Table.SYSTEM_TABLE, HintedHandOffManager.HINTS_CF, ColumnFamilyType.Super, ClockType.Timestamp, UTF8Type.instance, BytesType.instance, new TimestampReconciler(), "hinted handoff data", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 1, Collections.<byte[], ColumnDefinition>emptyMap());
|
public static final CFMetaData HintsCf = new CFMetaData(Table.SYSTEM_TABLE, HintedHandOffManager.HINTS_CF, ColumnFamilyType.Super, ClockType.Timestamp, UTF8Type.instance, BytesType.instance, new TimestampReconciler(), "hinted handoff data", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 1, Collections.<byte[], ColumnDefinition>emptyMap());
|
||||||
public static final CFMetaData MigrationsCf = new CFMetaData(Table.SYSTEM_TABLE, Migration.MIGRATIONS_CF, ColumnFamilyType.Standard, ClockType.Timestamp, TimeUUIDType.instance, null, new TimestampReconciler(), "individual schema mutations", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 2, Collections.<byte[], ColumnDefinition>emptyMap());
|
public static final CFMetaData MigrationsCf = new CFMetaData(Table.SYSTEM_TABLE, Migration.MIGRATIONS_CF, ColumnFamilyType.Standard, ClockType.Timestamp, TimeUUIDType.instance, null, new TimestampReconciler(), "individual schema mutations", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 2, Collections.<byte[], ColumnDefinition>emptyMap());
|
||||||
public static final CFMetaData SchemaCf = new CFMetaData(Table.SYSTEM_TABLE, Migration.SCHEMA_CF, ColumnFamilyType.Standard, ClockType.Timestamp, UTF8Type.instance, null, new TimestampReconciler(), "current state of the schema", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 3, Collections. <byte[], ColumnDefinition>emptyMap());
|
public static final CFMetaData SchemaCf = new CFMetaData(Table.SYSTEM_TABLE, Migration.SCHEMA_CF, ColumnFamilyType.Standard, ClockType.Timestamp, UTF8Type.instance, null, new TimestampReconciler(), "current state of the schema", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 3, Collections. <byte[], ColumnDefinition>emptyMap());
|
||||||
|
public static final CFMetaData StatisticsCf = new CFMetaData(Table.SYSTEM_TABLE, StatisticsTable.STATISTICS_CF, ColumnFamilyType.Super, ClockType.Timestamp, UTF8Type.instance, BytesType.instance, new TimestampReconciler(), "persistent CF statistics for the local node", 0, false, 0.01, DEFAULT_GC_GRACE_SECONDS, 4, Collections.<byte[], ColumnDefinition>emptyMap());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return An immutable mapping of (ksname,cfname) to id.
|
* @return An immutable mapping of (ksname,cfname) to id.
|
||||||
|
|
|
||||||
|
|
@ -348,12 +348,14 @@ public class DatabaseDescriptor
|
||||||
KSMetaData systemMeta = new KSMetaData(Table.SYSTEM_TABLE, LocalStrategy.class, 1, new CFMetaData[]{CFMetaData.StatusCf,
|
KSMetaData systemMeta = new KSMetaData(Table.SYSTEM_TABLE, LocalStrategy.class, 1, new CFMetaData[]{CFMetaData.StatusCf,
|
||||||
CFMetaData.HintsCf,
|
CFMetaData.HintsCf,
|
||||||
CFMetaData.MigrationsCf,
|
CFMetaData.MigrationsCf,
|
||||||
CFMetaData.SchemaCf
|
CFMetaData.SchemaCf,
|
||||||
|
CFMetaData.StatisticsCf
|
||||||
});
|
});
|
||||||
CFMetaData.map(CFMetaData.StatusCf);
|
CFMetaData.map(CFMetaData.StatusCf);
|
||||||
CFMetaData.map(CFMetaData.HintsCf);
|
CFMetaData.map(CFMetaData.HintsCf);
|
||||||
CFMetaData.map(CFMetaData.MigrationsCf);
|
CFMetaData.map(CFMetaData.MigrationsCf);
|
||||||
CFMetaData.map(CFMetaData.SchemaCf);
|
CFMetaData.map(CFMetaData.SchemaCf);
|
||||||
|
CFMetaData.map(CFMetaData.StatisticsCf);
|
||||||
tables.put(Table.SYSTEM_TABLE, systemMeta);
|
tables.put(Table.SYSTEM_TABLE, systemMeta);
|
||||||
|
|
||||||
/* Load the seeds for node contact points */
|
/* Load the seeds for node contact points */
|
||||||
|
|
|
||||||
|
|
@ -69,18 +69,20 @@ public class ColumnFamilySerializer implements ICompactSerializer2<ColumnFamily>
|
||||||
serializeForSSTable(columnFamily, dos);
|
serializeForSSTable(columnFamily, dos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serializeForSSTable(ColumnFamily columnFamily, DataOutput dos)
|
public int serializeForSSTable(ColumnFamily columnFamily, DataOutput dos)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
serializeCFInfo(columnFamily, dos);
|
serializeCFInfo(columnFamily, dos);
|
||||||
|
|
||||||
Collection<IColumn> columns = columnFamily.getSortedColumns();
|
Collection<IColumn> columns = columnFamily.getSortedColumns();
|
||||||
dos.writeInt(columns.size());
|
int count = columns.size();
|
||||||
|
dos.writeInt(count);
|
||||||
for (IColumn column : columns)
|
for (IColumn column : columns)
|
||||||
{
|
{
|
||||||
columnFamily.getColumnSerializer().serialize(column, dos);
|
columnFamily.getColumnSerializer().serialize(column, dos);
|
||||||
}
|
}
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
@ -95,10 +97,10 @@ public class ColumnFamilySerializer implements ICompactSerializer2<ColumnFamily>
|
||||||
columnFamily.getClockType().serializer().serialize(_markedForDeleteAt, dos);
|
columnFamily.getClockType().serializer().serialize(_markedForDeleteAt, dos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void serializeWithIndexes(ColumnFamily columnFamily, DataOutput dos)
|
public int serializeWithIndexes(ColumnFamily columnFamily, DataOutput dos)
|
||||||
{
|
{
|
||||||
ColumnIndexer.serialize(columnFamily, dos);
|
ColumnIndexer.serialize(columnFamily, dos);
|
||||||
serializeForSSTable(columnFamily, dos);
|
return serializeForSSTable(columnFamily, dos);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ColumnFamily deserialize(DataInput dis) throws IOException
|
public ColumnFamily deserialize(DataInput dis) throws IOException
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
||||||
TimeUnit.SECONDS,
|
TimeUnit.SECONDS,
|
||||||
new LinkedBlockingQueue<Runnable>(DatabaseDescriptor.getFlushWriters()),
|
new LinkedBlockingQueue<Runnable>(DatabaseDescriptor.getFlushWriters()),
|
||||||
new NamedThreadFactory("FLUSH-WRITER-POOL"));
|
new NamedThreadFactory("FLUSH-WRITER-POOL"));
|
||||||
private static ExecutorService commitLogUpdater_ = new JMXEnabledThreadPoolExecutor("MEMTABLE-POST-FLUSHER");
|
private static ExecutorService postFlushExecutor_ = new JMXEnabledThreadPoolExecutor("MEMTABLE-POST-FLUSHER");
|
||||||
|
|
||||||
private static final FilenameFilter DB_NAME_FILTER = new FilenameFilter()
|
private static final FilenameFilter DB_NAME_FILTER = new FilenameFilter()
|
||||||
{
|
{
|
||||||
|
|
@ -133,10 +133,6 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
||||||
private LatencyTracker readStats_ = new LatencyTracker();
|
private LatencyTracker readStats_ = new LatencyTracker();
|
||||||
private LatencyTracker writeStats_ = new LatencyTracker();
|
private LatencyTracker writeStats_ = new LatencyTracker();
|
||||||
|
|
||||||
private long minRowCompactedSize = 0L;
|
|
||||||
private long maxRowCompactedSize = 0L;
|
|
||||||
private long rowsCompactedTotalSize = 0L;
|
|
||||||
private long rowsCompactedCount = 0L;
|
|
||||||
final CFMetaData metadata;
|
final CFMetaData metadata;
|
||||||
|
|
||||||
ColumnFamilyStore(String table, String columnFamilyName, IPartitioner partitioner, int generation, CFMetaData metadata)
|
ColumnFamilyStore(String table, String columnFamilyName, IPartitioner partitioner, int generation, CFMetaData metadata)
|
||||||
|
|
@ -257,32 +253,41 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addToCompactedRowStats(long rowsize)
|
|
||||||
{
|
|
||||||
if (minRowCompactedSize < 1 || rowsize < minRowCompactedSize)
|
|
||||||
minRowCompactedSize = rowsize;
|
|
||||||
if (rowsize > maxRowCompactedSize)
|
|
||||||
maxRowCompactedSize = rowsize;
|
|
||||||
rowsCompactedCount++;
|
|
||||||
rowsCompactedTotalSize += rowsize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getMinRowCompactedSize()
|
public long getMinRowCompactedSize()
|
||||||
{
|
{
|
||||||
return minRowCompactedSize;
|
long min = 0;
|
||||||
|
for (SSTableReader sstable : ssTables_)
|
||||||
|
{
|
||||||
|
if (min == 0 || sstable.getEstimatedRowSize().min() < min)
|
||||||
|
min = sstable.getEstimatedRowSize().min();
|
||||||
|
}
|
||||||
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMaxRowCompactedSize()
|
public long getMaxRowCompactedSize()
|
||||||
{
|
{
|
||||||
return maxRowCompactedSize;
|
long max = 0;
|
||||||
|
for (SSTableReader sstable : ssTables_)
|
||||||
|
{
|
||||||
|
if (sstable.getEstimatedRowSize().max() > max)
|
||||||
|
max = sstable.getEstimatedRowSize().max();
|
||||||
|
}
|
||||||
|
return max;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getMeanRowCompactedSize()
|
public long getMeanRowCompactedSize()
|
||||||
{
|
{
|
||||||
if (rowsCompactedCount > 0)
|
long sum = 0;
|
||||||
return rowsCompactedTotalSize / rowsCompactedCount;
|
long count = 0;
|
||||||
else
|
for (SSTableReader sstable : ssTables_)
|
||||||
return 0L;
|
{
|
||||||
|
if (sstable.getEstimatedRowSize().median() > 0)
|
||||||
|
{
|
||||||
|
sum += sstable.getEstimatedRowSize().median();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return count > 0 ? sum / count : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ColumnFamilyStore createColumnFamilyStore(String table, String columnFamily)
|
public static ColumnFamilyStore createColumnFamilyStore(String table, String columnFamily)
|
||||||
|
|
@ -414,7 +419,7 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
||||||
// when all the memtables have been written, including for indexes, mark the flush in the commitlog header.
|
// when all the memtables have been written, including for indexes, mark the flush in the commitlog header.
|
||||||
// a second executor makes sure the onMemtableFlushes get called in the right order,
|
// a second executor makes sure the onMemtableFlushes get called in the right order,
|
||||||
// while keeping the wait-for-flush (future.get) out of anything latency-sensitive.
|
// while keeping the wait-for-flush (future.get) out of anything latency-sensitive.
|
||||||
return commitLogUpdater_.submit(new WrappedRunnable()
|
return postFlushExecutor_.submit(new WrappedRunnable()
|
||||||
{
|
{
|
||||||
public void runMayThrow() throws InterruptedException, IOException
|
public void runMayThrow() throws InterruptedException, IOException
|
||||||
{
|
{
|
||||||
|
|
@ -1295,7 +1300,12 @@ public class ColumnFamilyStore implements ColumnFamilyStoreMBean
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return commitLogUpdater_.submit(runnable);
|
return postFlushExecutor_.submit(runnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Future<?> submitPostFlush(Runnable runnable)
|
||||||
|
{
|
||||||
|
return postFlushExecutor_.submit(runnable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getBloomFilterFalsePositives()
|
public long getBloomFilterFalsePositives()
|
||||||
|
|
|
||||||
|
|
@ -348,9 +348,6 @@ public class CompactionManager implements CompactionManagerMBean
|
||||||
|
|
||||||
writer.append(row);
|
writer.append(row);
|
||||||
totalkeysWritten++;
|
totalkeysWritten++;
|
||||||
|
|
||||||
long rowsize = writer.getFilePointer() - prevpos;
|
|
||||||
cfs.addToCompactedRowStats(rowsize);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,129 @@
|
||||||
|
/**
|
||||||
|
* 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.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import org.apache.cassandra.db.filter.QueryPath;
|
||||||
|
import org.apache.cassandra.db.filter.QueryFilter;
|
||||||
|
import org.apache.cassandra.io.sstable.SSTable;
|
||||||
|
import org.apache.cassandra.io.sstable.Descriptor;
|
||||||
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
|
import org.apache.cassandra.utils.EstimatedHistogram;
|
||||||
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
|
import org.apache.cassandra.service.StorageService;
|
||||||
|
|
||||||
|
import static com.google.common.base.Charsets.UTF_8;
|
||||||
|
|
||||||
|
public class StatisticsTable
|
||||||
|
{
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(StatisticsTable.class);
|
||||||
|
public static final String STATISTICS_CF = "Statistics";
|
||||||
|
public static final byte[] ROWSIZE_SC = "RowSize".getBytes(UTF_8);
|
||||||
|
public static final byte[] COLUMNCOUNT_SC = "ColumnCount".getBytes(UTF_8);
|
||||||
|
|
||||||
|
private static DecoratedKey decorate(byte[] key)
|
||||||
|
{
|
||||||
|
return StorageService.getPartitioner().decorateKey(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void persistSSTableStatistics(Descriptor desc, EstimatedHistogram rowsizes, EstimatedHistogram columncounts) throws IOException
|
||||||
|
{
|
||||||
|
String filename = getRelativePath(desc.filenameFor(SSTable.COMPONENT_DATA));
|
||||||
|
if (isTemporary(filename) || desc.ksname.equals(Table.SYSTEM_TABLE))
|
||||||
|
return;
|
||||||
|
long[] rowbuckets = rowsizes.getBucketOffsets();
|
||||||
|
long[] rowvalues = rowsizes.get(false);
|
||||||
|
long[] columnbuckets = columncounts.getBucketOffsets();
|
||||||
|
long[] columnvalues = columncounts.get(false);
|
||||||
|
RowMutation rm = new RowMutation(Table.SYSTEM_TABLE, filename.getBytes(UTF_8));
|
||||||
|
for (int i=0; i<rowbuckets.length; i++)
|
||||||
|
{
|
||||||
|
QueryPath path = new QueryPath(STATISTICS_CF, ROWSIZE_SC, FBUtilities.toByteArray(rowbuckets[i]));
|
||||||
|
rm.add(path, FBUtilities.toByteArray(rowvalues[i]), new TimestampClock(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
for (int i=0; i<columnbuckets.length; i++)
|
||||||
|
{
|
||||||
|
QueryPath path = new QueryPath(STATISTICS_CF, COLUMNCOUNT_SC, FBUtilities.toByteArray(columnbuckets[i]));
|
||||||
|
rm.add(path, FBUtilities.toByteArray(columnvalues[i]), new TimestampClock(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
rm.apply();
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Recorded SSTable statistics for " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deleteSSTableStatistics(String filepath) throws IOException
|
||||||
|
{
|
||||||
|
String filename = getRelativePath(filepath);
|
||||||
|
RowMutation rm = new RowMutation(Table.SYSTEM_TABLE, filename.getBytes(UTF_8));
|
||||||
|
QueryPath path = new QueryPath(STATISTICS_CF);
|
||||||
|
rm.delete(path, new TimestampClock(System.currentTimeMillis()));
|
||||||
|
rm.apply();
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Deleted SSTable statistics for " + filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] getSSTableStatistics(String filepath, byte[] superCol) throws IOException
|
||||||
|
{
|
||||||
|
long[] rv;
|
||||||
|
String filename = getRelativePath(filepath);
|
||||||
|
QueryPath path = new QueryPath(STATISTICS_CF);
|
||||||
|
QueryFilter filter = QueryFilter.getNamesFilter(decorate(filename.getBytes(UTF_8)), path, superCol);
|
||||||
|
ColumnFamily cf = Table.open(Table.SYSTEM_TABLE).getColumnFamilyStore(STATISTICS_CF).getColumnFamily(filter);
|
||||||
|
if (cf == null)
|
||||||
|
return new long[0];
|
||||||
|
IColumn scolumn = cf.getColumn(superCol);
|
||||||
|
rv = new long[scolumn.getSubColumns().size()];
|
||||||
|
int i = 0;
|
||||||
|
for (IColumn col : scolumn.getSubColumns()) {
|
||||||
|
rv[i] = ByteBuffer.wrap(col.value()).getLong();
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long [] getSSTableRowSizeStatistics(String filename) throws IOException
|
||||||
|
{
|
||||||
|
return getSSTableStatistics(filename, ROWSIZE_SC);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long [] getSSTableColumnCountStatistics(String filename) throws IOException
|
||||||
|
{
|
||||||
|
return getSSTableStatistics(filename, COLUMNCOUNT_SC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getRelativePath(String filename)
|
||||||
|
{
|
||||||
|
for (String prefix : DatabaseDescriptor.getAllDataFileLocations())
|
||||||
|
{
|
||||||
|
if (filename.startsWith(prefix))
|
||||||
|
return filename.substring(prefix.length());
|
||||||
|
}
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isTemporary(String filename)
|
||||||
|
{
|
||||||
|
return filename.contains("-" + SSTable.TEMPFILE_MARKER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -25,4 +25,6 @@ public abstract class AbstractCompactedRow
|
||||||
public abstract void update(MessageDigest digest);
|
public abstract void update(MessageDigest digest);
|
||||||
|
|
||||||
public abstract boolean isEmpty();
|
public abstract boolean isEmpty();
|
||||||
|
|
||||||
|
public abstract int columnCount();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -151,6 +151,11 @@ public class LazilyCompactedRow extends AbstractCompactedRow implements IIterabl
|
||||||
return Iterators.filter(iter, Predicates.notNull());
|
return Iterators.filter(iter, Predicates.notNull());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int columnCount()
|
||||||
|
{
|
||||||
|
return columnCount;
|
||||||
|
}
|
||||||
|
|
||||||
private class LazyColumnIterator extends ReducingIterator<IColumn, IColumn>
|
private class LazyColumnIterator extends ReducingIterator<IColumn, IColumn>
|
||||||
{
|
{
|
||||||
ColumnFamily container = emptyColumnFamily.cloneMeShallow();
|
ColumnFamily container = emptyColumnFamily.cloneMeShallow();
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ public class PrecompactedRow extends AbstractCompactedRow
|
||||||
private static Logger logger = LoggerFactory.getLogger(PrecompactedRow.class);
|
private static Logger logger = LoggerFactory.getLogger(PrecompactedRow.class);
|
||||||
|
|
||||||
private final DataOutputBuffer buffer;
|
private final DataOutputBuffer buffer;
|
||||||
|
private int columnCount = 0;
|
||||||
|
|
||||||
public PrecompactedRow(DecoratedKey key, DataOutputBuffer buffer)
|
public PrecompactedRow(DecoratedKey key, DataOutputBuffer buffer)
|
||||||
{
|
{
|
||||||
|
|
@ -61,7 +62,7 @@ public class PrecompactedRow extends AbstractCompactedRow
|
||||||
ColumnFamily cfPurged = major ? ColumnFamilyStore.removeDeleted(cf, gcBefore) : cf;
|
ColumnFamily cfPurged = major ? ColumnFamilyStore.removeDeleted(cf, gcBefore) : cf;
|
||||||
if (cfPurged == null)
|
if (cfPurged == null)
|
||||||
return;
|
return;
|
||||||
ColumnFamily.serializer().serializeWithIndexes(cfPurged, buffer);
|
columnCount = ColumnFamily.serializer().serializeWithIndexes(cfPurged, buffer);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -69,6 +70,7 @@ public class PrecompactedRow extends AbstractCompactedRow
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
rows.get(0).echoData(buffer);
|
rows.get(0).echoData(buffer);
|
||||||
|
columnCount = rows.get(0).getColumnCount();
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
@ -92,4 +94,9 @@ public class PrecompactedRow extends AbstractCompactedRow
|
||||||
{
|
{
|
||||||
return buffer.getLength() == 0;
|
return buffer.getLength() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int columnCount()
|
||||||
|
{
|
||||||
|
return columnCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.dht.IPartitioner;
|
import org.apache.cassandra.dht.IPartitioner;
|
||||||
import org.apache.cassandra.io.util.FileUtils;
|
import org.apache.cassandra.io.util.FileUtils;
|
||||||
|
import org.apache.cassandra.db.StatisticsTable;
|
||||||
|
import org.apache.cassandra.utils.EstimatedHistogram;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is built on top of the SequenceFile. It stores
|
* This class is built on top of the SequenceFile. It stores
|
||||||
|
|
@ -61,6 +63,8 @@ public abstract class SSTable
|
||||||
public static final String TEMPFILE_MARKER = "tmp";
|
public static final String TEMPFILE_MARKER = "tmp";
|
||||||
|
|
||||||
public static List<String> components = Collections.unmodifiableList(Arrays.asList(COMPONENT_FILTER, COMPONENT_INDEX, COMPONENT_DATA));
|
public static List<String> components = Collections.unmodifiableList(Arrays.asList(COMPONENT_FILTER, COMPONENT_INDEX, COMPONENT_DATA));
|
||||||
|
protected EstimatedHistogram estimatedRowSize = new EstimatedHistogram(130);
|
||||||
|
protected EstimatedHistogram estimatedColumnCount = new EstimatedHistogram(112);
|
||||||
|
|
||||||
protected SSTable(String filename, IPartitioner partitioner)
|
protected SSTable(String filename, IPartitioner partitioner)
|
||||||
{
|
{
|
||||||
|
|
@ -75,6 +79,16 @@ public abstract class SSTable
|
||||||
this.partitioner = partitioner;
|
this.partitioner = partitioner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EstimatedHistogram getEstimatedRowSize()
|
||||||
|
{
|
||||||
|
return estimatedRowSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public EstimatedHistogram getEstimatedColumnCount()
|
||||||
|
{
|
||||||
|
return estimatedColumnCount;
|
||||||
|
}
|
||||||
|
|
||||||
public IPartitioner getPartitioner()
|
public IPartitioner getPartitioner()
|
||||||
{
|
{
|
||||||
return partitioner;
|
return partitioner;
|
||||||
|
|
@ -119,6 +133,7 @@ public abstract class SSTable
|
||||||
FileUtils.deleteWithConfirm(new File(SSTable.indexFilename(dataFilename)));
|
FileUtils.deleteWithConfirm(new File(SSTable.indexFilename(dataFilename)));
|
||||||
FileUtils.deleteWithConfirm(new File(SSTable.filterFilename(dataFilename)));
|
FileUtils.deleteWithConfirm(new File(SSTable.filterFilename(dataFilename)));
|
||||||
FileUtils.deleteWithConfirm(new File(SSTable.compactedFilename(dataFilename)));
|
FileUtils.deleteWithConfirm(new File(SSTable.compactedFilename(dataFilename)));
|
||||||
|
StatisticsTable.deleteSSTableStatistics(dataFilename);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
catch (IOException e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -26,11 +26,13 @@ import java.lang.ref.Reference;
|
||||||
|
|
||||||
import com.google.common.base.Function;
|
import com.google.common.base.Function;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
|
import org.apache.cassandra.db.StatisticsTable;
|
||||||
import org.apache.cassandra.io.util.BufferedRandomAccessFile;
|
import org.apache.cassandra.io.util.BufferedRandomAccessFile;
|
||||||
import org.apache.cassandra.io.util.SegmentedFile;
|
import org.apache.cassandra.io.util.SegmentedFile;
|
||||||
import org.apache.cassandra.utils.BloomFilter;
|
import org.apache.cassandra.utils.BloomFilter;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
import org.apache.cassandra.utils.Pair;
|
import org.apache.cassandra.utils.Pair;
|
||||||
|
import org.apache.cassandra.utils.EstimatedHistogram;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
@ -140,6 +142,22 @@ public class SSTableReader extends SSTable implements Comparable<SSTableReader>
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void loadStatistics(Descriptor desc) throws IOException
|
||||||
|
{
|
||||||
|
// skip loading stats for the system table, or we will infinitely recurse
|
||||||
|
if (desc.ksname.equals(Table.SYSTEM_TABLE))
|
||||||
|
return;
|
||||||
|
if (logger.isDebugEnabled())
|
||||||
|
logger.debug("Load statistics for " + desc);
|
||||||
|
long[] rowsizes = StatisticsTable.getSSTableRowSizeStatistics(desc.filenameFor(SSTable.COMPONENT_DATA));
|
||||||
|
long[] colcounts = StatisticsTable.getSSTableColumnCountStatistics(desc.filenameFor(SSTable.COMPONENT_DATA));
|
||||||
|
if (rowsizes.length > 0)
|
||||||
|
{
|
||||||
|
estimatedRowSize = new EstimatedHistogram(rowsizes);
|
||||||
|
estimatedColumnCount = new EstimatedHistogram(colcounts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static SSTableReader open(String dataFileName) throws IOException
|
public static SSTableReader open(String dataFileName) throws IOException
|
||||||
{
|
{
|
||||||
return open(Descriptor.fromFilename(dataFileName));
|
return open(Descriptor.fromFilename(dataFileName));
|
||||||
|
|
@ -191,17 +209,38 @@ public class SSTableReader extends SSTable implements Comparable<SSTableReader>
|
||||||
sstable.load(false);
|
sstable.load(false);
|
||||||
sstable.loadBloomFilter();
|
sstable.loadBloomFilter();
|
||||||
}
|
}
|
||||||
|
sstable.loadStatistics(desc);
|
||||||
|
|
||||||
return sstable;
|
return sstable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SSTableReader(Descriptor desc,
|
||||||
|
IPartitioner partitioner,
|
||||||
|
SegmentedFile ifile,
|
||||||
|
SegmentedFile dfile,
|
||||||
|
IndexSummary indexSummary,
|
||||||
|
BloomFilter bloomFilter,
|
||||||
|
long maxDataAge)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
super(desc, partitioner);
|
||||||
|
this.maxDataAge = maxDataAge;
|
||||||
|
|
||||||
|
|
||||||
|
this.ifile = ifile;
|
||||||
|
this.dfile = dfile;
|
||||||
|
this.indexSummary = indexSummary;
|
||||||
|
this.bf = bloomFilter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a RowIndexedReader which already has its state initialized (by SSTableWriter).
|
* Open a RowIndexedReader which already has its state initialized (by SSTableWriter).
|
||||||
*/
|
*/
|
||||||
static SSTableReader internalOpen(Descriptor desc, IPartitioner partitioner, SegmentedFile ifile, SegmentedFile dfile, IndexSummary isummary, BloomFilter bf, long maxDataAge) throws IOException
|
static SSTableReader internalOpen(Descriptor desc, IPartitioner partitioner, SegmentedFile ifile, SegmentedFile dfile, IndexSummary isummary, BloomFilter bf, long maxDataAge, EstimatedHistogram rowsize,
|
||||||
|
EstimatedHistogram columncount) throws IOException
|
||||||
{
|
{
|
||||||
assert desc != null && partitioner != null && ifile != null && dfile != null && isummary != null && bf != null;
|
assert desc != null && partitioner != null && ifile != null && dfile != null && isummary != null && bf != null;
|
||||||
return new SSTableReader(desc, partitioner, ifile, dfile, isummary, bf, maxDataAge);
|
return new SSTableReader(desc, partitioner, ifile, dfile, isummary, bf, maxDataAge, rowsize, columncount);
|
||||||
}
|
}
|
||||||
|
|
||||||
SSTableReader(Descriptor desc,
|
SSTableReader(Descriptor desc,
|
||||||
|
|
@ -210,7 +249,9 @@ public class SSTableReader extends SSTable implements Comparable<SSTableReader>
|
||||||
SegmentedFile dfile,
|
SegmentedFile dfile,
|
||||||
IndexSummary indexSummary,
|
IndexSummary indexSummary,
|
||||||
BloomFilter bloomFilter,
|
BloomFilter bloomFilter,
|
||||||
long maxDataAge)
|
long maxDataAge,
|
||||||
|
EstimatedHistogram rowsize,
|
||||||
|
EstimatedHistogram columncount)
|
||||||
throws IOException
|
throws IOException
|
||||||
{
|
{
|
||||||
super(desc, partitioner);
|
super(desc, partitioner);
|
||||||
|
|
@ -221,6 +262,8 @@ public class SSTableReader extends SSTable implements Comparable<SSTableReader>
|
||||||
this.dfile = dfile;
|
this.dfile = dfile;
|
||||||
this.indexSummary = indexSummary;
|
this.indexSummary = indexSummary;
|
||||||
this.bf = bloomFilter;
|
this.bf = bloomFilter;
|
||||||
|
estimatedRowSize = rowsize;
|
||||||
|
estimatedColumnCount = columncount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTrackedBy(SSTableTracker tracker)
|
public void setTrackedBy(SSTableTracker tracker)
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,9 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.apache.cassandra.config.DatabaseDescriptor;
|
import org.apache.cassandra.config.DatabaseDescriptor;
|
||||||
import org.apache.cassandra.db.ColumnFamily;
|
import org.apache.cassandra.db.ColumnFamily;
|
||||||
|
import org.apache.cassandra.db.ColumnFamilyStore;
|
||||||
import org.apache.cassandra.db.DecoratedKey;
|
import org.apache.cassandra.db.DecoratedKey;
|
||||||
|
import org.apache.cassandra.db.StatisticsTable;
|
||||||
import org.apache.cassandra.dht.IPartitioner;
|
import org.apache.cassandra.dht.IPartitioner;
|
||||||
import org.apache.cassandra.io.AbstractCompactedRow;
|
import org.apache.cassandra.io.AbstractCompactedRow;
|
||||||
import org.apache.cassandra.io.util.BufferedRandomAccessFile;
|
import org.apache.cassandra.io.util.BufferedRandomAccessFile;
|
||||||
|
|
@ -34,6 +36,7 @@ import org.apache.cassandra.io.util.SegmentedFile;
|
||||||
import org.apache.cassandra.service.StorageService;
|
import org.apache.cassandra.service.StorageService;
|
||||||
import org.apache.cassandra.utils.BloomFilter;
|
import org.apache.cassandra.utils.BloomFilter;
|
||||||
import org.apache.cassandra.utils.FBUtilities;
|
import org.apache.cassandra.utils.FBUtilities;
|
||||||
|
import org.apache.cassandra.utils.WrappedRunnable;
|
||||||
|
|
||||||
public class SSTableWriter extends SSTable
|
public class SSTableWriter extends SSTable
|
||||||
{
|
{
|
||||||
|
|
@ -83,6 +86,8 @@ public class SSTableWriter extends SSTable
|
||||||
long currentPosition = beforeAppend(row.key);
|
long currentPosition = beforeAppend(row.key);
|
||||||
FBUtilities.writeShortByteArray(row.key.key, dataFile);
|
FBUtilities.writeShortByteArray(row.key.key, dataFile);
|
||||||
row.write(dataFile);
|
row.write(dataFile);
|
||||||
|
estimatedRowSize.add(dataFile.getFilePointer() - currentPosition);
|
||||||
|
estimatedColumnCount.add(row.columnCount());
|
||||||
afterAppend(row.key, currentPosition);
|
afterAppend(row.key, currentPosition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -94,7 +99,7 @@ public class SSTableWriter extends SSTable
|
||||||
long sizePosition = dataFile.getFilePointer();
|
long sizePosition = dataFile.getFilePointer();
|
||||||
dataFile.writeLong(-1);
|
dataFile.writeLong(-1);
|
||||||
// write out row data
|
// write out row data
|
||||||
ColumnFamily.serializer().serializeWithIndexes(cf, dataFile);
|
int columnCount = ColumnFamily.serializer().serializeWithIndexes(cf, dataFile);
|
||||||
// seek back and write the row size (not including the size Long itself)
|
// seek back and write the row size (not including the size Long itself)
|
||||||
long endPosition = dataFile.getFilePointer();
|
long endPosition = dataFile.getFilePointer();
|
||||||
dataFile.seek(sizePosition);
|
dataFile.seek(sizePosition);
|
||||||
|
|
@ -102,6 +107,8 @@ public class SSTableWriter extends SSTable
|
||||||
// finally, reset for next row
|
// finally, reset for next row
|
||||||
dataFile.seek(endPosition);
|
dataFile.seek(endPosition);
|
||||||
afterAppend(decoratedKey, startPosition);
|
afterAppend(decoratedKey, startPosition);
|
||||||
|
estimatedRowSize.add(endPosition - startPosition);
|
||||||
|
estimatedColumnCount.add(columnCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void append(DecoratedKey decoratedKey, byte[] value) throws IOException
|
public void append(DecoratedKey decoratedKey, byte[] value) throws IOException
|
||||||
|
|
@ -128,12 +135,21 @@ public class SSTableWriter extends SSTable
|
||||||
dataFile.close(); // calls force
|
dataFile.close(); // calls force
|
||||||
|
|
||||||
// remove the 'tmp' marker from all components
|
// remove the 'tmp' marker from all components
|
||||||
Descriptor newdesc = rename(desc);
|
final Descriptor newdesc = rename(desc);
|
||||||
|
|
||||||
|
Runnable runnable = new WrappedRunnable()
|
||||||
|
{
|
||||||
|
protected void runMayThrow() throws IOException
|
||||||
|
{
|
||||||
|
StatisticsTable.persistSSTableStatistics(newdesc, estimatedRowSize, estimatedColumnCount);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ColumnFamilyStore.submitPostFlush(runnable);
|
||||||
|
|
||||||
// finalize in-memory state for the reader
|
// finalize in-memory state for the reader
|
||||||
SegmentedFile ifile = iwriter.builder.complete(newdesc.filenameFor(SSTable.COMPONENT_INDEX));
|
SegmentedFile ifile = iwriter.builder.complete(newdesc.filenameFor(SSTable.COMPONENT_INDEX));
|
||||||
SegmentedFile dfile = dbuilder.complete(newdesc.filenameFor(SSTable.COMPONENT_DATA));
|
SegmentedFile dfile = dbuilder.complete(newdesc.filenameFor(SSTable.COMPONENT_DATA));
|
||||||
SSTableReader sstable = SSTableReader.internalOpen(newdesc, partitioner, ifile, dfile, iwriter.summary, iwriter.bf, maxDataAge);
|
SSTableReader sstable = SSTableReader.internalOpen(newdesc, partitioner, ifile, dfile, iwriter.summary, iwriter.bf, maxDataAge, estimatedRowSize, estimatedColumnCount);
|
||||||
iwriter = null;
|
iwriter = null;
|
||||||
dbuilder = null;
|
dbuilder = null;
|
||||||
return sstable;
|
return sstable;
|
||||||
|
|
|
||||||
|
|
@ -25,27 +25,58 @@ public class EstimatedHistogram
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This series starts at 1 and grows by 1.2 each time (rounding down and removing duplicates). It goes from 1
|
* The series of values to which the counts in `buckets` correspond:
|
||||||
* to around 30M, which will give us timing resolution from microseconds to 30 seconds, with less precision
|
* 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, 18, 22, etc.
|
||||||
* as the numbers get larger.
|
* Thus, a `buckets` of [0, 0, 1, 10] would mean we had seen one value of 3 and 10 values of 4.
|
||||||
|
*
|
||||||
|
* The series starts at 1 and grows by 1.2 each time (rounding and removing duplicates). It goes from 1
|
||||||
|
* to around 36M by default (creating 90+1 buckets), which will give us timing resolution from microseconds to
|
||||||
|
* 36 seconds, with less precision as the numbers get larger.
|
||||||
*/
|
*/
|
||||||
private static final long[] bucketOffsets = {
|
private long[] bucketOffsets;
|
||||||
1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 15, 18, 22, 26, 31, 38, 46, 55, 66, 79, 95, 114, 137, 164, 197, 237, 284, 341, 410, 492, 590,
|
private int numBuckets;
|
||||||
708, 850, 1020, 1224, 1469, 1763, 2116, 2539, 3047, 3657, 4388, 5266, 6319, 7583, 9100, 10920, 13104, 15725, 18870, 22644,
|
|
||||||
27173, 32608, 39130, 46956, 56347, 67617, 81140, 97368, 116842, 140210, 168252, 201903, 242283, 290740, 348888, 418666,
|
|
||||||
502400, 602880, 723456, 868147, 1041776, 1250132, 1500158, 1800190, 2160228, 2592274, 3110728, 3732874, 4479449, 5375339,
|
|
||||||
6450407, 7740489, 9288586, 11146304, 13375565, 16050678, 19260813, 23112976, 27735572, 33282686
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final int numBuckets = bucketOffsets.length + 1;
|
|
||||||
|
|
||||||
final AtomicLongArray buckets;
|
final AtomicLongArray buckets;
|
||||||
|
|
||||||
public EstimatedHistogram()
|
public EstimatedHistogram()
|
||||||
{
|
{
|
||||||
|
makeOffsets(90);
|
||||||
buckets = new AtomicLongArray(numBuckets);
|
buckets = new AtomicLongArray(numBuckets);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public EstimatedHistogram(int bucketCount)
|
||||||
|
{
|
||||||
|
makeOffsets(bucketCount);
|
||||||
|
buckets = new AtomicLongArray(numBuckets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EstimatedHistogram(long[] bucketData)
|
||||||
|
{
|
||||||
|
makeOffsets(bucketData.length - 1);
|
||||||
|
buckets = new AtomicLongArray(bucketData);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void makeOffsets(int size)
|
||||||
|
{
|
||||||
|
bucketOffsets = new long[size];
|
||||||
|
long last = 1;
|
||||||
|
bucketOffsets[0] = last;
|
||||||
|
for(int i = 1; i < size; i++)
|
||||||
|
{
|
||||||
|
long next = Math.round(last * 1.2);
|
||||||
|
if (next == last)
|
||||||
|
next++;
|
||||||
|
bucketOffsets[i] = next;
|
||||||
|
last = next;
|
||||||
|
}
|
||||||
|
numBuckets = bucketOffsets.length + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long[] getBucketOffsets()
|
||||||
|
{
|
||||||
|
return bucketOffsets;
|
||||||
|
}
|
||||||
|
|
||||||
public void add(long n)
|
public void add(long n)
|
||||||
{
|
{
|
||||||
int index = Arrays.binarySearch(bucketOffsets, n);
|
int index = Arrays.binarySearch(bucketOffsets, n);
|
||||||
|
|
@ -74,4 +105,40 @@ public class EstimatedHistogram
|
||||||
|
|
||||||
return rv;
|
return rv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long min()
|
||||||
|
{
|
||||||
|
for (int i = 0; i < numBuckets; i++)
|
||||||
|
{
|
||||||
|
if (buckets.get(i) > 0)
|
||||||
|
return bucketOffsets[i == 0 ? 0 : i - 1];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long max()
|
||||||
|
{
|
||||||
|
for (int i = numBuckets - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (buckets.get(i) > 0)
|
||||||
|
return bucketOffsets[i == 0 ? 0 : i - 1];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long median()
|
||||||
|
{
|
||||||
|
long max = 0;
|
||||||
|
long median = 0;
|
||||||
|
for (int i = 0; i < numBuckets; i++)
|
||||||
|
{
|
||||||
|
if (max < 1 || buckets.get(i) > max)
|
||||||
|
{
|
||||||
|
max = buckets.get(i);
|
||||||
|
if (max > 0)
|
||||||
|
median = bucketOffsets[i == 0 ? 0 : i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return median;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -110,4 +110,22 @@ public class SSTableReaderTest extends CleanupHelper
|
||||||
assert sstable.getPosition(dk, SSTableReader.Operator.EQ) == -1;
|
assert sstable.getPosition(dk, SSTableReader.Operator.EQ) == -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPersistentStatistics() throws IOException, ExecutionException, InterruptedException
|
||||||
|
{
|
||||||
|
|
||||||
|
Table table = Table.open("Keyspace1");
|
||||||
|
ColumnFamilyStore store = table.getColumnFamilyStore("Standard1");
|
||||||
|
|
||||||
|
for (int j = 0; j < 100; j += 2)
|
||||||
|
{
|
||||||
|
byte[] key = String.valueOf(j).getBytes();
|
||||||
|
RowMutation rm = new RowMutation("Keyspace1", key);
|
||||||
|
rm.add(new QueryPath("Standard1", null, "0".getBytes()), new byte[0], new TimestampClock(j));
|
||||||
|
rm.apply();
|
||||||
|
}
|
||||||
|
store.forceBlockingFlush();
|
||||||
|
assert store.getMaxRowCompactedSize() != 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,20 +31,23 @@ public class EstimatedHistogramTest
|
||||||
EstimatedHistogram histogram = new EstimatedHistogram();
|
EstimatedHistogram histogram = new EstimatedHistogram();
|
||||||
|
|
||||||
histogram.add(0L);
|
histogram.add(0L);
|
||||||
assertEquals(1, histogram.get(true)[0]);
|
assertEquals(1, histogram.get(false)[0]);
|
||||||
|
|
||||||
histogram.add(33282687);
|
histogram.add(33282687);
|
||||||
assertEquals(1, histogram.get(true)[histogram.buckets.length()-1]);
|
assertEquals(1, histogram.get(false)[histogram.buckets.length()-1]);
|
||||||
|
|
||||||
histogram.add(1);
|
histogram.add(1);
|
||||||
assertEquals(1, histogram.get(true)[1]);
|
assertEquals(1, histogram.get(false)[1]);
|
||||||
|
|
||||||
histogram.add(9);
|
histogram.add(9);
|
||||||
assertEquals(1, histogram.get(true)[8]);
|
assertEquals(1, histogram.get(false)[8]);
|
||||||
|
|
||||||
histogram.add(23);
|
histogram.add(20);
|
||||||
histogram.add(24);
|
histogram.add(21);
|
||||||
histogram.add(25);
|
histogram.add(22);
|
||||||
assertEquals(3, histogram.get(true)[13]);
|
assertEquals(3, histogram.get(false)[13]);
|
||||||
|
assertEquals(1, histogram.min());
|
||||||
|
assertEquals(25109160, histogram.max());
|
||||||
|
assertEquals(20, histogram.median());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue