This is an automated email from the ASF dual-hosted git repository.

marcuse pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/cassandra.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 8974fdb821 Include Level information for UnifiedCompactionStrategy in 
nodetool tablestats output
8974fdb821 is described below

commit 8974fdb821db6b72eac5a34d96fe1f78ead3c11f
Author: Alan Wang <[email protected]>
AuthorDate: Fri Nov 14 11:02:53 2025 -0800

    Include Level information for UnifiedCompactionStrategy in nodetool 
tablestats output
    
    Patch by Alan Wang, reviewed by Jyothsna Konisa and marcuse for 
CASSANDRA-20820
---
 CHANGES.txt                                        |   1 +
 .../org/apache/cassandra/db/ColumnFamilyStore.java |  36 +++++
 .../cassandra/db/ColumnFamilyStoreMBean.java       |  42 ++++++
 .../db/compaction/CompactionStrategyManager.java   | 157 +++++++++++++++++++++
 .../db/compaction/UnifiedCompactionStrategy.java   |  28 +++-
 .../cassandra/tools/nodetool/stats/StatsTable.java |   7 +
 .../tools/nodetool/stats/TableStatsHolder.java     |  33 +++++
 .../tools/nodetool/stats/TableStatsPrinter.java    |  16 +++
 .../test/ColumnFamilyStoreMBeansTest.java          | 133 +++++++++++++++++
 .../compaction/CompactionStrategyManagerTest.java  |  56 ++++++++
 10 files changed, 508 insertions(+), 1 deletion(-)

diff --git a/CHANGES.txt b/CHANGES.txt
index a78c0876ad..c748e17c1f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 5.1
+ * Include Level information for UnifiedCompactionStrategy in nodetool 
tablestats output (CASSANDRA-20820)
  * Support low-overhead async profiling (CASSANDRA-20854)
  * Minor perf optimizations around memtable put logic (CASSANDRA-21088)
  * When level compaction validates its table properties, it used the wrong 
default value for sstable_size_in_mb which allowed properties that would later 
be rejected at runtime (CASSANDRA-20570)
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java 
b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
index d93cf1b2c7..a60d4a01a7 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStore.java
@@ -3021,6 +3021,42 @@ public <T> T withAllSSTables(final OperationType 
operationType, Function<Lifecyc
         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()
     {
diff --git a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java 
b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
index 33cd6ebbc2..91bd7f07e1 100644
--- a/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
+++ b/src/java/org/apache/cassandra/db/ColumnFamilyStoreMBean.java
@@ -277,6 +277,48 @@ public interface ColumnFamilyStoreMBean
      */
     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.
      */
diff --git 
a/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java 
b/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java
index 04c386ea46..d73a3ee6e7 100644
--- a/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java
+++ b/src/java/org/apache/cassandra/db/compaction/CompactionStrategyManager.java
@@ -32,6 +32,8 @@ import java.util.Objects;
 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;
@@ -55,6 +57,7 @@ import org.apache.cassandra.db.commitlog.CommitLogPosition;
 import org.apache.cassandra.db.commitlog.IntervalSet;
 import org.apache.cassandra.db.compaction.AbstractStrategyHolder.TaskSupplier;
 import org.apache.cassandra.db.compaction.PendingRepairManager.CleanupTask;
+import org.apache.cassandra.db.compaction.UnifiedCompactionStrategy.Level;
 import org.apache.cassandra.db.lifecycle.ILifecycleTransaction;
 import org.apache.cassandra.db.lifecycle.LifecycleTransaction;
 import org.apache.cassandra.db.lifecycle.SSTableSet;
@@ -691,6 +694,73 @@ public class CompactionStrategyManager implements 
INotificationConsumer
         }
     }
 
+    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();
@@ -703,6 +773,93 @@ public class CompactionStrategyManager implements 
INotificationConsumer
         }
     }
 
