You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cassandra.apache.org by mc...@apache.org on 2020/11/17 21:55:14 UTC

[cassandra] branch cassandra-3.11 updated (50d8245 -> d9e1af8)

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

mck pushed a change to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git.


    from 50d8245  Merge branch 'cassandra-3.0' into cassandra-3.11
     new 8ef5a88  Improved check of num_tokens against initial_token in the cassandra.yaml
     new d9e1af8  Merge branch 'cassandra-3.0' into cassandra-3.11

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 CHANGES.txt                                        |  1 +
 NEWS.txt                                           |  8 +++
 src/java/org/apache/cassandra/config/Config.java   |  2 +-
 .../cassandra/config/DatabaseDescriptor.java       | 27 ++++++--
 .../config/DatabaseDescriptorRefTest.java          |  2 +-
 .../cassandra/config/DatabaseDescriptorTest.java   | 78 ++++++++++++++++++++++
 6 files changed, 112 insertions(+), 6 deletions(-)


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org


[cassandra] 01/01: Merge branch 'cassandra-3.0' into cassandra-3.11

Posted by mc...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mck pushed a commit to branch cassandra-3.11
in repository https://gitbox.apache.org/repos/asf/cassandra.git

commit d9e1af8824f6914a6eff99a6345575b93188c51a
Merge: 50d8245 8ef5a88
Author: Mick Semb Wever <mc...@apache.org>
AuthorDate: Tue Nov 17 22:42:06 2020 +0100

    Merge branch 'cassandra-3.0' into cassandra-3.11

 CHANGES.txt                                        |  1 +
 NEWS.txt                                           |  8 +++
 src/java/org/apache/cassandra/config/Config.java   |  2 +-
 .../cassandra/config/DatabaseDescriptor.java       | 27 ++++++--
 .../config/DatabaseDescriptorRefTest.java          |  2 +-
 .../cassandra/config/DatabaseDescriptorTest.java   | 78 ++++++++++++++++++++++
 6 files changed, 112 insertions(+), 6 deletions(-)

diff --cc CHANGES.txt
index 1083096,7aecefa..80b1532
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@@ -1,7 -1,8 +1,8 @@@
 -3.0.24:
 +3.11.10
 +Merged from 3.0:
+  * Improved check of num_tokens against the length of initial_token (CASSANDRA-14477)
   * Fix a race condition on ColumnFamilyStore and TableMetrics (CASSANDRA-16228)
   * Remove the SEPExecutor blocking behavior (CASSANDRA-16186)
 - * Wait for schema agreement when bootstrapping (CASSANDRA-15158)
   * Fix invalid cell value skipping when reading from disk (CASSANDRA-16223)
   * Prevent invoking enable/disable gossip when not in NORMAL (CASSANDRA-16146)
  
diff --cc NEWS.txt
index b3246b5,42fbf63..d16bcce
--- a/NEWS.txt
+++ b/NEWS.txt
@@@ -42,15 -42,16 +42,23 @@@ restore snapshots created with the prev
  'sstableloader' tool. You can upgrade the file format of your snapshots
  using the provided 'sstableupgrade' tool.
  
 -3.0.24
 -======
 -
++3.11.10
++=====
+ Upgrading
+ ---------
+     - In cassandra.yaml, num_tokens must be defined if initial_token is defined.
+       If it is not defined, or not equal to the numbers of tokens defined in initial_tokens,
+       the node will not start. See CASSANDRA-14477 for details.
+ 
 -3.0.20
 +3.11.9
 +======
 +Upgrading
 +---------
 +   - Custom compaction strategies must handle getting sstables added/removed notifications for
 +     sstables already added/removed - see CASSANDRA-14103 for details. This has been a requirement
 +     for correct operation since 3.11.0 due to an issue in CompactionStrategyManager.
 +
 +3.11.7
  ======
  
  Upgrading
