Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/java/org/apache/cassandra/db/ColumnFamilyStore.java
Original file line number Diff line number Diff line change
Expand Up @@ -2994,6 +2994,42 @@ public long[] getPerLevelSizeBytes()
return compactionStrategyManager.getPerLevelSizeBytes();
}

@Override
public double[] getPerLevelAvgTokenSpace()
{
return compactionStrategyManager.getPerLevelAvgTokenSpace();
}

@Override
public double[] getPerLevelMaxDensityThreshold()
{
return compactionStrategyManager.getPerLevelMaxDensityThreshold();
}

@Override
public double[] getPerLevelAvgSize()
{
return compactionStrategyManager.getPerLevelAvgSize();
}

@Override
public double[] getPerLevelAvgDensity()
{
return compactionStrategyManager.getPerLevelAvgDensity();
}

@Override
public double[] getPerLevelAvgDensityMaxDensityThresholdRatio()
{
return compactionStrategyManager.getPerLevelAvgDensityMaxDensityThresholdRatio();
}

@Override
public double[] getPerLevelMaxDensityMaxDensityThresholdRatio()
{
return compactionStrategyManager.getPerLevelMaxDensityMaxDensityThresholdRatio();
}

@Override
public boolean isLeveledCompaction()
{
Expand Down
42 changes: 42 additions & 0 deletions src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,48 @@ public List<String> importNewSSTables(Set<String> srcPaths,
*/
public long[] getPerLevelSizeBytes();

/**
* @return average of sstable covered token spaces in each level.
* null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelAvgTokenSpace();

/**
* @return the maximum density each level is allowed to hold.
* null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelMaxDensityThreshold();

/**
* @return the average size of sstables in each level.
* null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelAvgSize();

/**
* @return the average density of sstables in each level.
* null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelAvgDensity();

/**
* @return the ratio of avg density to the maximum density threshold of that level
* in each level. null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelAvgDensityMaxDensityThresholdRatio();

/**
* @return the ratio of maximum density to the maximum density threshold of that level
* in each level. null unless unified compaction strategy is used.
* array index corresponds to level(int[0] is for level 0, ...).
*/
public double[] getPerLevelMaxDensityMaxDensityThresholdRatio();

/**
* @return true if the table is using LeveledCompactionStrategy. false otherwise.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
Expand Down Expand Up @@ -80,6 +82,7 @@
import org.apache.cassandra.schema.CompactionParams;
import org.apache.cassandra.service.ActiveRepairService;
import org.apache.cassandra.utils.TimeUUID;
import org.apache.cassandra.db.compaction.UnifiedCompactionStrategy.Level;

import static org.apache.cassandra.db.compaction.AbstractStrategyHolder.GroupedSSTableContainer;

Expand Down Expand Up @@ -690,6 +693,73 @@ public long[] getPerLevelSizeBytes()
}
}

public double[] getPerLevelAvgTokenSpace()
{
return computeUCSMetric(
data -> {
data.sum[data.levelIndex] += data.sstable.tokenSpaceCoverage();
data.count[data.levelIndex]++;
},
CompactionStrategyManager::averageArrayFinalizer
);
}

public double[] getPerLevelMaxDensityThreshold()
{
return computeUCSMetric(
data -> {
data.max[data.levelIndex] = Math.max(data.max[data.levelIndex], data.level.max);
},
CompactionStrategyManager::maxArrayFinalizer
);
}

public double[] getPerLevelAvgSize()
{
return computeUCSMetric(
data -> {
data.sum[data.levelIndex] += data.sstable.onDiskLength();
data.count[data.levelIndex]++;
},
CompactionStrategyManager::averageArrayFinalizer
);
}

public double[] getPerLevelAvgDensity()
{
return computeUCSMetric(
data -> {
data.sum[data.levelIndex] += data.strategy.getDensity(data.sstable);
data.count[data.levelIndex]++;
},
CompactionStrategyManager::averageArrayFinalizer
);
}

public double[] getPerLevelAvgDensityMaxDensityThresholdRatio()
{
double[] avgDensity = getPerLevelAvgDensity();
if (avgDensity == null)
return null;

double[] maxThreshold = getPerLevelMaxDensityThreshold();
double[] res = new double[avgDensity.length];
for (int i = 0; i < avgDensity.length; i++)
res[i] = avgDensity[i] / maxThreshold[i];
return res;
}

public double[] getPerLevelMaxDensityMaxDensityThresholdRatio()
{
return computeUCSMetric(
data -> {
data.sum[data.levelIndex] = Math.max(data.sum[data.levelIndex], data.strategy.getDensity(data.sstable));
data.max[data.levelIndex] = Math.max(data.max[data.levelIndex], data.level.max);
},
CompactionStrategyManager::ratioArrayFinalizer
);
}

public boolean isLeveledCompaction()
{
readLock.lock();
Expand All @@ -702,6 +772,93 @@ public boolean isLeveledCompaction()
}
}

/**
* Data class for accumulating UCS metrics computation state.
* Holds intermediate values during metric calculation across all strategies and levels.
*/
@VisibleForTesting
static class CompactionStatsMetricsData
{
final double[] sum = new double[UnifiedCompactionStrategy.MAX_LEVELS];
final int[] count = new int[UnifiedCompactionStrategy.MAX_LEVELS];
final double[] max = new double[UnifiedCompactionStrategy.MAX_LEVELS];
int numberOfLevels = 0;

int levelIndex;
Level level;
SSTableReader sstable;
UnifiedCompactionStrategy strategy;
}

/**
* Generic helper to compute UCS metrics across all strategies and levels.
* Reduces code duplication for per-level metric calculations.
*
* @param accumulator processes each sstable and updates the metrics data state
* @param finalizer computes the final result array from the accumulated metrics data
* @return computed metric array, one value per level, or null if not using UCS
*/
private double[] computeUCSMetric(Consumer<CompactionStatsMetricsData> accumulator, Function<CompactionStatsMetricsData, double[]> finalizer)
{
readLock.lock();
try
{
if (repaired.first() instanceof UnifiedCompactionStrategy)
{
CompactionStatsMetricsData data = new CompactionStatsMetricsData();

for (AbstractCompactionStrategy strategy : getAllStrategies())
{
UnifiedCompactionStrategy ucsStrategy = (UnifiedCompactionStrategy) strategy;
List<Level> levels = ucsStrategy.getLevelsSnapshot();

data.numberOfLevels = Math.max(data.numberOfLevels, levels.size());
data.strategy = ucsStrategy;

for (int i = 0; i < levels.size(); i++)
{
data.levelIndex = i;
data.level = levels.get(i);
for (SSTableReader sstable : levels.get(i).getSSTables())
{
data.sstable = sstable;
accumulator.accept(data);
}
}
}

return finalizer.apply(data);
}
return null;
}
finally {
readLock.unlock();
}
}

@VisibleForTesting
static double[] averageArrayFinalizer(CompactionStatsMetricsData data)
{
double[] res = new double[data.numberOfLevels];
for (int i = 0; i < data.numberOfLevels; i++)
res[i] = data.count[i] == 0 ? 0 : data.sum[i] / data.count[i];
return res;
}

@VisibleForTesting
static double[] maxArrayFinalizer(CompactionStatsMetricsData data)
{
return Arrays.copyOf(data.max, data.numberOfLevels);
}

@VisibleForTesting
static double[] ratioArrayFinalizer(CompactionStatsMetricsData data) {
double[] res = new double[data.numberOfLevels];
for (int i = 0; i < data.numberOfLevels; i++)
res[i] = data.sum[i] / data.max[i];
return res;
}

public int[] getSSTableCountPerTWCSBucket()
{
readLock.lock();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,24 @@ List<Level> getLevels()
return getLevels(getSSTables(), UnifiedCompactionStrategy::isSuitableForCompaction);
}

/**
* @return a list of the levels in the compaction hierarchy, that also includes SSTables that
* are currently undergoing compaction. This is used only for table stats so we can have a consistent
* snapshot of the levels.
*/
List<Level> getLevelsSnapshot()
{
Set<SSTableReader> sstables = getSSTables();
List<SSTableReader> suitable = new ArrayList<>(sstables.size());
for (SSTableReader rdr : sstables)
{
if (isSuitableForCompaction(rdr))
suitable.add(rdr);
}

return formLevels(suitable);
}

/**
* Groups the sstables passed in into levels. This is used by the strategy to determine
* new compactions, and by external tools to analyze the strategy decisions.
Expand All @@ -546,6 +564,14 @@ public List<Level> getLevels(Collection<SSTableReader> sstables,
return formLevels(suitable);
}

/**
* Returns the density of the SSTable
*/
public double getDensity(SSTableReader sstable)
{
return shardManager.density(sstable);
}

private List<Level> formLevels(List<SSTableReader> suitable)
{
maybeUpdateShardManager();
Expand All @@ -557,7 +583,7 @@ private List<Level> formLevels(List<SSTableReader> suitable)
Level level = new Level(controller, index, 0, maxDensity);
for (SSTableReader candidate : suitable)
{
final double density = shardManager.density(candidate);
final double density = getDensity(candidate);
if (density < level.max)
{
level.add(candidate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class StatsTable
public String tableName;
public boolean isIndex;
public boolean isLeveledSstable = false;
public boolean isUCSSstable = false;
public Object sstableCount;
public Object oldSSTableCount;
public Long maxSSTableSize;
Expand Down Expand Up @@ -72,6 +73,12 @@ public class StatsTable
public long maximumTombstonesPerSliceLastFiveMinutes;
public List<String> sstablesInEachLevel = new ArrayList<>();
public List<String> sstableBytesInEachLevel = new ArrayList<>();
public List<String> sstableAvgTokenSpaceInEachLevel = new ArrayList<>();
public List<String> sstableMaxDensityThresholdInEachLevel = new ArrayList<>();
public List<String> sstableAvgSizeInEachLevel = new ArrayList<>();
public List<String> sstableAvgDensityInEachLevel = new ArrayList<>();
public List<String> sstableAvgDensityMaxDensityThresholdRatioInEachLevel = new ArrayList<>();
public List<String> sstableMaxDensityMaxDensityThresholdRatioInEachLevel = new ArrayList<>();
public int[] sstableCountPerTWCSBucket = null;
public Boolean isInCorrectLocation = null; // null: option not active
public double droppableTombstoneRatio;
Expand Down
Loading