Skip to content

Commit 1db6138

Browse files
CNDB-13830: Fix KD-tree metrics test (#1778)
The test for KD-tree posting list was meant to run a query that performs skips and then verifies if the skip metric has been bumped up. Unfortunately there were several problems with it: * the query didn't do skips because the clause intersection got optimized out by the SAI optimizer * the test for the metric didn't inspect the metric value, but only checked if the metric was updated; so essentially it boiled down to checking if the index was used * the metric value incorrectly recorded queries with no skips, because the histogram was configured to not allow zeroes. In addition, the usage of index is dependent on the version of the optimizer, so that made the test behave differently between DC and EC index version. Now that we disable the optimizer for this test, that difference is gone. Fixes riptano/cndb#13830 --------- Co-authored-by: ekaterinadimitrova2 <[email protected]>
1 parent ae1c14c commit 1db6138

File tree

6 files changed

+47
-12
lines changed

6 files changed

+47
-12
lines changed

src/java/org/apache/cassandra/config/CassandraRelevantProperties.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ public enum CassandraRelevantProperties
369369
/** Whether to validate terms that will be SAI indexed at the coordinator */
370370
SAI_VALIDATE_TERMS_AT_COORDINATOR("cassandra.sai.validate_terms_at_coordinator", "true"),
371371

372+
/** Whether to optimize query plans */
373+
SAI_QUERY_OPTIMIZATION_LEVEL("cassandra.sai.query_optimization_level", "1"),
374+
372375
/** Whether vector type only allows float vectors. True by default. **/
373376
VECTOR_FLOAT_ONLY("cassandra.float_only_vectors", "true"),
374377
/** Enables use of vector type. True by default. **/

src/java/org/apache/cassandra/index/sai/metrics/TableQueryMetrics.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,12 @@ public PerQueryMetrics(TableMetadata table)
120120
sstablesHit = Metrics.histogram(createMetricName("SSTableIndexesHit"), false);
121121
segmentsHit = Metrics.histogram(createMetricName("IndexSegmentsHit"), false);
122122

123-
kdTreePostingsSkips = Metrics.histogram(createMetricName("KDTreePostingsSkips"), false);
123+
kdTreePostingsSkips = Metrics.histogram(createMetricName("KDTreePostingsSkips"), true);
124124

125125
kdTreePostingsNumPostings = Metrics.histogram(createMetricName("KDTreePostingsNumPostings"), false);
126126
kdTreePostingsDecodes = Metrics.histogram(createMetricName("KDTreePostingsDecodes"), false);
127127

128-
postingsSkips = Metrics.histogram(createMetricName("PostingsSkips"), false);
128+
postingsSkips = Metrics.histogram(createMetricName("PostingsSkips"), true);
129129
postingsDecodes = Metrics.histogram(createMetricName("PostingsDecodes"), false);
130130

131131
partitionReads = Metrics.histogram(createMetricName("PartitionReads"), false);

src/java/org/apache/cassandra/index/sai/plan/QueryController.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import com.google.common.collect.ArrayListMultimap;
2828
import com.google.common.collect.Lists;
2929
import com.google.common.collect.Multimap;
30+
import org.apache.cassandra.config.CassandraRelevantProperties;
3031
import org.apache.cassandra.index.FeatureNeedsIndexRebuildException;
3132
import org.apache.cassandra.index.sai.disk.format.Version;
3233
import org.slf4j.Logger;
@@ -106,7 +107,7 @@ public class QueryController implements Plan.Executor, Plan.CostEstimator
106107
* Note: the config is not final to simplify testing.
107108
*/
108109
@VisibleForTesting
109-
public static int QUERY_OPT_LEVEL = Integer.getInteger("cassandra.sai.query.optimization.level", 1);
110+
public static int QUERY_OPT_LEVEL = CassandraRelevantProperties.SAI_QUERY_OPTIMIZATION_LEVEL.getInt();
110111

111112
private final ColumnFamilyStore cfs;
112113
private final ReadCommand command;

test/unit/org/apache/cassandra/index/sai/metrics/AbstractMetricsTest.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ protected void waitForIndexCompaction(String keyspace, String table, String inde
5656
}, 60, TimeUnit.SECONDS);
5757
}
5858