diff --cc src/java/org/apache/cassandra/config/DatabaseDescriptor.java
index fe7291b,04293fb..cbf42b9
--- a/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
+++ b/src/java/org/apache/cassandra/config/DatabaseDescriptor.java
@@@ -314,34 -193,136 +314,34 @@@ public class DatabaseDescripto
          }
      }
  
 -    @VisibleForTesting
 -    public static void applyAddressConfig(Config config) throws ConfigurationException
 +    private static void setConfig(Config config)
      {
 -        listenAddress = null;
 -        rpcAddress = null;
 -        broadcastAddress = null;
 -        broadcastRpcAddress = null;
 -
 -        /* Local IP, hostname or interface to bind services to */
 -        if (config.listen_address != null && config.listen_interface != null)
 -        {
 -            throw new ConfigurationException("Set listen_address OR listen_interface, not both", false);
 -        }
 -        else if (config.listen_address != null)
 -        {
 -            try
 -            {
 -                listenAddress = InetAddress.getByName(config.listen_address);
 -            }
 -            catch (UnknownHostException e)
 -            {
 -                throw new ConfigurationException("Unknown listen_address '" + config.listen_address + "'", false);
 -            }
 -
 -            if (listenAddress.isAnyLocalAddress())
 -                throw new ConfigurationException("listen_address cannot be a wildcard address (" + config.listen_address + ")!", false);
 -        }
 -        else if (config.listen_interface != null)
 -        {
 -            listenAddress = getNetworkInterfaceAddress(config.listen_interface, "listen_interface", config.listen_interface_prefer_ipv6);
 -        }
 +        conf = config;
 +    }
  
 -        /* Gossip Address to broadcast */
 -        if (config.broadcast_address != null)
 -        {
 -            try
 -            {
 -                broadcastAddress = InetAddress.getByName(config.broadcast_address);
 -            }
 -            catch (UnknownHostException e)
 -            {
 -                throw new ConfigurationException("Unknown broadcast_address '" + config.broadcast_address + "'", false);
 -            }
 +    private static void applyAll() throws ConfigurationException
 +    {
 +        applySimpleConfig();
  
 -            if (broadcastAddress.isAnyLocalAddress())
 -                throw new ConfigurationException("broadcast_address cannot be a wildcard address (" + config.broadcast_address + ")!", false);
 -        }
 +        applyPartitioner();
  
 -        /* Local IP, hostname or interface to bind RPC server to */
 -        if (config.rpc_address != null && config.rpc_interface != null)
 -        {
 -            throw new ConfigurationException("Set rpc_address OR rpc_interface, not both", false);
 -        }
 -        else if (config.rpc_address != null)
 -        {
 -            try
 -            {
 -                rpcAddress = InetAddress.getByName(config.rpc_address);
 -            }
 -            catch (UnknownHostException e)
 -            {
 -                throw new ConfigurationException("Unknown host in rpc_address " + config.rpc_address, false);
 -            }
 -        }
 -        else if (config.rpc_interface != null)
 -        {
 -            rpcAddress = getNetworkInterfaceAddress(config.rpc_interface, "rpc_interface", config.rpc_interface_prefer_ipv6);
 -        }
 -        else
 -        {
 -            rpcAddress = FBUtilities.getLocalAddress();
 -        }
 +        applyAddressConfig();
  
 -        /* RPC address to broadcast */
 -        if (config.broadcast_rpc_address != null)
 -        {
 -            try
 -            {
 -                broadcastRpcAddress = InetAddress.getByName(config.broadcast_rpc_address);
 -            }
 -            catch (UnknownHostException e)
 -            {
 -                throw new ConfigurationException("Unknown broadcast_rpc_address '" + config.broadcast_rpc_address + "'", false);
 -            }
 +        applyThriftHSHA();
  
 -            if (broadcastRpcAddress.isAnyLocalAddress())
 -                throw new ConfigurationException("broadcast_rpc_address cannot be a wildcard address (" + config.broadcast_rpc_address + ")!", false);
 -        }
 -        else
 -        {
 -            if (rpcAddress.isAnyLocalAddress())
 -                throw new ConfigurationException("If rpc_address is set to a wildcard address (" + config.rpc_address + "), then " +
 -                                                 "you must set broadcast_rpc_address to a value other than " + config.rpc_address, false);
 -        }
 -    }
 +        applySnitch();
  
 -    @VisibleForTesting
 -    static void applyTokensConfig(Config config) throws ConfigurationException
 -    {
 -        if (config.num_tokens != null && config.num_tokens > MAX_NUM_TOKENS)
 -            throw new ConfigurationException(String.format("A maximum number of %d tokens per node is supported", MAX_NUM_TOKENS), false);
 +        applyRequestScheduler();
  
-         applyInitialTokens();
 -        if (config.initial_token != null)
 -        {
 -            Collection<String> tokens = tokensFromString(config.initial_token);
 -            if (config.num_tokens == null)
 -            {
 -                throw new ConfigurationException("initial_token was set but num_tokens is not!", false);
 -            }
++        applyTokensConfig();
  
 -            if (tokens.size() != config.num_tokens)
 -            {
 -                throw new ConfigurationException(String.format("The number of initial tokens (by initial_token) specified (%s) is different from num_tokens value (%s)",
 -                                                               tokens.size(),
 -                                                               config.num_tokens),
 -                                                 false);
 -            }
 +        applySeedProvider();
  
 -            for (String token : tokens)
 -                partitioner.getTokenFactory().validate(token);
 -        }
 -        else if (config.num_tokens == null)
 -        {
 -            config.num_tokens = 1;
 -        }
 +        applyEncryptionContext();
      }
  
 -    public static void applyConfig(Config config) throws ConfigurationException
 +    private static void applySimpleConfig()
      {
 -        conf = config;
  
          if (conf.commitlog_sync == null)
          {
@@@ -616,40 -686,7 +616,40 @@@
          if (conf.concurrent_compactors <= 0)
              throw new ConfigurationException("concurrent_compactors should be strictly greater than 0, but was " + conf.concurrent_compactors, false);
  
-         if (conf.num_tokens > MAX_NUM_TOKENS)
 -        applyTokensConfig(config);
++        if (conf.num_tokens != null && conf.num_tokens > MAX_NUM_TOKENS)
 +            throw new ConfigurationException(String.format("A maximum number of %d tokens per node is supported", MAX_NUM_TOKENS), false);
 +
 +        try
 +        {
 +            // if prepared_statements_cache_size_mb option was set to "auto" then size of the cache should be "max(1/256 of Heap (in MB), 10MB)"
 +            preparedStatementsCacheSizeInMB = (conf.prepared_statements_cache_size_mb == null)
 +                                              ? Math.max(10, (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024 / 256))
 +                                              : conf.prepared_statements_cache_size_mb;
 +
 +            if (preparedStatementsCacheSizeInMB <= 0)
 +                throw new NumberFormatException(); // to escape duplicating error message
 +        }
 +        catch (NumberFormatException e)
 +        {
 +            throw new ConfigurationException("prepared_statements_cache_size_mb option was set incorrectly to '"
 +                                             + conf.prepared_statements_cache_size_mb + "', supported values are <integer> >= 0.", false);
 +        }
 +
 +        try
 +        {
 +            // if thrift_prepared_statements_cache_size_mb option was set to "auto" then size of the cache should be "max(1/256 of Heap (in MB), 10MB)"
 +            thriftPreparedStatementsCacheSizeInMB = (conf.thrift_prepared_statements_cache_size_mb == null)
 +                                                    ? Math.max(10, (int) (Runtime.getRuntime().maxMemory() / 1024 / 1024 / 256))
 +                                                    : conf.thrift_prepared_statements_cache_size_mb;
 +
 +            if (thriftPreparedStatementsCacheSizeInMB <= 0)
 +                throw new NumberFormatException(); // to escape duplicating error message
 +        }
 +        catch (NumberFormatException e)
 +        {
 +            throw new ConfigurationException("thrift_prepared_statements_cache_size_mb option was set incorrectly to '"
 +                                             + conf.thrift_prepared_statements_cache_size_mb + "', supported values are <integer> >= 0.", false);
 +        }
  
          try
          {
@@@ -940,107 -753,60 +940,126 @@@
          }
          if (seedProvider.getSeeds().size() == 0)
              throw new ConfigurationException("The seed provider lists no seeds.", false);
 +    }
  
-     public static void applyInitialTokens()
 -        if (conf.user_defined_function_fail_timeout < 0)
 -            throw new ConfigurationException("user_defined_function_fail_timeout must not be negative", false);
 -        if (conf.user_defined_function_warn_timeout < 0)
 -            throw new ConfigurationException("user_defined_function_warn_timeout must not be negative", false);
++    public static void applyTokensConfig()
++    {
++        applyTokensConfig(conf);
++    }
+ 
 -        if (conf.user_defined_function_fail_timeout < conf.user_defined_function_warn_timeout)
 -            throw new ConfigurationException("user_defined_function_warn_timeout must less than user_defined_function_fail_timeout", false);
++    static void applyTokensConfig(Config conf)
 +    {
 +        if (conf.initial_token != null)
 +        {
 +            Collection<String> tokens = tokensFromString(conf.initial_token);
++            if (conf.num_tokens == null)
++            {
++                throw new ConfigurationException("initial_token was set but num_tokens is not!", false);
++            }
+ 
 -        if (conf.commitlog_segment_size_in_mb <= 0)
 -            throw new ConfigurationException("commitlog_segment_size_in_mb must be positive, but was "
 -                    + conf.commitlog_segment_size_in_mb, false);
 -        else if (conf.commitlog_segment_size_in_mb >= 2048)
 -            throw new ConfigurationException("commitlog_segment_size_in_mb must be smaller than 2048, but was "
 -                    + conf.commitlog_segment_size_in_mb, false);
 +            if (tokens.size() != conf.num_tokens)
-                 throw new ConfigurationException("The number of initial tokens (by initial_token) specified is different from num_tokens value", false);
++            {
++                throw new ConfigurationException(String.format("The number of initial tokens (by initial_token) specified (%s) is different from num_tokens value (%s)",
++                                                               tokens.size(),
++                                                               conf.num_tokens),
++                                                 false);
++            }
 +
 +            for (String token : tokens)
 +                partitioner.getTokenFactory().validate(token);
 +        }
++        else if (conf.num_tokens == null)
++        {
++            conf.num_tokens = 1;
++        }
 +    }
 +
 +    // Maybe safe for clients + tools
 +    public static void applyRequestScheduler()
 +    {
 +        /* Request Scheduler setup */
 +        requestSchedulerOptions = conf.request_scheduler_options;
 +        if (conf.request_scheduler != null)
 +        {
 +            try
 +            {
 +                if (requestSchedulerOptions == null)
 +                {
 +                    requestSchedulerOptions = new RequestSchedulerOptions();
 +                }
 +                Class<?> cls = Class.forName(conf.request_scheduler);
 +                requestScheduler = (IRequestScheduler) cls.getConstructor(RequestSchedulerOptions.class).newInstance(requestSchedulerOptions);
 +            }
 +            catch (ClassNotFoundException e)
 +            {
 +                throw new ConfigurationException("Invalid Request Scheduler class " + conf.request_scheduler, false);
 +            }
 +            catch (Exception e)
 +            {
 +                throw new ConfigurationException("Unable to instantiate request scheduler", e);
 +            }
 +        }
 +        else
 +        {
 +            requestScheduler = new NoScheduler();
 +        }
  
 -        if (conf.max_mutation_size_in_kb == null)
 -            conf.max_mutation_size_in_kb = conf.commitlog_segment_size_in_mb * 1024 / 2;
 -        else if (conf.commitlog_segment_size_in_mb * 1024 < 2 * conf.max_mutation_size_in_kb)
 -            throw new ConfigurationException("commitlog_segment_size_in_mb must be at least twice the size of max_mutation_size_in_kb / 1024", false);
 +        if (conf.request_scheduler_id == RequestSchedulerId.keyspace)
 +        {
 +            requestSchedulerId = conf.request_scheduler_id;
 +        }
 +        else
 +        {
 +            // Default to Keyspace
 +            requestSchedulerId = RequestSchedulerId.keyspace;
 +        }
 +    }
  
 -        // native transport encryption options
 -        if (conf.native_transport_port_ssl != null
 -            && conf.native_transport_port_ssl.intValue() != conf.native_transport_port.intValue()
 -            && !conf.client_encryption_options.enabled)
 +    // definitely not safe for tools + clients - implicitly instantiates StorageService
 +    public static void applySnitch()
 +    {
 +        /* end point snitch */
 +        if (conf.endpoint_snitch == null)
          {
 -            throw new ConfigurationException("Encryption must be enabled in client_encryption_options for native_transport_port_ssl", false);
 +            throw new ConfigurationException("Missing endpoint_snitch directive", false);
          }
 +        snitch = createEndpointSnitch(conf.dynamic_snitch, conf.endpoint_snitch);
 +        EndpointSnitchInfo.create();
  
 -        // If max protocol version has been set, just validate it's within an acceptable range
 -        if (conf.native_transport_max_negotiable_protocol_version != Integer.MIN_VALUE)
 +        localDC = snitch.getDatacenter(FBUtilities.getBroadcastAddress());
 +        localComparator = new Comparator<InetAddress>()
          {
 -            if (conf.native_transport_max_negotiable_protocol_version < Server.MIN_SUPPORTED_VERSION
 -                || conf.native_transport_max_negotiable_protocol_version > Server.CURRENT_VERSION)
 +            public int compare(InetAddress endpoint1, InetAddress endpoint2)
              {
 -                throw new ConfigurationException(String.format("Invalid setting for native_transport_max_negotiable_version (%d); " +
 -                                                               "Values between %s and %s are supported",
 -                                                               conf.native_transport_max_negotiable_protocol_version,
 -                                                               Server.MIN_SUPPORTED_VERSION,
 -                                                               Server.CURRENT_VERSION));
 +                boolean local1 = localDC.equals(snitch.getDatacenter(endpoint1));
 +                boolean local2 = localDC.equals(snitch.getDatacenter(endpoint2));
 +                if (local1 && !local2)
 +                    return -1;
 +                if (local2 && !local1)
 +                    return 1;
 +                return 0;
              }
 -        }
 -
 -        if (conf.max_value_size_in_mb == null || conf.max_value_size_in_mb <= 0)
 -            throw new ConfigurationException("max_value_size_in_mb must be positive", false);
 -        else if (conf.max_value_size_in_mb >= 2048)
 -            throw new ConfigurationException("max_value_size_in_mb must be smaller than 2048, but was "
 -                    + conf.max_value_size_in_mb, false);
 +        };
 +    }
  
 -        if (conf.otc_coalescing_enough_coalesced_messages > 128)
 -            throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be smaller than 128", false);
 +    // definitely not safe for tools + clients - implicitly instantiates schema
 +    public static void applyPartitioner()
 +    {
 +        /* Hashing strategy */
 +        if (conf.partitioner == null)
 +        {
 +            throw new ConfigurationException("Missing directive: partitioner", false);
 +        }
 +        try
 +        {
 +            partitioner = FBUtilities.newPartitioner(System.getProperty(Config.PROPERTY_PREFIX + "partitioner", conf.partitioner));
 +        }
 +        catch (Exception e)
 +        {
 +            throw new ConfigurationException("Invalid partitioner class " + conf.partitioner, false);
 +        }
  
 -        if (conf.otc_coalescing_enough_coalesced_messages <= 0)
 -            throw new ConfigurationException("otc_coalescing_enough_coalesced_messages must be positive", false);
 +        paritionerName = partitioner.getClass().getCanonicalName();
      }
  
      /**
diff --cc test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
index 88c965b,0000000..6e865ae
mode 100644,000000..100644
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java
@@@ -1,268 -1,0 +1,268 @@@
 +/*
 + * 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.config;
 +
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.PrintStream;
 +import java.lang.management.ManagementFactory;
 +import java.lang.management.ThreadInfo;
 +import java.lang.management.ThreadMXBean;
 +import java.lang.reflect.Method;
 +import java.net.URL;
 +import java.util.ArrayList;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.List;
 +import java.util.Map;
 +import java.util.Set;
 +
 +import org.junit.Test;
 +
 +import org.apache.cassandra.utils.Pair;
 +
 +import static org.junit.Assert.assertEquals;
 +import static org.junit.Assert.fail;
 +
 +/**
 + * Verifies that {@link DatabaseDescriptor#clientInitialization()} } and a couple of <i>apply</i> methods
 + * do not somehow lazily initialize any unwanted part of Cassandra like schema, commit log or start
 + * unexpected threads.
 + *
 + * {@link DatabaseDescriptor#toolInitialization()} is tested via unit tests extending
 + * {@link org.apache.cassandra.tools.ToolsTester}.
 + */
 +public class DatabaseDescriptorRefTest
 +{
 +    static final String[] validClasses = {
 +    "org.apache.cassandra.auth.IInternodeAuthenticator",
 +    "org.apache.cassandra.auth.IAuthenticator",
 +    "org.apache.cassandra.auth.IAuthorizer",
 +    "org.apache.cassandra.auth.IRoleManager",
 +    "org.apache.cassandra.config.DatabaseDescriptor",
 +    "org.apache.cassandra.config.ConfigurationLoader",
 +    "org.apache.cassandra.config.Config",
 +    "org.apache.cassandra.config.Config$1",
 +    "org.apache.cassandra.config.Config$RequestSchedulerId",
 +    "org.apache.cassandra.config.Config$CommitLogSync",
 +    "org.apache.cassandra.config.Config$DiskAccessMode",
 +    "org.apache.cassandra.config.Config$DiskFailurePolicy",
 +    "org.apache.cassandra.config.Config$CommitFailurePolicy",
 +    "org.apache.cassandra.config.Config$DiskOptimizationStrategy",
 +    "org.apache.cassandra.config.Config$InternodeCompression",
 +    "org.apache.cassandra.config.Config$MemtableAllocationType",
 +    "org.apache.cassandra.config.Config$UserFunctionTimeoutPolicy",
 +    "org.apache.cassandra.config.RequestSchedulerOptions",
 +    "org.apache.cassandra.config.ParameterizedClass",
 +    "org.apache.cassandra.config.EncryptionOptions",
 +    "org.apache.cassandra.config.EncryptionOptions$ClientEncryptionOptions",
 +    "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptions",
 +    "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptions$InternodeEncryption",
 +    "org.apache.cassandra.config.ReplicaFilteringProtectionOptions",
 +    "org.apache.cassandra.config.YamlConfigurationLoader",
 +    "org.apache.cassandra.config.YamlConfigurationLoader$PropertiesChecker",
 +    "org.apache.cassandra.config.YamlConfigurationLoader$PropertiesChecker$1",
 +    "org.apache.cassandra.config.YamlConfigurationLoader$CustomConstructor",
 +    "org.apache.cassandra.config.TransparentDataEncryptionOptions",
 +    "org.apache.cassandra.dht.IPartitioner",
 +    "org.apache.cassandra.distributed.impl.InstanceClassLoader",
 +    "org.apache.cassandra.distributed.impl.InstanceConfig",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$CallableNoExcept",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$InstanceFunction",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableBiConsumer",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableBiFunction",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableCallable",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableConsumer",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableFunction",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableRunnable",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$SerializableTriFunction",
 +    "org.apache.cassandra.distributed.impl.InvokableInstance$TriFunction",
 +    "org.apache.cassandra.distributed.impl.Message",
 +    "org.apache.cassandra.exceptions.ConfigurationException",
 +    "org.apache.cassandra.exceptions.RequestValidationException",
 +    "org.apache.cassandra.exceptions.CassandraException",
 +    "org.apache.cassandra.exceptions.TransportException",
 +    "org.apache.cassandra.locator.IEndpointSnitch",
 +    "org.apache.cassandra.io.FSWriteError",
 +    "org.apache.cassandra.io.FSError",
 +    "org.apache.cassandra.io.compress.ICompressor",
 +    "org.apache.cassandra.io.compress.LZ4Compressor",
 +    "org.apache.cassandra.io.sstable.metadata.MetadataType",
 +    "org.apache.cassandra.io.util.BufferedDataOutputStreamPlus",
 +    "org.apache.cassandra.io.util.DataOutputBuffer",
 +    "org.apache.cassandra.io.util.DataOutputBufferFixed",
 +    "org.apache.cassandra.io.util.DataOutputStreamPlus",
 +    "org.apache.cassandra.io.util.DataOutputPlus",
 +    "org.apache.cassandra.io.util.DiskOptimizationStrategy",
 +    "org.apache.cassandra.io.util.SpinningDiskOptimizationStrategy",
 +    "org.apache.cassandra.locator.SimpleSeedProvider",
 +    "org.apache.cassandra.locator.SeedProvider",
 +    "org.apache.cassandra.net.BackPressureStrategy",
 +    "org.apache.cassandra.scheduler.IRequestScheduler",
 +    "org.apache.cassandra.security.EncryptionContext",
 +    "org.apache.cassandra.service.CacheService$CacheType",
 +    "org.apache.cassandra.utils.FBUtilities",
 +    "org.apache.cassandra.utils.FBUtilities$1",
 +    "org.apache.cassandra.utils.CloseableIterator",
 +    "org.apache.cassandra.utils.Pair",
 +    "org.apache.cassandra.OffsetAwareConfigurationLoader",
 +    "org.apache.cassandra.ConsoleAppender",
 +    "org.apache.cassandra.ConsoleAppender$1",
 +    "org.apache.cassandra.LogbackStatusListener",
 +    "org.apache.cassandra.LogbackStatusListener$ToLoggerOutputStream",
 +    "org.apache.cassandra.LogbackStatusListener$WrappedPrintStream",
 +    "org.apache.cassandra.TeeingAppender",
 +    // generated classes
 +    "org.apache.cassandra.config.ConfigBeanInfo",
 +    "org.apache.cassandra.config.ConfigCustomizer",
 +    "org.apache.cassandra.config.EncryptionOptionsBeanInfo",
 +    "org.apache.cassandra.config.EncryptionOptionsCustomizer",
 +    "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptionsBeanInfo",
 +    "org.apache.cassandra.config.EncryptionOptions$ServerEncryptionOptionsCustomizer",
 +    "org.apache.cassandra.ConsoleAppenderBeanInfo",
 +    "org.apache.cassandra.ConsoleAppenderCustomizer",
 +    };
 +
 +    static final Set<String> checkedClasses = new HashSet<>(Arrays.asList(validClasses));
 +
 +    @Test
 +    public void testDatabaseDescriptorRef() throws Throwable
 +    {
 +        PrintStream out = System.out;
 +        PrintStream err = System.err;
 +
 +        ThreadMXBean threads = ManagementFactory.getThreadMXBean();
 +        int threadCount = threads.getThreadCount();
 +
 +        ClassLoader delegate = Thread.currentThread().getContextClassLoader();
 +
 +        List<Pair<String, Exception>> violations = Collections.synchronizedList(new ArrayList<>());
 +
 +        ClassLoader cl = new ClassLoader(null)
 +        {
 +            final Map<String, Class<?>> classMap = new HashMap<>();
 +
 +            public URL getResource(String name)
 +            {
 +                return delegate.getResource(name);
 +            }
 +
 +            public InputStream getResourceAsStream(String name)
 +            {
 +                return delegate.getResourceAsStream(name);
 +            }
 +
 +            protected Class<?> findClass(String name) throws ClassNotFoundException
 +            {
 +                Class<?> cls = classMap.get(name);
 +                if (cls != null)
 +                    return cls;
 +
 +                if (name.startsWith("org.apache.cassandra."))
 +                {
 +                    // out.println(name);
 +
 +                    if (!checkedClasses.contains(name))
 +                        violations.add(Pair.create(name, new Exception()));
 +                }
 +
 +                URL url = delegate.getResource(name.replace('.', '/') + ".class");
 +                if (url == null)
 +                    throw new ClassNotFoundException(name);
 +                try (InputStream in = url.openConnection().getInputStream())
 +                {
 +                    ByteArrayOutputStream os = new ByteArrayOutputStream();
 +                    int c;
 +                    while ((c = in.read()) != -1)
 +                        os.write(c);
 +                    byte[] data = os.toByteArray();
 +                    cls = defineClass(name, data, 0, data.length);
 +                    classMap.put(name, cls);
 +                    return cls;
 +                }
 +                catch (IOException e)
 +                {
 +                    throw new ClassNotFoundException(name, e);
 +                }
 +            }
 +        };
 +
 +        Thread.currentThread().setContextClassLoader(cl);
 +
 +        assertEquals("thread started", threadCount, threads.getThreadCount());
 +
 +        Class cDatabaseDescriptor = Class.forName("org.apache.cassandra.config.DatabaseDescriptor", true, cl);
 +
 +        for (String methodName : new String[]{
 +            "clientInitialization",
 +            "applyAddressConfig",
 +            "applyThriftHSHA",
-             "applyInitialTokens",
++            "applyTokensConfig",
 +            // no seed provider in default configuration for clients
 +            // "applySeedProvider",
 +            // definitely not safe for clients - implicitly instantiates schema
 +            // "applyPartitioner",
 +            // definitely not safe for clients - implicitly instantiates StorageService
 +            // "applySnitch",
 +            "applyEncryptionContext",
 +            // starts "REQUEST-SCHEDULER" thread via RoundRobinScheduler
 +            // "applyRequestScheduler",
 +        })
 +        {
 +            Method method = cDatabaseDescriptor.getDeclaredMethod(methodName);
 +            method.invoke(null);
 +
 +            if ("clientInitialization".equals(methodName) &&
 +                threadCount + 1 == threads.getThreadCount())
 +            {
 +                // ignore the "AsyncAppender-Worker-ASYNC" thread
 +                threadCount++;
 +            }
 +
 +            if (threadCount != threads.getThreadCount())
 +            {
 +                for (ThreadInfo threadInfo : threads.getThreadInfo(threads.getAllThreadIds()))
 +                    out.println("Thread #" + threadInfo.getThreadId() + ": " + threadInfo.getThreadName());
 +                assertEquals("thread started in " + methodName, threadCount, ManagementFactory.getThreadMXBean().getThreadCount());
 +            }
 +
 +            checkViolations(err, violations);
 +        }
 +    }
 +
 +    private void checkViolations(PrintStream err, List<Pair<String, Exception>> violations)
 +    {
 +        if (!violations.isEmpty())
 +        {
 +            for (Pair<String, Exception> violation : new ArrayList<>(violations))
 +            {
 +                err.println();
 +                err.println();
 +                err.println("VIOLATION: " + violation.left);
 +                violation.right.printStackTrace(err);
 +            }
 +
 +            fail();
 +        }
 +    }
 +}
