diff --git a/CHANGES.txt b/CHANGES.txt index 1d11bbc9757b..59a9ea4fcbf9 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 4.1 + * Allow column_index_size_in_kb to be configurable through nodetool (CASSANDRA-17121) * Emit a metric for number of local read and write calls * Add non-blocking mode for CDC writes (CASSANDRA-17001) * Add guardrails framework (CASSANDRA-17147) diff --git a/src/java/org/apache/cassandra/config/Config.java b/src/java/org/apache/cassandra/config/Config.java index f08da7a27ed2..61aaf3cbc8b7 100644 --- a/src/java/org/apache/cassandra/config/Config.java +++ b/src/java/org/apache/cassandra/config/Config.java @@ -223,7 +223,7 @@ public class Config public volatile long snapshot_links_per_second = 0; /* if the size of columns or super-columns are more than this, indexing will kick in */ - public int column_index_size_in_kb = 64; + public volatile int column_index_size_in_kb = 64; public volatile int column_index_cache_size_in_kb = 2; public volatile int batch_size_warn_threshold_in_kb = 5; public volatile int batch_size_fail_threshold_in_kb = 50; diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index fa6276e121f9..fe820cdc12eb 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -1513,7 +1513,6 @@ public static int getColumnIndexSizeInKB() return conf.column_index_size_in_kb; } - @VisibleForTesting public static void setColumnIndexSize(int val) { checkValidForByteConversion(val, "column_index_size_in_kb", ByteUnit.KIBI_BYTES); diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index 8fde79b2a379..8d07784c2882 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -5860,6 +5860,18 @@ public void setCachedReplicaRowsFailThreshold(int threshold) logger.info("updated replica_filtering_protection.cached_rows_fail_threshold to {}", threshold); } + public int getColumnIndexSizeInKB() + { + return DatabaseDescriptor.getColumnIndexSizeInKB(); + } + + public void setColumnIndexSize(int columnIndexSizeInKB) + { + int oldValueInKB = DatabaseDescriptor.getColumnIndexSizeInKB(); + DatabaseDescriptor.setColumnIndexSize(columnIndexSizeInKB); + logger.info("Updated column_index_size_in_kb to {} KiB (was {} KiB)", columnIndexSizeInKB, oldValueInKB); + } + public int getColumnIndexCacheSize() { return DatabaseDescriptor.getColumnIndexCacheSizeInKB(); diff --git a/src/java/org/apache/cassandra/service/StorageServiceMBean.java b/src/java/org/apache/cassandra/service/StorageServiceMBean.java index ed31e3c47940..dcc1b1ea83f9 100644 --- a/src/java/org/apache/cassandra/service/StorageServiceMBean.java +++ b/src/java/org/apache/cassandra/service/StorageServiceMBean.java @@ -767,6 +767,11 @@ default int upgradeSSTables(String keyspaceName, boolean excludeCurrentVersion, /** Sets the number of rows cached at the coordinator before filtering/index queries fail outright. */ public void setCachedReplicaRowsFailThreshold(int threshold); + /** Returns the granularity of the collation index of rows within a partition **/ + public int getColumnIndexSizeInKB(); + /** Sets the granularity of the collation index of rows within a partition **/ + public void setColumnIndexSize(int columnIndexSizeInKB); + /** Returns the threshold for skipping the column index when caching partition info **/ public int getColumnIndexCacheSize(); /** Sets the threshold for skipping the column index when caching partition info **/ diff --git a/src/java/org/apache/cassandra/tools/NodeProbe.java b/src/java/org/apache/cassandra/tools/NodeProbe.java index 0371630e0358..9078f52b9008 100644 --- a/src/java/org/apache/cassandra/tools/NodeProbe.java +++ b/src/java/org/apache/cassandra/tools/NodeProbe.java @@ -1202,6 +1202,16 @@ public boolean isInitialized() return ssProxy.isInitialized(); } + public void setColumnIndexSize(int columnIndexSizeInKB) + { + ssProxy.setColumnIndexSize(columnIndexSizeInKB); + } + + public int getColumnIndexSizeInKB() + { + return ssProxy.getColumnIndexSizeInKB(); + } + public void setCompactionThroughput(int value) { ssProxy.setCompactionThroughputMbPerSec(value); diff --git a/src/java/org/apache/cassandra/tools/NodeTool.java b/src/java/org/apache/cassandra/tools/NodeTool.java index 3d62fde70566..dd85f10f9a62 100644 --- a/src/java/org/apache/cassandra/tools/NodeTool.java +++ b/src/java/org/apache/cassandra/tools/NodeTool.java @@ -130,6 +130,7 @@ public int execute(String... args) GcStats.class, GetAuditLog.class, GetBatchlogReplayTrottle.class, + GetColumnIndexSize.class, GetCompactionThreshold.class, GetCompactionThroughput.class, GetConcurrency.class, @@ -189,6 +190,7 @@ public int execute(String... args) SetBatchlogReplayThrottle.class, SetCacheCapacity.class, SetCacheKeysToSave.class, + SetColumnIndexSize.class, SetCompactionThreshold.class, SetCompactionThroughput.class, SetConcurrency.class, diff --git a/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java b/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java new file mode 100644 index 000000000000..806d8154f232 --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/GetColumnIndexSize.java @@ -0,0 +1,33 @@ +/* + * 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.tools.nodetool; + +import io.airlift.airline.Command; +import org.apache.cassandra.tools.NodeProbe; +import org.apache.cassandra.tools.NodeTool.NodeToolCmd; + +@Command(name = "getcolumnindexsize", description = "Print the granularity of the collation index of rows within a partition in KiB") +public class GetColumnIndexSize extends NodeToolCmd +{ + @Override + protected void execute(NodeProbe probe) + { + probe.output().out.println("Current value for column_index_size_in_kb: " + probe.getColumnIndexSizeInKB() + " KiB"); + } +} diff --git a/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java b/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java new file mode 100644 index 000000000000..85a066dfd4da --- /dev/null +++ b/src/java/org/apache/cassandra/tools/nodetool/SetColumnIndexSize.java @@ -0,0 +1,38 @@ +/* + * 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.tools.nodetool; + +import io.airlift.airline.Arguments; +import io.airlift.airline.Command; +import org.apache.cassandra.tools.NodeProbe; +import org.apache.cassandra.tools.NodeTool.NodeToolCmd; + +@Command(name = "setcolumnindexsize", description = "Set the granularity of the collation index of rows within a partition in KiB") +public class SetColumnIndexSize extends NodeToolCmd +{ + @SuppressWarnings("UnusedDeclaration") + @Arguments(title = "column_index_size_in_kb", usage = "", description = "Value in KiB", required = true) + private int columnIndexSizeInKB; + + @Override + protected void execute(NodeProbe probe) + { + probe.setColumnIndexSize(columnIndexSizeInKB); + } +} diff --git a/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java b/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java new file mode 100644 index 000000000000..67b519e8f1c2 --- /dev/null +++ b/test/unit/org/apache/cassandra/tools/nodetool/SetGetColumnIndexSizeTest.java @@ -0,0 +1,110 @@ +/* + * 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.tools.nodetool; + +import org.junit.BeforeClass; +import org.junit.Test; + +import org.apache.cassandra.cql3.CQLTester; +import org.apache.cassandra.service.StorageService; + +import static org.apache.cassandra.tools.ToolRunner.ToolResult; +import static org.apache.cassandra.tools.ToolRunner.invokeNodetool; +import static org.assertj.core.api.Assertions.assertThat; + +/** + * Tests for {@code nodetool setcolumnindexsize} and {@code nodetool getcolumnindexsize}. + */ +public class SetGetColumnIndexSizeTest extends CQLTester +{ + @BeforeClass + public static void setup() throws Exception + { + startJMXServer(); + } + + @Test + public void testNull() + { + assertSetInvalidColumnIndexSize(null, "Required parameters are missing: column_index_size_in_kb", 1); + } + + @Test + public void testPositive() + { + assertSetGetValidColumnIndexSize(7); + } + + @Test + public void testMaxValue() + { + assertSetGetValidColumnIndexSize(2097151); + } + + @Test + public void testZero() + { + assertSetGetValidColumnIndexSize(0); + } + + @Test + public void testNegative() + { + assertSetInvalidColumnIndexSize("-7", "column_index_size_in_kb must be positive value < 2097152, but was -7", 2); + } + + @Test + public void testInvalidValue() + { + assertSetInvalidColumnIndexSize("2097152", "column_index_size_in_kb must be positive value < 2097152, but was 2097152", 2); + } + + @Test + public void testUnparseable() + { + assertSetInvalidColumnIndexSize("1.2", "column_index_size_in_kb: can not convert \"1.2\" to a int", 1); + assertSetInvalidColumnIndexSize("value", "column_index_size_in_kb: can not convert \"value\" to a int", 1); + } + + private static void assertSetGetValidColumnIndexSize(int columnIndexSizeInKB) + { + ToolResult tool = invokeNodetool("setcolumnindexsize", String.valueOf(columnIndexSizeInKB)); + tool.assertOnCleanExit(); + assertThat(tool.getStdout()).isEmpty(); + + assertGetThroughput(columnIndexSizeInKB); + + assertThat(StorageService.instance.getColumnIndexSizeInKB()).isEqualTo(columnIndexSizeInKB); + } + + private static void assertSetInvalidColumnIndexSize(String columnIndexSizeInKB, String expectedErrorMessage, int expectedErrorCode) + { + ToolResult tool = columnIndexSizeInKB == null ? invokeNodetool("setcolumnindexsize") + : invokeNodetool("setcolumnindexsize", columnIndexSizeInKB); + assertThat(tool.getExitCode()).isEqualTo(expectedErrorCode); + assertThat(expectedErrorCode == 1 ? tool.getStdout() : tool.getStderr()).contains(expectedErrorMessage); + } + + private static void assertGetThroughput(int expected) + { + ToolResult tool = invokeNodetool("getcolumnindexsize"); + tool.assertOnCleanExit(); + assertThat(tool.getStdout()).contains("Current value for column_index_size_in_kb: " + expected + " KiB"); + } +}