59-
protected void waitForVerifyHistogram(ObjectName name, long count)
59+
protected void waitForHistogramCountEquals(ObjectName name, long count)
6060
{
6161
waitForAssert(() -> {
6262
try
@@ -70,6 +70,22 @@ protected void waitForVerifyHistogram(ObjectName name, long count)
7070
}, 10, TimeUnit.SECONDS);
7171
}
7272

73+
protected void waitForHistogramMeanBetween(ObjectName name, double min, double max)
74+
{
75+
waitForAssert(() -> {
76+
try
77+
{
78+
double mean = (double) jmxConnection.getAttribute(name, "Mean");
79+
assertTrue("Median " + mean + " is between " + min + " and " + max, mean >= min && mean <= max);
80+
}
81+
catch (Throwable ex)
82+
{
83+
throw Throwables.unchecked(ex);
84+
}
85+
}, 10, TimeUnit.SECONDS);
86+
}
87+
88+
7389
protected void waitForGreaterThanZero(ObjectName name)
7490
{
7591
waitForAssert(() -> {

test/unit/org/apache/cassandra/index/sai/metrics/IndexMetricsTest.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public void testMetricsThroughWriteLifecycle()
110110
assertEquals(0L, getMetricValue(objectName("DiskUsedBytes", KEYSPACE, table, index, "IndexMetrics")));
111111
assertEquals(0L, getMetricValue(objectName("CompactionCount", KEYSPACE, table, index, "IndexMetrics")));
112112

113-
waitForVerifyHistogram(objectName("MemtableIndexFlushCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 0);
113+
waitForHistogramCountEquals(objectName("MemtableIndexFlushCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 0);
114114

115115
flush(KEYSPACE, table);
116116

@@ -122,7 +122,8 @@ public void testMetricsThroughWriteLifecycle()
122122
assertTrue((Long)getMetricValue(objectName("DiskUsedBytes", KEYSPACE, table, index, "IndexMetrics")) > 0);
123123
assertEquals(0L, getMetricValue(objectName("CompactionCount", KEYSPACE, table, index, "IndexMetrics")));
124124

125-
waitForVerifyHistogram(objectName("MemtableIndexFlushCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1);
125+
waitForHistogramCountEquals(objectName("MemtableIndexFlushCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1);
126+
waitForHistogramMeanBetween(objectName("MemtableIndexFlushCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1.0, 1000000.0);
126127

127128
compact(KEYSPACE, table);
128129

@@ -139,7 +140,8 @@ public void testMetricsThroughWriteLifecycle()
139140
assertTrue((Long)getMetricValue(objectName("DiskUsedBytes", KEYSPACE, table, index, "IndexMetrics")) > 0);
140141
assertEquals(1L, getMetricValue(objectName("CompactionCount", KEYSPACE, table, index, "IndexMetrics")));
141142

142-
waitForVerifyHistogram(objectName("CompactionSegmentCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1);
143+
waitForHistogramCountEquals(objectName("CompactionSegmentCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1);
144+
waitForHistogramMeanBetween(objectName("CompactionSegmentCellsPerSecond", KEYSPACE, table, index, "IndexMetrics"), 1.0, 1000000.0);
143145
}
144146

145147
private void assertIndexQueryCount(String index, long expectedCount)

test/unit/org/apache/cassandra/index/sai/metrics/QueryMetricsTest.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import com.datastax.driver.core.ResultSet;
3030
import org.apache.cassandra.index.sai.disk.format.Version;
31+
import org.apache.cassandra.index.sai.plan.QueryController;
3132
import org.apache.cassandra.metrics.CassandraMetricsRegistry;
3233

3334
import static org.apache.cassandra.index.sai.metrics.TableQueryMetrics.TABLE_QUERY_METRIC_TYPE;
@@ -225,6 +226,11 @@ public void testKDTreeQueryMetricsWithSingleIndex()
225226
@Test
226227
public void testKDTreePostingsQueryMetricsWithSingleIndex()
227228
{
229+
// Turn off the query optimizer.
230+
// We need to do this in order to remove unpredictability of query plans, so that we get consistent metrics.
231+
// We don't want the query optimizer to eliminate the use of indexes.
232+
QueryController.QUERY_OPT_LEVEL = 0;
233+
228234
String table = "test_kdtree_postings_metrics_through_write_lifecycle";
229235
String v1Index = "test_kdtree_postings_metrics_through_write_lifecycle_v1_index";
230236
String v2Index = "test_kdtree_postings_metrics_through_write_lifecycle_v2_index";
@@ -255,13 +261,20 @@ public void testKDTreePostingsQueryMetricsWithSingleIndex()
255261
assertEquals(rowsWritten, actualRows);
256262

257263
assertTrue(((Number) getMetricValue(objectName("NumPostings", keyspace, table, v1Index, "KDTreePostings"))).longValue() > 0);
264+
waitForHistogramCountEquals(objectNameNoIndex("KDTreePostingsNumPostings", keyspace, table, PER_QUERY_METRIC_TYPE), 1);
265+
waitForHistogramMeanBetween(objectNameNoIndex("KDTreePostingsNumPostings", keyspace, table, PER_QUERY_METRIC_TYPE), 1.0, 1.0);
258266

259-
waitForVerifyHistogram(objectNameNoIndex("KDTreePostingsNumPostings", keyspace, table, PER_QUERY_METRIC_TYPE), 1);
267+
// the query performed no skips, but the metric should be updated because the index was used, so we should get
268+
// a single entry in the histogram with 0 skips
269+
waitForHistogramCountEquals(objectNameNoIndex("KDTreePostingsSkips", keyspace, table, PER_QUERY_METRIC_TYPE), 1);
270+
waitForHistogramMeanBetween(objectNameNoIndex("KDTreePostingsSkips", keyspace, table, PER_QUERY_METRIC_TYPE), 0.0, 0.0);
260271

261-
// V2 index is very selective, so it should lead the union merge process, causing V1 index to be not used at all.
262-
execute("SELECT id1 FROM " + keyspace + "." + table + " WHERE v1 >= 0 AND v1 <= 1000 AND v2 = '5' ALLOW FILTERING");
272+
// V2 index is very selective, so it should lead the union merge process, causing V1 index to skip/advance
273+
execute("SELECT id1 FROM " + keyspace + "." + table + " WHERE v1 >= 0 AND v1 <= 1000 AND v2 IN ('5', '10', '20', '22') ALLOW FILTERING");
263274

264-
waitForVerifyHistogram(objectNameNoIndex("KDTreePostingsSkips", keyspace, table, PER_QUERY_METRIC_TYPE), 2);
275+
// we expect exactly 4 skips from this query, but the mean will be 2.0 because of the previous query which had 0 skips
276+
waitForHistogramCountEquals(objectNameNoIndex("KDTreePostingsSkips", keyspace, table, PER_QUERY_METRIC_TYPE), 2);
277+
waitForHistogramMeanBetween(objectNameNoIndex("KDTreePostingsSkips", keyspace, table, PER_QUERY_METRIC_TYPE), 1.99, 2.01);
265278
}
266279

267280
@Test
@@ -353,7 +366,7 @@ public void testKDTreePartitionsReadAndRowsFiltered()
353366

354367
//TODO This needs revisiting with STAR-903 because we are now reading rows one at a time
355368
waitForEquals(objectNameNoIndex("TotalPartitionReads", keyspace, table, TABLE_QUERY_METRIC_TYPE), Version.current() == Version.AA ? 2 : 3);
356-
waitForVerifyHistogram(objectNameNoIndex("RowsFiltered", keyspace, table, PER_QUERY_METRIC_TYPE), 1);
369+
waitForHistogramCountEquals(objectNameNoIndex("RowsFiltered", keyspace, table, PER_QUERY_METRIC_TYPE), 1);
357370
waitForEquals(objectNameNoIndex("TotalRowsFiltered", keyspace, table, TABLE_QUERY_METRIC_TYPE), 3);
358371
}
359372

0 commit comments

Comments
 (0)