diff --cc test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
index 4788289,7614e02..4c6b3d7
--- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
+++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorTest.java
@@@ -321,4 -313,131 +321,82 @@@ public class DatabaseDescriptorTes
              DatabaseDescriptor.setRepairSessionMaxTreeDepth(previousDepth);
          }
      }
+ 
+     @Test
 -    public void testApplyInitialTokensInitialTokensSetNumTokensSetAndDoesMatch() throws Exception
++    public void testApplyInitialTokensInitialTokensSetNumTokensSetAndDoesMatch()
+     {
+         Config config = DatabaseDescriptor.loadConfig();
+         config.initial_token = "0,256,1024";
+         config.num_tokens = 3;
+ 
 -        unregisterSnitchesForTokenConfigTest();
 -
+         try
+         {
+             DatabaseDescriptor.applyTokensConfig(config);
 -
+             Assert.assertEquals(Integer.valueOf(3), config.num_tokens);
+             Assert.assertEquals(3, DatabaseDescriptor.tokensFromString(config.initial_token).size());
+         }
 -        finally
++        catch (ConfigurationException e)
+         {
 -            unregisterSnitchesForTokenConfigTest();
++            Assert.fail("number of tokens in initial_token=0,256,1024 does not match num_tokens = 3");
+         }
+     }
+ 
+     @Test
 -    public void testApplyInitialTokensInitialTokensSetNumTokensSetAndDoesntMatch() throws Exception
