Repository: cassandra Updated Branches: refs/heads/cassandra-2.1 728f677b0 -> f67b7a477
Auto reload GossipingPropertyFileSnitch config Patch by Danield Shelepov, reviewed by Tyler Hobbs for CASSANDRA-5897 Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/f67b7a47 Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/f67b7a47 Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/f67b7a47 Branch: refs/heads/cassandra-2.1 Commit: f67b7a477ae08fe7c8be2bb61eeecb0a7cc55e62 Parents: 728f677 Author: Tyler Hobbs <ty...@datastax.com> Authored: Thu Mar 6 11:22:47 2014 -0600 Committer: Tyler Hobbs <ty...@datastax.com> Committed: Thu Mar 6 11:22:47 2014 -0600 ---------------------------------------------------------------------- CHANGES.txt | 1 + .../org/apache/cassandra/locator/Ec2Snitch.java | 2 +- .../locator/GossipingPropertyFileSnitch.java | 104 ++++++++++++++++--- .../cassandra/locator/PropertyFileSnitch.java | 2 +- .../cassandra/locator/SnitchProperties.java | 10 +- test/conf/cassandra-rackdc.properties.mod | 17 +++ .../GossipingPropertyFileSnitchTest.java | 59 +++++++++++ .../YamlFileNetworkTopologySnitchTest.java | 2 +- 8 files changed, 175 insertions(+), 22 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/CHANGES.txt ---------------------------------------------------------------------- diff --git a/CHANGES.txt b/CHANGES.txt index b933bad..af7f2fd 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,4 +1,5 @@ 2.1.0-beta2 + * Auto reload GossipingPropertyFileSnitch config (CASSANDRA-5897) * Fix overflow of memtable_total_space_in_mb (CASSANDRA-6573) * Fix ABTC NPE (CASSANDRA-6692) * Allow nodetool to use a file or prompt for password (CASSANDRA-6660) http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/src/java/org/apache/cassandra/locator/Ec2Snitch.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/locator/Ec2Snitch.java b/src/java/org/apache/cassandra/locator/Ec2Snitch.java index 216a224..59eb27b 100644 --- a/src/java/org/apache/cassandra/locator/Ec2Snitch.java +++ b/src/java/org/apache/cassandra/locator/Ec2Snitch.java @@ -62,7 +62,7 @@ public class Ec2Snitch extends AbstractNetworkTopologySnitch if (ec2region.endsWith("1")) ec2region = az.substring(0, az.length() - 3); - String datacenterSuffix = SnitchProperties.get("dc_suffix", ""); + String datacenterSuffix = (new SnitchProperties()).get("dc_suffix", ""); ec2region = ec2region.concat(datacenterSuffix); logger.info("EC2Snitch using region: {}, zone: {}.", ec2region, ec2zone); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java b/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java index 83c1efe..720e804 100644 --- a/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java +++ b/src/java/org/apache/cassandra/locator/GossipingPropertyFileSnitch.java @@ -19,6 +19,7 @@ package org.apache.cassandra.locator; import java.net.InetAddress; +import java.util.concurrent.atomic.AtomicReference; import java.util.Map; import org.slf4j.Logger; @@ -29,8 +30,10 @@ import org.apache.cassandra.exceptions.ConfigurationException; import org.apache.cassandra.gms.ApplicationState; import org.apache.cassandra.gms.EndpointState; import org.apache.cassandra.gms.Gossiper; -import org.apache.cassandra.utils.FBUtilities; import org.apache.cassandra.service.StorageService; +import org.apache.cassandra.utils.FBUtilities; +import org.apache.cassandra.utils.ResourceWatcher; +import org.apache.cassandra.utils.WrappedRunnable; public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// implements IEndpointStateChangeSubscriber @@ -38,23 +41,30 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// private static final Logger logger = LoggerFactory.getLogger(GossipingPropertyFileSnitch.class); private PropertyFileSnitch psnitch; - private String myDC; - private String myRack; + + private volatile String myDC; + private volatile String myRack; + private volatile boolean preferLocal; + private AtomicReference<ReconnectableSnitchHelper> snitchHelperReference; + private volatile boolean gossipStarted; + private Map<InetAddress, Map<String, String>> savedEndpoints; - private String DEFAULT_DC = "UNKNOWN_DC"; - private String DEFAULT_RACK = "UNKNOWN_RACK"; - private final boolean preferLocal; + private static final String DEFAULT_DC = "UNKNOWN_DC"; + private static final String DEFAULT_RACK = "UNKNOWN_RACK"; + private static final int DEFAULT_REFRESH_PERIOD_IN_SECONDS = 60; + public GossipingPropertyFileSnitch() throws ConfigurationException { - myDC = SnitchProperties.get("dc", null); - myRack = SnitchProperties.get("rack", null); - if (myDC == null || myRack == null) - throw new ConfigurationException("DC or rack not found in snitch properties, check your configuration in: " + SnitchProperties.RACKDC_PROPERTY_FILENAME); + this(DEFAULT_REFRESH_PERIOD_IN_SECONDS); + } + + public GossipingPropertyFileSnitch(int refreshPeriodInSeconds) throws ConfigurationException + { + snitchHelperReference = new AtomicReference<ReconnectableSnitchHelper>(); + + reloadConfiguration(); - myDC = myDC.trim(); - myRack = myRack.trim(); - preferLocal = Boolean.parseBoolean(SnitchProperties.get("prefer_local", "false")); try { psnitch = new PropertyFileSnitch(); @@ -64,6 +74,23 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// { logger.info("Unable to load {}; compatibility mode disabled", PropertyFileSnitch.SNITCH_PROPERTIES_FILENAME); } + + try + { + FBUtilities.resourceToFile(SnitchProperties.RACKDC_PROPERTY_FILENAME); + Runnable runnable = new WrappedRunnable() + { + protected void runMayThrow() throws ConfigurationException + { + reloadConfiguration(); + } + }; + ResourceWatcher.watch(SnitchProperties.RACKDC_PROPERTY_FILENAME, runnable, refreshPeriodInSeconds * 1000); + } + catch (ConfigurationException ex) + { + logger.error("{} found, but does not look like a plain file. Will not watch it for changes", SnitchProperties.RACKDC_PROPERTY_FILENAME); + } } /** @@ -125,8 +152,55 @@ public class GossipingPropertyFileSnitch extends AbstractNetworkTopologySnitch// public void gossiperStarting() { super.gossiperStarting(); + Gossiper.instance.addLocalApplicationState(ApplicationState.INTERNAL_IP, - StorageService.instance.valueFactory.internalIP(FBUtilities.getLocalAddress().getHostAddress())); - Gossiper.instance.register(new ReconnectableSnitchHelper(this, myDC, preferLocal)); + StorageService.instance.valueFactory.internalIP(FBUtilities.getLocalAddress().getHostAddress())); + + reloadGossiperState(); + + gossipStarted = true; + } + + private void reloadConfiguration() throws ConfigurationException + { + final SnitchProperties properties = new SnitchProperties(); + + String newDc = properties.get("dc", null); + String newRack = properties.get("rack", null); + if (newDc == null || newRack == null) + throw new ConfigurationException("DC or rack not found in snitch properties, check your configuration in: " + SnitchProperties.RACKDC_PROPERTY_FILENAME); + + newDc = newDc.trim(); + newRack = newRack.trim(); + final boolean newPreferLocal = Boolean.parseBoolean(properties.get("prefer_local", "false")); + + if (myDC != newDc || myRack != newRack || (preferLocal != newPreferLocal)) + { + myDC = newDc; + myRack = newRack; + preferLocal = newPreferLocal; + + reloadGossiperState(); + + if (StorageService.instance != null) + StorageService.instance.getTokenMetadata().invalidateCachedRings(); + + if (gossipStarted) + StorageService.instance.gossipSnitchInfo(); + } + } + + private void reloadGossiperState() + { + if (Gossiper.instance != null) + { + ReconnectableSnitchHelper pendingHelper = new ReconnectableSnitchHelper(this, myDC, preferLocal); + Gossiper.instance.register(pendingHelper); + + pendingHelper = snitchHelperReference.getAndSet(pendingHelper); + if (pendingHelper != null) + Gossiper.instance.unregister(pendingHelper); + } + // else this will eventually rerun at gossiperStarting() } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java b/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java index 90dddcc..d235369 100644 --- a/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java +++ b/src/java/org/apache/cassandra/locator/PropertyFileSnitch.java @@ -73,7 +73,7 @@ public class PropertyFileSnitch extends AbstractNetworkTopologySnitch } catch (ConfigurationException ex) { - logger.debug("{} found, but does not look like a plain file. Will not watch it for changes", SNITCH_PROPERTIES_FILENAME); + logger.error("{} found, but does not look like a plain file. Will not watch it for changes", SNITCH_PROPERTIES_FILENAME); } } http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/src/java/org/apache/cassandra/locator/SnitchProperties.java ---------------------------------------------------------------------- diff --git a/src/java/org/apache/cassandra/locator/SnitchProperties.java b/src/java/org/apache/cassandra/locator/SnitchProperties.java index a8b2d03..57541d4 100644 --- a/src/java/org/apache/cassandra/locator/SnitchProperties.java +++ b/src/java/org/apache/cassandra/locator/SnitchProperties.java @@ -28,10 +28,12 @@ public class SnitchProperties { private static final Logger logger = LoggerFactory.getLogger(SnitchProperties.class); public static final String RACKDC_PROPERTY_FILENAME = "cassandra-rackdc.properties"; - private static Properties properties = new Properties(); - static + private Properties properties; + + public SnitchProperties() { + properties = new Properties(); InputStream stream = SnitchProperties.class.getClassLoader().getResourceAsStream(RACKDC_PROPERTY_FILENAME); try { @@ -49,9 +51,9 @@ public class SnitchProperties } /** - * Get a snitch property value or return null if not defined. + * Get a snitch property value or return defaultValue if not defined. */ - public static String get(String propertyName, String defaultValue) + public String get(String propertyName, String defaultValue) { return properties.getProperty(propertyName, defaultValue); } http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/test/conf/cassandra-rackdc.properties.mod ---------------------------------------------------------------------- diff --git a/test/conf/cassandra-rackdc.properties.mod b/test/conf/cassandra-rackdc.properties.mod new file mode 100644 index 0000000..457ad65 --- /dev/null +++ b/test/conf/cassandra-rackdc.properties.mod @@ -0,0 +1,17 @@ +# 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. +dc=DC2 +rack=RAC2 http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java b/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java new file mode 100644 index 0000000..9026ebf --- /dev/null +++ b/test/unit/org/apache/cassandra/locator/GossipingPropertyFileSnitchTest.java @@ -0,0 +1,59 @@ +/* + * 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.locator; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.apache.cassandra.utils.FBUtilities; +import org.junit.Test; + +/** + * Unit tests for {@link GossipingPropertyFileSnitch}. + */ +public class GossipingPropertyFileSnitchTest +{ + @Test + public void testAutoReloadConfig() throws Exception + { + String confFile = FBUtilities.resourceToFile(SnitchProperties.RACKDC_PROPERTY_FILENAME); + + final GossipingPropertyFileSnitch snitch = new GossipingPropertyFileSnitch(/*refreshPeriodInSeconds*/1); + YamlFileNetworkTopologySnitchTest.checkEndpoint(snitch, FBUtilities.getBroadcastAddress().getHostAddress(), "DC1", "RAC1"); + + final Path effectiveFile = Paths.get(confFile); + final Path backupFile = Paths.get(confFile + ".bak"); + final Path modifiedFile = Paths.get(confFile + ".mod"); + + try + { + Files.copy(effectiveFile, backupFile); + Files.copy(modifiedFile, effectiveFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + + Thread.sleep(1500); + + YamlFileNetworkTopologySnitchTest.checkEndpoint(snitch, FBUtilities.getBroadcastAddress().getHostAddress(), "DC2", "RAC2"); + } + finally + { + Files.copy(backupFile, effectiveFile, java.nio.file.StandardCopyOption.REPLACE_EXISTING); + Files.delete(backupFile); + } + } +} http://git-wip-us.apache.org/repos/asf/cassandra/blob/f67b7a47/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java ---------------------------------------------------------------------- diff --git a/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java b/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java index be1c24b..af1a7e9 100644 --- a/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java +++ b/test/unit/org/apache/cassandra/locator/YamlFileNetworkTopologySnitchTest.java @@ -88,7 +88,7 @@ public class YamlFileNetworkTopologySnitchTest * @param expectedRack * expected rack */ - private void checkEndpoint(final AbstractNetworkTopologySnitch snitch, + public static void checkEndpoint(final AbstractNetworkTopologySnitch snitch, final String endpointString, final String expectedDatacenter, final String expectedRack) {