+    /**
+     * 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();
diff --git 
a/src/java/org/apache/cassandra/db/compaction/UnifiedCompactionStrategy.java 
b/src/java/org/apache/cassandra/db/compaction/UnifiedCompactionStrategy.java
index 61717b5e28..f6a3fbaeb9 100644
--- a/src/java/org/apache/cassandra/db/compaction/UnifiedCompactionStrategy.java
+++ b/src/java/org/apache/cassandra/db/compaction/UnifiedCompactionStrategy.java
@@ -530,6 +530,24 @@ public class UnifiedCompactionStrategy extends 
AbstractCompactionStrategy
         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.
@@ -547,6 +565,14 @@ public class UnifiedCompactionStrategy extends 
AbstractCompactionStrategy
         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();
@@ -558,7 +584,7 @@ public class UnifiedCompactionStrategy extends 
AbstractCompactionStrategy
         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);
diff --git a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
index 7bb816ed1d..b809ca199f 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/StatsTable.java
@@ -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;
@@ -74,6 +75,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;
diff --git 
a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
index 6e05ac5e43..ab9fa5b1fa 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsHolder.java
@@ -133,6 +133,15 @@ public class TableStatsHolder implements StatsHolder
         mpTable.put("old_sstable_count", table.oldSSTableCount);
         mpTable.put("sstables_in_each_level", table.sstablesInEachLevel);
         mpTable.put("sstable_bytes_in_each_level", 
table.sstableBytesInEachLevel);
+        if (table.isUCSSstable)
+        {
+            mpTable.put("sstable_avg_token_space_in_each_level", 
table.sstableAvgTokenSpaceInEachLevel);
+            mpTable.put("sstable_max_density_threshold_in_each_level", 
table.sstableMaxDensityThresholdInEachLevel);
+            mpTable.put("sstable_avg_size_in_each_level", 
table.sstableAvgSizeInEachLevel);
+            mpTable.put("sstable_avg_density_in_each_level", 
table.sstableAvgDensityInEachLevel);
+            
mpTable.put("sstable_avg_density_max_density_threshold_ratio_in_each_level", 
table.sstableAvgDensityMaxDensityThresholdRatioInEachLevel);
+            
mpTable.put("sstable_max_density_max_density_threshold_ratio_in_each_level", 
table.sstableMaxDensityMaxDensityThresholdRatioInEachLevel);
+        }
         mpTable.put("max_sstable_size", table.maxSSTableSize);
         mpTable.put("twcs", table.twcs);
         mpTable.put("space_used_live", table.spaceUsedLive);
@@ -286,6 +295,18 @@ public class TableStatsHolder implements StatsHolder
                     }
                 }
 
+                addUCSMetric(statsTable, 
statsTable.sstableAvgTokenSpaceInEachLevel, table.getPerLevelAvgTokenSpace());
+
+                addUCSMetric(statsTable, 
statsTable.sstableMaxDensityThresholdInEachLevel, 
table.getPerLevelMaxDensityThreshold());
+
+                addUCSMetric(statsTable, statsTable.sstableAvgSizeInEachLevel, 
table.getPerLevelAvgSize());
+
+                addUCSMetric(statsTable, 
statsTable.sstableAvgDensityInEachLevel, table.getPerLevelAvgDensity());
+
+                addUCSMetric(statsTable, 
statsTable.sstableAvgDensityMaxDensityThresholdRatioInEachLevel, 
table.getPerLevelAvgDensityMaxDensityThresholdRatio());
+
+                addUCSMetric(statsTable, 
statsTable.sstableMaxDensityMaxDensityThresholdRatioInEachLevel, 
table.getPerLevelMaxDensityMaxDensityThresholdRatio());
+
                 if (locationCheck)
                     statsTable.isInCorrectLocation = 
!table.hasMisplacedSSTables();
 
@@ -465,6 +486,18 @@ public class TableStatsHolder implements StatsHolder
         }
     }
 
+    private void addUCSMetric(StatsTable statsTable, List<String> acc, 
double[] values)
+    {
+        if (values != null)
+        {
+            statsTable.isUCSSstable = true;
+            for (int level = 0; level < values.length; level++)
+            {
+                acc.add(String.format("%.03f", values[level]));
+            }
+        }
+    }
+
     private double getMetricMean(Object metricObject) {
         if (metricObject instanceof CassandraMetricsRegistry.JmxTimerMBean) {
             return ((CassandraMetricsRegistry.JmxTimerMBean) 
metricObject).getMean() / 1000;
diff --git 
a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java 
b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
index b500326999..9d1735dbe2 100644
--- a/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
+++ b/src/java/org/apache/cassandra/tools/nodetool/stats/TableStatsPrinter.java
@@ -100,6 +100,22 @@ public class TableStatsPrinter<T extends StatsHolder>
                                                                                
     table.sstableBytesInEachLevel) + "]");
             }
 
+            if (table.isUCSSstable)
+            {
+                out.println(indent + "Average token space for SSTables in each 
level: [" + String.join(", ",
+                        table.sstableAvgTokenSpaceInEachLevel) + "]");
+                out.println(indent + "Maximum density threshold for SSTables 
in each level: [" + String.join(", ",
+                        table.sstableMaxDensityThresholdInEachLevel) + "]");
+                out.println(indent + "Average SSTable size in each level: [" + 
String.join(", ",
+                        table.sstableAvgSizeInEachLevel) + "]");
+                out.println(indent + "Average SSTable density in each level: 
[" + String.join(", ",
+                        table.sstableAvgDensityInEachLevel) + "]");
+                out.println(indent + "Average SSTable density to max threshold 
ratio in each level: [" + String.join(", ",
+                        
table.sstableAvgDensityMaxDensityThresholdRatioInEachLevel) + "]");
+                out.println(indent + "Maximum SSTable density to max threshold 
ratio in each level: [" + String.join(", ",
+                        
table.sstableMaxDensityMaxDensityThresholdRatioInEachLevel) + "]");
+            }
+
             out.println(indent + "Space used (live): " + table.spaceUsedLive);
             out.println(indent + "Space used (total): " + 
table.spaceUsedTotal);
             out.println(indent + "Space used by snapshots (total): " + 
table.spaceUsedBySnapshotsTotal);
diff --git 
a/test/distributed/org/apache/cassandra/distributed/test/ColumnFamilyStoreMBeansTest.java
 
b/test/distributed/org/apache/cassandra/distributed/test/ColumnFamilyStoreMBeansTest.java
new file mode 100644
index 0000000000..8bbdb77a6d
--- /dev/null
+++ 
b/test/distributed/org/apache/cassandra/distributed/test/ColumnFamilyStoreMBeansTest.java
@@ -0,0 +1,133 @@
+/*
+ * 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.distributed.test;
+
+import java.io.IOException;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import org.apache.cassandra.db.ColumnFamilyStore;
+import org.apache.cassandra.db.Keyspace;
+import org.apache.cassandra.distributed.Cluster;
+
+import static org.junit.Assert.assertTrue;
+
+public class ColumnFamilyStoreMBeansTest extends TestBaseImpl
+{
+    private static Cluster CLUSTER;
+
+    @BeforeClass
+    public static void setup() throws IOException
+    {
+        CLUSTER = init(Cluster.build(1)
+                              .start());
+
+        CLUSTER.schemaChange(withKeyspace("DROP KEYSPACE %s"));
+        CLUSTER.schemaChange(withKeyspace("CREATE KEYSPACE %s WITH replication 
= {'class': 'SimpleStrategy', 'replication_factor': 1}"));
+        CLUSTER.schemaChange(withKeyspace("CREATE TABLE %s.cf (k text, c1 
text, c2 text, PRIMARY KEY (k)) WITH compaction = {'class': 
'UnifiedCompactionStrategy', 'scaling_parameters': 'L10'}"));
+
+        for (int i = 0; i < 10000; i++)
+            CLUSTER.get(1).executeInternal(withKeyspace("INSERT INTO %s.cf (k, 
c1, c2) VALUES (?, 'value1', 'value2');"), Integer.toString(i));
+
+        CLUSTER.get(1).nodetool("flush");
+    }
+
+    @AfterClass
+    public static void teardownCluster() throws Exception
+    {
+        if (CLUSTER != null)
+            CLUSTER.close();
+    }
+
+    @Test
+    public void testPerLevelAverageTokenSpace() throws Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            double[] perLevelAvgTokenSpace = cfs.getPerLevelAvgTokenSpace();
+            assertTrue(perLevelAvgTokenSpace.length > 0);
+            for (int i = 0; i < perLevelAvgTokenSpace.length; i++)
+                assertTrue(perLevelAvgTokenSpace[i] > 0);
+        });
+    }
+
+    @Test
+    public void testGetPerLevelMaxDensityThreshold() throws Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            assertTrue(cfs.getPerLevelMaxDensityThreshold().length > 0);
+        });
+    }
+
+    @Test
+    public void testGetPerLevelAvgSize() throws Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            double[] perLevelAvgSize = cfs.getPerLevelAvgSize();
+            assertTrue(perLevelAvgSize.length > 0);
+            for (int i = 0; i < perLevelAvgSize.length; i++)
+                assertTrue(perLevelAvgSize[i] > 0);
+        });
+    }
+
+    @Test
+    public void testGetPerLevelAvgDensity() throws Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            double[] perLevelAvgDensity = cfs.getPerLevelAvgDensity();
+            assertTrue(perLevelAvgDensity.length > 0);
+            for (int i = 0; i < perLevelAvgDensity.length; i++)
+                assertTrue(perLevelAvgDensity[i] > 0);
+        });
+    }
+
+    @Test
+    public void testGetPerLevelAvgDensityMaxDensityThresholdRatio() throws 
Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            double[] perLevelAvgDensityMaxDensityThresholdRatio = 
cfs.getPerLevelAvgDensityMaxDensityThresholdRatio();
+            assertTrue(perLevelAvgDensityMaxDensityThresholdRatio.length > 0);
+            for (int i = 0; i < 
perLevelAvgDensityMaxDensityThresholdRatio.length; i++)
+            {
+                assertTrue(0 <= perLevelAvgDensityMaxDensityThresholdRatio[i]);
+                assertTrue(perLevelAvgDensityMaxDensityThresholdRatio[i] < 1);
+            }
+        });
+    }
+
+    @Test
+    public void testGetPerLevelMaxDensityMaxDensityThresholdRatio() throws 
Throwable
+    {
+        CLUSTER.get(1).runOnInstance(() -> {
+            ColumnFamilyStore cfs = 
Keyspace.open(KEYSPACE).getColumnFamilyStore("cf");
+            double[] perLevelMaxDensityMaxDensityThresholdRatio = 
cfs.getPerLevelMaxDensityMaxDensityThresholdRatio();
+            assertTrue(perLevelMaxDensityMaxDensityThresholdRatio.length > 0);
+            for (int i = 0; i < 
perLevelMaxDensityMaxDensityThresholdRatio.length; i++) {
+                assertTrue(0 <= perLevelMaxDensityMaxDensityThresholdRatio[i]);
+                assertTrue(perLevelMaxDensityMaxDensityThresholdRatio[i] < 1);
+            }
+        });
+    }
+}
diff --git 
a/test/unit/org/apache/cassandra/db/compaction/CompactionStrategyManagerTest.java
 
b/test/unit/org/apache/cassandra/db/compaction/CompactionStrategyManagerTest.java
index 7907b1dd30..b52b550187 100644
--- 
a/test/unit/org/apache/cassandra/db/compaction/CompactionStrategyManagerTest.java
+++ 
b/test/unit/org/apache/cassandra/db/compaction/CompactionStrategyManagerTest.java
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -434,6 +435,61 @@ public class CompactionStrategyManagerTest extends 
CassandraTestBase
                 Collections.emptyMap()), 
CompactionStrategyManager.TWCS_BUCKET_COUNT_MAX));
     }
 
+    @Test
+    public void testAverageArrayFinalizer()
+    {
+        CompactionStrategyManager.CompactionStatsMetricsData data = new 
CompactionStrategyManager.CompactionStatsMetricsData();
+        Random random = new Random();
+        data.numberOfLevels = random.nextInt(32);
+
+        for (int i = 0; i < data.numberOfLevels; i++) {
+            data.sum[i] = random.nextInt(250);
+            data.count[i] = random.nextInt(10);
+        }
+
+        double[] res = CompactionStrategyManager.averageArrayFinalizer(data);
+        assertEquals(res.length, data.numberOfLevels);
+        for (int i = 0; i < data.numberOfLevels; i++)
+            if (data.count[i] == 0)
+                assertEquals(0, res[i], 0.0);
+            else
+                assertEquals(data.sum[i] / data.count[i], res[i], 0.1);
+    }
+
+    @Test
+    public void testMaxArrayFinalizer()
+    {
+        CompactionStrategyManager.CompactionStatsMetricsData data = new 
CompactionStrategyManager.CompactionStatsMetricsData();
+        Random random = new Random();
+        data.numberOfLevels = random.nextInt(32);
+
+        for (int i = 0; i < data.numberOfLevels; i++)
+            data.max[i] = random.nextInt(250);
+
+        double[] res = CompactionStrategyManager.maxArrayFinalizer(data);
+        assertEquals(res.length, data.numberOfLevels);
+        for (int i = 0; i < data.numberOfLevels; i++)
+            assertEquals(data.max[i], res[i], 0.0);
+    }
+
+    @Test
+    public void testRatioArrayFinalizer()
+    {
+        CompactionStrategyManager.CompactionStatsMetricsData data = new 
CompactionStrategyManager.CompactionStatsMetricsData();
+        Random random = new Random();
+        data.numberOfLevels = random.nextInt(32);
+
+        for (int i = 0; i < data.numberOfLevels; i++) {
+            data.sum[i] = random.nextInt(250);
+            data.max[i] = 250 + random.nextInt(500);
+        }
+
+        double[] res = CompactionStrategyManager.ratioArrayFinalizer(data);
+        assertEquals(res.length, data.numberOfLevels);
+        for (int i = 0; i < data.numberOfLevels; i++)
+            assertEquals(data.sum[i] / data.max[i], res[i], 0.1);
+    }
+
     private MockCFS createJBODMockCFS(int disks)
     {
         // Create #disks data directories to simulate JBOD


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to