++    public void testApplyInitialTokensInitialTokensSetNumTokensSetAndDoesntMatch()
+     {
+         Config config = DatabaseDescriptor.loadConfig();
+         config.initial_token = "0,256,1024";
+         config.num_tokens = 10;
+ 
 -        unregisterSnitchesForTokenConfigTest();
 -
+         try
+         {
+             DatabaseDescriptor.applyTokensConfig(config);
+ 
+             Assert.fail("initial_token = 0,256,1024 and num_tokens = 10 but applyInitialTokens() did not fail!");
+         }
+         catch (ConfigurationException ex)
+         {
+             Assert.assertEquals("The number of initial tokens (by initial_token) specified (3) is different from num_tokens value (10)",
+                                 ex.getMessage());
+         }
 -        finally
 -        {
 -            unregisterSnitchesForTokenConfigTest();
 -        }
+     }
+ 
+     @Test
 -    public void testApplyInitialTokensInitialTokensSetNumTokensNotSet() throws Exception
++    public void testApplyInitialTokensInitialTokensSetNumTokensNotSet()
+     {
+         Config config = DatabaseDescriptor.loadConfig();
 -
 -        unregisterSnitchesForTokenConfigTest();
++        config.initial_token = "0,256,1024";
+ 
+         try
+         {
 -            config.initial_token = "0,256,1024";
 -            config.num_tokens = null;
+             DatabaseDescriptor.applyTokensConfig(config);
+             Assert.fail("setting initial_token and not setting num_tokens is invalid");
+         }
+         catch (ConfigurationException ex)
+         {
+             Assert.assertEquals("initial_token was set but num_tokens is not!", ex.getMessage());
+         }
 -        finally
 -        {
 -            unregisterSnitchesForTokenConfigTest();
 -        }
+     }
+ 
+     @Test
 -    public void testApplyInitialTokensInitialTokensNotSetNumTokensSet() throws Exception
