From c4d1bf30e98b9a958c744d6dd07f9b78e5825fb1 Mon Sep 17 00:00:00 2001 From: Pedro Gordo Date: Thu, 9 Apr 2026 14:20:06 +0100 Subject: [PATCH] Update Config when properties are changed via JMX For snitch/dynamic_snitch, update Config fields inside updateProximityInternal so the Settings Virtual Table reflects the runtime state. For FQL and audit logging, resolve values directly from FullQueryLogger and AuditLogManager runtime instances, which revert to yaml defaults when disabled. Patch by Pedro Gordo; reviewed by Stefan Miklosovic, Marcus Eriksson for CASSANDRA-17736 --- .../cassandra/config/DatabaseDescriptor.java | 20 +++++++ .../cassandra/db/virtual/SettingsTable.java | 55 +++++++++++++++---- .../cassandra/service/StorageService.java | 13 +++-- .../service/StorageServiceServerTest.java | 13 +++++ 4 files changed, 87 insertions(+), 14 deletions(-) diff --git a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java index 59ffb9f02afa..25b4c269904b 100644 --- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java +++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java @@ -4100,6 +4100,26 @@ public static void setDynamicBadnessThreshold(double dynamicBadnessThreshold) conf.dynamic_snitch_badness_threshold = dynamicBadnessThreshold; } + public static boolean isDynamicSnitch() + { + return conf.dynamic_snitch; + } + + public static void setDynamicSnitch(boolean enabled) + { + conf.dynamic_snitch = enabled; + } + + public static void setEndpointSnitchClassName(String endpointSnitch) + { + conf.endpoint_snitch = endpointSnitch; + } + + public static void setNodeProximityClassName(String nodeProximity) + { + conf.node_proximity = nodeProximity; + } + public static EncryptionOptions.ServerEncryptionOptions getInternodeMessagingEncyptionOptions() { return conf.server_encryption_options; diff --git a/src/java/org/apache/cassandra/db/virtual/SettingsTable.java b/src/java/org/apache/cassandra/db/virtual/SettingsTable.java index 5a89b2342773..fe2f34c083f3 100644 --- a/src/java/org/apache/cassandra/db/virtual/SettingsTable.java +++ b/src/java/org/apache/cassandra/db/virtual/SettingsTable.java @@ -28,6 +28,8 @@ import org.yaml.snakeyaml.introspector.Property; +import org.apache.cassandra.audit.AuditLogManager; +import org.apache.cassandra.audit.AuditLogOptions; import org.apache.cassandra.config.CassandraRelevantProperties; import org.apache.cassandra.config.Config; import org.apache.cassandra.config.DatabaseDescriptor; @@ -39,6 +41,8 @@ import org.apache.cassandra.db.DecoratedKey; import org.apache.cassandra.db.marshal.UTF8Type; import org.apache.cassandra.dht.LocalPartitioner; +import org.apache.cassandra.fql.FullQueryLogger; +import org.apache.cassandra.fql.FullQueryLoggerOptions; import org.apache.cassandra.schema.TableMetadata; import org.apache.cassandra.service.ClientWarn; import org.apache.cassandra.utils.JsonUtils; @@ -48,6 +52,12 @@ public final class SettingsTable extends AbstractVirtualTable { private static final String NAME = "name"; private static final String VALUE = "value"; + private static final String FQL_PREFIX = "full_query_logging_options"; + private static final String AUDIT_PREFIX = "audit_logging_options"; + private static final Map FQL_PROPERTIES = ImmutableMap.copyOf( + Properties.defaultLoader().flatten(FullQueryLoggerOptions.class)); + private static final Map AUDIT_PROPERTIES = ImmutableMap.copyOf( + Properties.defaultLoader().flatten(AuditLogOptions.class)); public static final Map BACKWARDS_COMPATIBLE_NAMES = ImmutableMap.copyOf(getBackwardsCompatibleNames()); protected static final Map PROPERTIES = ImmutableMap.copyOf(getProperties()); @@ -94,6 +104,31 @@ public DataSet data() return result; } + /** + * Returns the value to use for the given property. For FQL and audit logging properties, + * resolves from the current runtime options (which revert to yaml defaults when disabled) + * so the virtual table reflects the actual active state rather than the Config object. + */ + private Object resolveValue(Property prop) + { + String name = prop.getName(); + if (name.startsWith(FQL_PREFIX + ".")) + { + String subName = name.substring(FQL_PREFIX.length() + 1); + Property subProp = FQL_PROPERTIES.get(subName); + if (subProp != null) + return subProp.get(FullQueryLogger.instance.getFullQueryLoggerOptions()); + } + if (name.startsWith(AUDIT_PREFIX + ".")) + { + String subName = name.substring(AUDIT_PREFIX.length() + 1); + Property subProp = AUDIT_PROPERTIES.get(subName); + if (subProp != null) + return subProp.get(AuditLogManager.instance.getAuditLogOptions()); + } + return prop.get(config); + } + @VisibleForTesting String getValue(Property prop) { @@ -101,7 +136,7 @@ String getValue(Property prop) if (maybeCredential != null) return maybeCredential.redactedValue(); - Object value = prop.get(config); + Object value = resolveValue(prop); if (value == null) return null; @@ -189,7 +224,7 @@ private static Map getBackwardsCompatibleNames() { Map names = new HashMap<>(); // Names that dont match yaml - names.put("audit_logging_options_logger", "audit_logging_options.logger.class_name"); + names.put(AUDIT_PREFIX + "_logger", AUDIT_PREFIX + ".logger.class_name"); names.put("server_encryption_options_client_auth", "server_encryption_options.require_client_auth"); names.put("server_encryption_options_endpoint_verification", "server_encryption_options.require_endpoint_verification"); names.put("server_encryption_options_legacy_ssl_storage_port", "server_encryption_options.legacy_ssl_storage_port_enabled"); @@ -202,14 +237,14 @@ private static Map getBackwardsCompatibleNames() // matching names - names.put("audit_logging_options_audit_logs_dir", "audit_logging_options.audit_logs_dir"); - names.put("audit_logging_options_enabled", "audit_logging_options.enabled"); - names.put("audit_logging_options_excluded_categories", "audit_logging_options.excluded_categories"); - names.put("audit_logging_options_excluded_keyspaces", "audit_logging_options.excluded_keyspaces"); - names.put("audit_logging_options_excluded_users", "audit_logging_options.excluded_users"); - names.put("audit_logging_options_included_categories", "audit_logging_options.included_categories"); - names.put("audit_logging_options_included_keyspaces", "audit_logging_options.included_keyspaces"); - names.put("audit_logging_options_included_users", "audit_logging_options.included_users"); + names.put(AUDIT_PREFIX + "_audit_logs_dir", AUDIT_PREFIX + ".audit_logs_dir"); + names.put(AUDIT_PREFIX + "_enabled", AUDIT_PREFIX + ".enabled"); + names.put(AUDIT_PREFIX + "_excluded_categories", AUDIT_PREFIX + ".excluded_categories"); + names.put(AUDIT_PREFIX + "_excluded_keyspaces", AUDIT_PREFIX + ".excluded_keyspaces"); + names.put(AUDIT_PREFIX + "_excluded_users", AUDIT_PREFIX + ".excluded_users"); + names.put(AUDIT_PREFIX + "_included_categories", AUDIT_PREFIX + ".included_categories"); + names.put(AUDIT_PREFIX + "_included_keyspaces", AUDIT_PREFIX + ".included_keyspaces"); + names.put(AUDIT_PREFIX + "_included_users", AUDIT_PREFIX + ".included_users"); names.put("server_encryption_options_algorithm", "server_encryption_options.algorithm"); names.put("server_encryption_options_cipher_suites", "server_encryption_options.cipher_suites"); names.put("server_encryption_options_enabled", "server_encryption_options.enabled"); diff --git a/src/java/org/apache/cassandra/service/StorageService.java b/src/java/org/apache/cassandra/service/StorageService.java index b7460b1be50e..668b94be06ad 100644 --- a/src/java/org/apache/cassandra/service/StorageService.java +++ b/src/java/org/apache/cassandra/service/StorageService.java @@ -4355,7 +4355,7 @@ public void setDynamicUpdateInterval(int dynamicUpdateInterval) try { - updateProximityInternal(null, true, dynamicUpdateInterval, null, null); + updateProximityInternal(null, () -> {}, true, dynamicUpdateInterval, null, null); } catch (ClassNotFoundException e) { @@ -4372,16 +4372,16 @@ public int getDynamicUpdateInterval() public void updateSnitch(String epSnitchClassName, Boolean dynamic, Integer dynamicUpdateInterval, Integer dynamicResetInterval, Double dynamicBadnessThreshold) throws ClassNotFoundException { Supplier factory = () -> new SnitchAdapter(DatabaseDescriptor.createEndpointSnitch(epSnitchClassName)); - updateProximityInternal(factory, dynamic, dynamicUpdateInterval, dynamicResetInterval, dynamicBadnessThreshold); + updateProximityInternal(factory, () -> DatabaseDescriptor.setEndpointSnitchClassName(epSnitchClassName), dynamic, dynamicUpdateInterval, dynamicResetInterval, dynamicBadnessThreshold); } public void updateNodeProximity(String npsClassName, Boolean dynamic, Integer dynamicUpdateInterval, Integer dynamicResetInterval, Double dynamicBadnessThreshold) throws ClassNotFoundException { Supplier factory = () -> DatabaseDescriptor.createProximityImpl(npsClassName); - updateProximityInternal(factory, dynamic, dynamicUpdateInterval, dynamicResetInterval, dynamicBadnessThreshold); + updateProximityInternal(factory, () -> DatabaseDescriptor.setNodeProximityClassName(npsClassName), dynamic, dynamicUpdateInterval, dynamicResetInterval, dynamicBadnessThreshold); } - private void updateProximityInternal(Supplier implSupplier, Boolean dynamic, Integer dynamicUpdateInterval, Integer dynamicResetInterval, Double dynamicBadnessThreshold) throws ClassNotFoundException + private void updateProximityInternal(Supplier implSupplier, Runnable configUpdater, Boolean dynamic, Integer dynamicUpdateInterval, Integer dynamicResetInterval, Double dynamicBadnessThreshold) throws ClassNotFoundException { // apply dynamic snitch configuration if (dynamicUpdateInterval != null) @@ -4427,6 +4427,11 @@ private void updateProximityInternal(Supplier implSupplier, Boole // point reference to the new instance DatabaseDescriptor.setNodeProximity(newProximity); + + // update Config so the Settings Virtual Table reflects the runtime state + configUpdater.run(); + if (dynamic != null) + DatabaseDescriptor.setDynamicSnitch(dynamic); } else { diff --git a/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java b/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java index 45b98c27e97c..70f2560a0dbb 100644 --- a/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java +++ b/test/unit/org/apache/cassandra/service/StorageServiceServerTest.java @@ -605,6 +605,19 @@ public void testAuditLogEnableLoggerTransitions() throws Exception StorageService.instance.disableAuditLog(); } + @Test + public void testSnitchSettersUpdateConfig() + { + DatabaseDescriptor.setDynamicSnitch(false); + Assert.assertFalse(DatabaseDescriptor.isDynamicSnitch()); + DatabaseDescriptor.setDynamicSnitch(true); + Assert.assertTrue(DatabaseDescriptor.isDynamicSnitch()); + + DatabaseDescriptor.setEndpointSnitchClassName("org.apache.cassandra.locator.SimpleSnitch"); + Assert.assertEquals("org.apache.cassandra.locator.SimpleSnitch", + DatabaseDescriptor.getRawConfig().endpoint_snitch); + } + /** Create a new AuditLogOptions instance with the log dir set appropriately to a temp dir for unit testing. */