Merge branch 'cassandra-4.1' into trunk

This commit is contained in:
Jacek Lewandowski 2023-02-01 09:48:05 +01:00
commit 763793e89d
13 changed files with 200 additions and 14 deletions

View File

@ -106,6 +106,7 @@ Merged from 4.1:
* Streaming progress virtual table lock contention can trigger TCP_USER_TIMEOUT and fail streaming (CASSANDRA-18110)
* Fix perpetual load of denylist on read in cases where denylist can never be loaded (CASSANDRA-18116)
Merged from 4.0:
* Improve unit tests performance (CASSANDRA-17427)
* Connect to listen address when own broadcast address is requested (CASSANDRA-18200)
* Add safeguard so cleanup fails when node has pending ranges (CASSANDRA-16418)
* Fix legacy clustering serialization for paging with compact storage (CASSANDRA-17507)

View File

@ -111,12 +111,18 @@
<property name="test.distributed.timeout" value="900000" />
<property name="test.simulation.timeout" value="1800000" />
<!-- default for cql tests. Can be override by -Dcassandra.test.use_prepared=false -->
<!-- default for cql tests. Can be overridden by -Dcassandra.test.use_prepared=false -->
<property name="cassandra.test.use_prepared" value="true" />
<!-- The number of active processors seen by JVM -->
<property name="cassandra.test.processorCount" value="2"/>
<!-- skip flushing schema tables during tests -->
<property name="cassandra.test.flush_local_schema_changes" value="false" />
<!-- fast shutdown of messaging service -->
<property name="cassandra.test.messagingService.nonGracefulShutdown" value="true"/>
<!-- https://www.eclemma.org/jacoco/ -->
<property name="jacoco.version" value="0.8.8"/>
<property name="jacoco.export.dir" value="${build.dir}/jacoco/" />
@ -137,6 +143,10 @@
<available file="${build.src.java}" type="dir" />
</condition>
<condition property="cassandra.use_nix_recursive_delete" value="false" else="true">
<os family="windows" />
</condition>
<!-- Check if all tests are being run or just one. If it's all tests don't spam the console with test output.
If it's an individual test print the output from the test under the assumption someone is debugging the test
and wants to know what is going on without having to context switch to the log file that is generated.
@ -1110,6 +1120,7 @@
more aggressively rather than waiting. See CASSANDRA-14922 for more details.
-->
<jvmarg value="-XX:SoftRefLRUPolicyMSPerMB=0" />
<jvmarg value="-XX:ActiveProcessorCount=${cassandra.test.processorCount}" />
<jvmarg value="-XX:HeapDumpPath=build/test" />
<jvmarg value="-Dcassandra.test.driver.connection_timeout_ms=${test.driver.connection_timeout_ms}"/>
<jvmarg value="-Dcassandra.test.driver.read_timeout_ms=${test.driver.read_timeout_ms}"/>
@ -1122,6 +1133,9 @@
<jvmarg value="-Dcassandra.keepBriefBrief=${cassandra.keepBriefBrief}" />
<jvmarg value="-Dcassandra.strict.runtime.checks=true" />
<jvmarg value="-Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true" /> <!-- enable defensive checks -->
<jvmarg value="-Dcassandra.test.flush_local_schema_changes=${cassandra.test.flush_local_schema_changes}"/>
<jvmarg value="-Dcassandra.test.messagingService.nonGracefulShutdown=${cassandra.test.messagingService.nonGracefulShutdown}"/>
<jvmarg value="-Dcassandra.use_nix_recursive_delete=${cassandra.use_nix_recursive_delete}"/>
<jvmarg line="${java11-jvmargs}"/>
<!-- disable shrinks in quicktheories CASSANDRA-15554 -->
<jvmarg value="-DQT_SHRINKS=0"/>
@ -1797,6 +1811,7 @@
<fileset dir="ide/idea"/>
</copy>
<replace file=".idea/workspace.xml" token="trunk" value="${eclipse.project.name}"/>
<replace file=".idea/workspace.xml" token="-Dcassandra.use_nix_recursive_delete=true" value="-Dcassandra.use_nix_recursive_delete=${cassandra.use_nix_recursive_delete}"/>
<copy tofile="${eclipse.project.name}.iml" file="ide/idea-iml-file.xml"/>
<echo file=".idea/.name">Apache Cassandra ${eclipse.project.name}</echo>
<echo file=".idea/modules.xml"><![CDATA[<?xml version="1.0" encoding="UTF-8"?>

View File

@ -161,13 +161,19 @@
<configuration default="true" type="JUnit" factoryName="JUnit">
<extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
<module name="" />
<extension name="coverage">
<pattern>
<option name="PATTERN" value="org.apache.cassandra.*" />
<option name="ENABLED" value="true" />
</pattern>
</extension>
<option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
<option name="ALTERNATIVE_JRE_PATH" value="" />
<option name="PACKAGE_NAME" />
<option name="MAIN_CLASS_NAME" value="" />
<option name="METHOD_NAME" value="" />
<option name="TEST_OBJECT" value="class" />
<option name="VM_PARAMETERS" value="-Dcassandra.config=file://$PROJECT_DIR$/test/conf/cassandra.yaml -Dlogback.configurationFile=file://$PROJECT_DIR$/test/conf/logback-test.xml -Dcassandra.logdir=$PROJECT_DIR$/build/test/logs -Djava.library.path=$PROJECT_DIR$/lib/sigar-bin -Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables -Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables -Dcassandra.ring_delay_ms=1000 -Dcassandra.skip_sync=true -ea -XX:MaxMetaspaceSize=384M -XX:SoftRefLRUPolicyMSPerMB=0 -XX:HeapDumpPath=build/test -Dcassandra.strict.runtime.checks=true -Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables -Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables -Dmigration-sstable-root=$PROJECT_DIR$/test/data/migration-sstables -Dcassandra.ring_delay_ms=1000 -Dcassandra.tolerate_sstable_size=true -Dcassandra.skip_sync=true -Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true" />
<option name="VM_PARAMETERS" value="-Dcassandra.config=file://$PROJECT_DIR$/test/conf/cassandra.yaml -Dlogback.configurationFile=file://$PROJECT_DIR$/test/conf/logback-test.xml -Dcassandra.logdir=$PROJECT_DIR$/build/test/logs -Djava.library.path=$PROJECT_DIR$/lib/sigar-bin -Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables -Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables -Dcassandra.ring_delay_ms=1000 -Dcassandra.skip_sync=true -ea -XX:MaxMetaspaceSize=1G -XX:SoftRefLRUPolicyMSPerMB=0 -XX:HeapDumpPath=build/test -XX:ActiveProcessorCount=2 -Dcassandra.strict.runtime.checks=true -Dlegacy-sstable-root=$PROJECT_DIR$/test/data/legacy-sstables -Dinvalid-legacy-sstable-root=$PROJECT_DIR$/test/data/invalid-legacy-sstables -Dmigration-sstable-root=$PROJECT_DIR$/test/data/migration-sstables -Dcassandra.ring_delay_ms=1000 -Dcassandra.tolerate_sstable_size=true -Dcassandra.skip_sync=true -Dcassandra.reads.thresholds.coordinator.defensive_checks_enabled=true -Dcassandra.use_nix_recursive_delete=true -Dcassandra.test.messagingService.nonGracefulShutdown=true -Dcassandra.test.flush_local_schema_changes=false " />
<option name="PARAMETERS" value="" />
<fork_mode value="class" />
<option name="WORKING_DIRECTORY" value="" />

View File

@ -339,6 +339,23 @@ public enum CassandraRelevantProperties
* The default used in SimpleSeedProvider is 20.
*/
SEED_COUNT_WARN_THRESHOLD("cassandra.seed_count_warn_threshold"),
/** When enabled, recursive directory deletion will be executed using a unix command `rm -rf` instead of traversing
* and removing individual files. This is now used only tests, but eventually we will make it true by default.*/
USE_NIX_RECURSIVE_DELETE("cassandra.use_nix_recursive_delete"),
/** If set, {@link org.apache.cassandra.net.MessagingService} is shutdown abrtuptly without waiting for anything.
* This is an optimization used in unit tests becuase we never restart a node there. The only node is stopoped
* when the JVM terminates. Therefore, we can use such optimization and not wait unnecessarily. */
NON_GRACEFUL_SHUTDOWN("cassandra.test.messagingService.nonGracefulShutdown"),
/** Flush changes of {@link org.apache.cassandra.schema.SchemaKeyspace} after each schema modification. In production,
* we always do that. However, tests which do not restart nodes may disable this functionality in order to run
* faster. Note that this is disabled for unit tests but if an individual test requires schema to be flushed, it
* can be also done manually for that particular case: {@code flush(SchemaConstants.SCHEMA_KEYSPACE_NAME);}. */
FLUSH_LOCAL_SCHEMA_CHANGES("cassandra.test.flush_local_schema_changes", "true"),
;
CassandraRelevantProperties(String key, String defaultVal)
@ -382,6 +399,17 @@ public enum CassandraRelevantProperties
return defaultVal;
}
/**
* Sets the property to its default value if a default value was specified. Remove the property otherwise.
*/
public void reset()
{
if (defaultVal != null)
System.setProperty(key, defaultVal);
else
System.getProperties().remove(key);
}
/**
* Gets the value of a system property as a String.
* @return system property String value if it exists, overrideDefaultValue otherwise.
@ -584,4 +612,3 @@ public enum CassandraRelevantProperties
return System.getProperties().containsKey(key);
}
}

View File

@ -24,6 +24,7 @@ import java.nio.file.attribute.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@ -44,6 +45,7 @@ import org.apache.cassandra.utils.NoSpamLogger;
import static java.nio.file.StandardOpenOption.*;
import static java.util.Collections.unmodifiableSet;
import static org.apache.cassandra.config.CassandraRelevantProperties.USE_NIX_RECURSIVE_DELETE;
import static org.apache.cassandra.utils.Throwables.merge;
/**
@ -71,7 +73,7 @@ public final class PathUtils
if (StorageService.instance.isDaemonSetupCompleted())
setDeletionListener(ignore -> {});
else
logger.info("Deleting file during startup: {}", path);
logger.trace("Deleting file during startup: {}", path);
};
public static FileChannel newReadChannel(Path path) throws NoSuchFileException
@ -310,6 +312,55 @@ public final class PathUtils
return accumulate;
}
/**
* Uses unix `rm -r` to delete a directory recursively.
* Note that, it will trigger {@link #onDeletion} listener only for the provided path and will not call it for any
* nested path. This method can be much faster than deleting files and directories recursively by traversing them
* with Java. Though, we use it only for tests because it provides less information about the problem when something
* goes wrong.
*
* @param path path to be deleted
* @param quietly if quietly, additional `-f` flag is added to the `rm` command so that it will not complain in case
* the provided path is missing
*/
private static void deleteRecursiveUsingNixCommand(Path path, boolean quietly)
{
String [] cmd = new String[]{ "rm", quietly ? "-rdf" : "-rd", path.toAbsolutePath().toString() };
try
{
if (!quietly && !Files.exists(path))
throw new NoSuchFileException(path.toString());
Process p = Runtime.getRuntime().exec(cmd);
int result = p.waitFor();
String out, err;
try (BufferedReader outReader = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader errReader = new BufferedReader(new InputStreamReader(p.getErrorStream())))
{
out = outReader.lines().collect(Collectors.joining("\n"));
err = errReader.lines().collect(Collectors.joining("\n"));
}
if (result != 0 && Files.exists(path))
{
logger.error("{} returned:\nstdout:\n{}\n\nstderr:\n{}", Arrays.toString(cmd), out, err);
throw new IOException(String.format("%s returned non-zero exit code: %d%nstdout:%n%s%n%nstderr:%n%s", Arrays.toString(cmd), result, out, err));
}
onDeletion.accept(path);
}
catch (IOException e)
{
throw propagateUnchecked(e, path, true);
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
throw new FSWriteError(e, path);
}
}
/**
* Deletes all files and subdirectories under "path".
* @param path file to be deleted
@ -317,10 +368,16 @@ public final class PathUtils
*/
public static void deleteRecursive(Path path)
{
if (USE_NIX_RECURSIVE_DELETE.getBoolean() && path.getFileSystem() == FileSystems.getDefault())
{
deleteRecursiveUsingNixCommand(path, false);
return;
}
if (isDirectory(path))
forEach(path, PathUtils::deleteRecursive);
// The directory is now empty so now it can be smoked
// The directory is now empty, so now it can be smoked
delete(path);
}
@ -331,6 +388,12 @@ public final class PathUtils
*/
public static void deleteRecursive(Path path, RateLimiter rateLimiter)
{
if (USE_NIX_RECURSIVE_DELETE.getBoolean() && path.getFileSystem() == FileSystems.getDefault())
{
deleteRecursiveUsingNixCommand(path, false);
return;
}
deleteRecursive(path, rateLimiter, p -> deleteRecursive(p, rateLimiter));
}

View File

@ -51,6 +51,7 @@ import org.apache.cassandra.utils.FBUtilities;
import static java.util.Collections.synchronizedList;
import static java.util.concurrent.TimeUnit.MINUTES;
import static org.apache.cassandra.concurrent.Stage.MUTATION;
import static org.apache.cassandra.config.CassandraRelevantProperties.NON_GRACEFUL_SHUTDOWN;
import static org.apache.cassandra.utils.Clock.Global.nanoTime;
import static org.apache.cassandra.utils.Throwables.maybeFail;
@ -523,15 +524,20 @@ public class MessagingService extends MessagingServiceMBeanImpl
}
/**
* Wait for callbacks and don't allow any more to be created (since they could require writing hints)
* Wait for callbacks and don't allow anymore to be created (since they could require writing hints)
*/
public void shutdown()
{
shutdown(1L, MINUTES, true, true);
if (NON_GRACEFUL_SHUTDOWN.getBoolean())
// this branch is used in unit-tests when we really never restart a node and shutting down means the end of test
shutdownAbrubtly();
else
shutdown(1L, MINUTES, true, true);
}
public void shutdown(long timeout, TimeUnit units, boolean shutdownGracefully, boolean shutdownExecutors)
{
logger.debug("Shutting down: timeout={}s, gracefully={}, shutdownExecutors={}", units.toSeconds(timeout), shutdownGracefully, shutdownExecutors);
if (isShuttingDown)
{
logger.info("Shutdown was already called");
@ -555,7 +561,7 @@ public class MessagingService extends MessagingServiceMBeanImpl
() -> {
List<ExecutorService> inboundExecutors = new ArrayList<>();
inboundSockets.close(synchronizedList(inboundExecutors)::add).get();
ExecutorUtils.awaitTermination(1L, TimeUnit.MINUTES, inboundExecutors);
ExecutorUtils.awaitTermination(timeout, units, inboundExecutors);
},
() -> {
if (shutdownExecutors)
@ -587,6 +593,30 @@ public class MessagingService extends MessagingServiceMBeanImpl
}
}
public void shutdownAbrubtly()
{
logger.debug("Shutting down abruptly");
if (isShuttingDown)
{
logger.info("Shutdown was already called");
return;
}
isShuttingDown = true;
logger.info("Waiting for messaging service to quiesce");
// We may need to schedule hints on the mutation stage, so it's erroneous to shut down the mutation stage first
assert !MUTATION.executor().isShutdown();
callbacks.shutdownNow(false);
inboundSockets.close();
for (OutboundConnections pool : channelManagers.values())
pool.close(false);
maybeFail(socketFactory::shutdownNow,
inboundSink::clear,
outboundSink::clear);
}
private void shutdownExecutors(long deadlineNanos) throws TimeoutException, InterruptedException
{
socketFactory.shutdownNow();

View File

@ -76,7 +76,7 @@ public final class SchemaKeyspace
private static final Logger logger = LoggerFactory.getLogger(SchemaKeyspace.class);
private static final boolean FLUSH_SCHEMA_TABLES = Boolean.parseBoolean(System.getProperty("cassandra.test.flush_local_schema_changes", "true"));
private static final boolean FLUSH_SCHEMA_TABLES = CassandraRelevantProperties.FLUSH_LOCAL_SCHEMA_CHANGES.getBoolean();
private static final boolean IGNORE_CORRUPTED_SCHEMA_TABLES = Boolean.parseBoolean(System.getProperty("cassandra.ignore_corrupted_schema_tables", "false"));
/**

View File

@ -30,10 +30,12 @@ import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.Enumeration;
import io.netty.util.concurrent.FastThreadLocal;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.utils.logging.LoggingSupportFactory;
import io.netty.util.concurrent.FastThreadLocal;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.utils.logging.LoggingSupportFactory;
/**
* Custom {@link SecurityManager} and {@link Policy} implementation that only performs access checks
@ -46,6 +48,8 @@ import org.apache.cassandra.config.DatabaseDescriptor;
*/
public final class ThreadAwareSecurityManager extends SecurityManager
{
private static final Logger logger = LoggerFactory.getLogger(ThreadAwareSecurityManager.class);
public static final PermissionCollection noPermissions = new PermissionCollection()
{
public void add(Permission permission)
@ -81,6 +85,14 @@ public final class ThreadAwareSecurityManager extends SecurityManager
{
if (installed)
return;
// this line is needed - we need to make sure AccessControlException is loaded before we install this SM
// otherwise we may get into stackoverflow when javax.security is not allowed package, and ACE is tried to be
// loaded when it is going to be thrown from SM (class loader triggers SM to verify javax.security,
// it recognizes it as not allowed and attempts to throw it...)
//noinspection PlaceholderCountMatchesArgumentCount
logger.trace("Initialized thread aware security manager", AccessControlException.class.getName());
System.setSecurityManager(new ThreadAwareSecurityManager());
LoggingSupportFactory.getLoggingSupport().onStartup();
installed = true;

View File

@ -60,6 +60,7 @@ import org.junit.Assume;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.db.ColumnFamilyStore;
import org.apache.cassandra.db.Keyspace;
import org.apache.cassandra.dht.IPartitioner;
@ -184,6 +185,12 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
private INodeProvisionStrategy.Strategy nodeProvisionStrategy = INodeProvisionStrategy.Strategy.MultipleNetworkInterfaces;
private ShutdownExecutor shutdownExecutor = DEFAULT_SHUTDOWN_EXECUTOR;
{
// those properties may be set for unit-test optimizations; those should not be used when running dtests
CassandraRelevantProperties.FLUSH_LOCAL_SCHEMA_CHANGES.reset();
CassandraRelevantProperties.NON_GRACEFUL_SHUTDOWN.reset();
}
public AbstractBuilder(Factory<I, C, B> factory)
{
super(factory);
@ -1319,4 +1326,3 @@ public abstract class AbstractCluster<I extends IInstance> implements ICluster<I
INSTANCE;
}
}

View File

@ -58,6 +58,7 @@ import com.datastax.driver.core.DataType;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.exceptions.UnauthorizedException;
import com.datastax.shaded.netty.channel.EventLoopGroup;
import org.apache.cassandra.SchemaLoader;
import org.apache.cassandra.ServerTestUtils;
import org.apache.cassandra.Util;
@ -162,6 +163,16 @@ public abstract class CQLTester
CREATE_INDEX_NAME_REGEX);
private static final Pattern CREATE_INDEX_PATTERN = Pattern.compile(CREATE_INDEX_REGEX, Pattern.CASE_INSENSITIVE);
public static final NettyOptions IMMEDIATE_CONNECTION_SHUTDOWN_NETTY_OPTIONS = new NettyOptions()
{
@Override
public void onClusterClose(EventLoopGroup eventLoopGroup)
{
// shutdown driver connection immediatelly
eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS).syncUninterruptibly();
}
};
/** Return the current server version if supported by the driver, else
* the latest that is supported.
*
@ -574,7 +585,9 @@ public abstract class CQLTester
.addContactPoints(nativeAddr)
.withClusterName("Test Cluster")
.withPort(nativePort)
.withSocketOptions(socketOptions);
.withSocketOptions(socketOptions)
.withNettyOptions(IMMEDIATE_CONNECTION_SHUTDOWN_NETTY_OPTIONS);
if (user != null)
builder.withCredentials(user.username, user.password);

View File

@ -36,7 +36,9 @@ import org.apache.cassandra.io.sstable.format.SSTableReader;
import org.apache.cassandra.metrics.CassandraMetricsRegistry;
import org.apache.cassandra.metrics.TableMetrics;
import org.apache.cassandra.schema.KeyspaceParams;
import org.apache.cassandra.schema.SchemaConstants;
import org.apache.cassandra.schema.TableMetadata;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
@ -46,12 +48,19 @@ import static org.junit.Assert.*;
public class ColumnFamilyMetricTest
{
@BeforeClass
public static void defineSchema()
public static void defineSchema() throws Exception
{
SchemaLoader.prepareServer();
SchemaLoader.createKeyspace("Keyspace1",
KeyspaceParams.simple(1),
SchemaLoader.standardCFMD("Keyspace1", "Standard2"));
// we need this to properly initialize various static fields in the whole system
// since for the unit tests automatic schema flushing is disabled, the first flush may happen unexpectedly
// late - after the whole system is already running, and some static fields may remain uninitialized
// OTOH, late initialization of them may have creepy effects (for example NPEs in static initializers)
// disclaimer: this is not a proper way to fix that
StorageService.instance.forceKeyspaceFlush(SchemaConstants.SYSTEM_KEYSPACE_NAME, ColumnFamilyStore.FlushReason.UNIT_TESTS);
}
@Test

View File

@ -37,6 +37,7 @@ import org.apache.commons.lang3.RandomUtils;
import org.junit.Assert;
import org.junit.Test;
import org.apache.cassandra.config.CassandraRelevantProperties;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.assertj.core.api.Assertions;
import org.psjava.util.Triple;
@ -50,6 +51,7 @@ public class FileTest
private static final java.io.File dir;
static
{
CassandraRelevantProperties.USE_NIX_RECURSIVE_DELETE.setBoolean(false);
java.io.File parent = new java.io.File(JAVA_IO_TMPDIR.getString()); //checkstyle: permit this instantiation
String dirName = Long.toHexString(ThreadLocalRandom.current().nextLong());
while (new java.io.File(parent, dirName).exists()) //checkstyle: permit this instantiation

View File

@ -111,6 +111,8 @@ public class TpStatsTest extends CQLTester
createTable("CREATE TABLE %s (pk int, c int, PRIMARY KEY(pk))");
execute("INSERT INTO %s (pk, c) VALUES (?, ?)", 1, 1);
flush();
tool = ToolRunner.invokeNodetool("tpstats");
tool.assertOnCleanExit();
stdout = tool.getStdout();