++    public void testApplyInitialTokensInitialTokensNotSetNumTokensSet()
+     {
+         Config config = DatabaseDescriptor.loadConfig();
+         config.num_tokens = 3;
+ 
 -        unregisterSnitchesForTokenConfigTest();
 -
 -        try
 -        {
 -            DatabaseDescriptor.applyTokensConfig(config);
 -        }
 -        finally
 -        {
 -            unregisterSnitchesForTokenConfigTest();
 -        }
++        DatabaseDescriptor.applyTokensConfig(config);
+ 
+         Assert.assertEquals(Integer.valueOf(3), config.num_tokens);
+         Assert.assertTrue(DatabaseDescriptor.tokensFromString(config.initial_token).isEmpty());
+     }
+ 
+     @Test
 -    public void testApplyInitialTokensInitialTokensNotSetNumTokensNotSet() throws Exception
++    public void testApplyInitialTokensInitialTokensNotSetNumTokensNotSet()
+     {
+         Config config = DatabaseDescriptor.loadConfig();
 -
 -        unregisterSnitchesForTokenConfigTest();
 -
 -        try
 -        {
 -            DatabaseDescriptor.applyTokensConfig(config);
 -        }
 -        finally
 -        {
 -            unregisterSnitchesForTokenConfigTest();
 -        }
++        DatabaseDescriptor.applyTokensConfig(config);
+ 
+         Assert.assertEquals(Integer.valueOf(1), config.num_tokens);
+         Assert.assertTrue(DatabaseDescriptor.tokensFromString(config.initial_token).isEmpty());
+     }
 -
 -    private void unregisterSnitchesForTokenConfigTest() throws Exception
 -    {
 -        try
 -        {
 -            MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
 -            mbs.unregisterMBean(new ObjectName("org.apache.cassandra.db:type=DynamicEndpointSnitch"));
 -            mbs.unregisterMBean(new ObjectName("org.apache.cassandra.db:type=EndpointSnitchInfo"));
 -        }
 -        catch (InstanceNotFoundException ex)
 -        {
 -            // ok
 -        }
 -    }
 -}
 +}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@cassandra.apache.org
For additional commands, e-mail: commits-help@cassandra.apache.org