From 876f7b05914a68ae314e5e420667272cf4415cf9 Mon Sep 17 00:00:00 2001 From: Aleksei Zotov Date: Fri, 17 Sep 2021 12:14:12 +0400 Subject: [PATCH] Fix flaky test DatabaseDescriptorRefTest. Patch by Aleksei Zotov; reviewed by Ekaterina Dimitrova and John Meredith for CASSANDRA-16862 --- .../config/DatabaseDescriptorRefTest.java | 36 ++++++++++++++----- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java index 1c07a7d88d..791212d275 100644 --- a/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java +++ b/test/unit/org/apache/cassandra/config/DatabaseDescriptorRefTest.java @@ -35,6 +35,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import org.junit.Test; @@ -145,6 +146,7 @@ public class DatabaseDescriptorRefTest static final Set checkedClasses = new HashSet<>(Arrays.asList(validClasses)); @Test + @SuppressWarnings({"DynamicRegexReplaceableByCompiledPattern", "UseOfSystemOutOrSystemErr"}) public void testDatabaseDescriptorRef() throws Throwable { PrintStream out = System.out; @@ -152,6 +154,7 @@ public class DatabaseDescriptorRefTest ThreadMXBean threads = ManagementFactory.getThreadMXBean(); int threadCount = threads.getThreadCount(); + List existingThreadIDs = Arrays.stream(threads.getAllThreadIds()).boxed().collect(Collectors.toList()); ClassLoader delegate = Thread.currentThread().getContextClassLoader(); @@ -210,7 +213,29 @@ public class DatabaseDescriptorRefTest assertEquals("thread started", threadCount, threads.getThreadCount()); - Class cDatabaseDescriptor = Class.forName("org.apache.cassandra.config.DatabaseDescriptor", true, cl); + Class databaseDescriptorClass = Class.forName("org.apache.cassandra.config.DatabaseDescriptor", true, cl); + + // During DatabaseDescriptor instantiation some threads are spawned. We need to take them into account in + // threadCount variable, otherwise they will be considered as new threads spawned by methods below. There is a + // trick: in case of multiple runs of this test in the same JVM the number of such threads will be multiplied by + // the number of runs. That's because DatabaseDescriptor is instantiated via a different class loader. So in + // order to keep calculation logic correct, we ignore existing threads that were spawned during the previous + // runs and change threadCount variable for the new threads only (if they have some specific names). + for (ThreadInfo threadInfo : threads.getThreadInfo(threads.getAllThreadIds())) + { + // All existing threads have been already taken into account in threadCount variable, so we ignore them + if (existingThreadIDs.contains(threadInfo.getThreadId())) + continue; + // Logback AsyncAppender thread needs to be taken into account + if (threadInfo.getThreadName().equals("AsyncAppender-Worker-ASYNC")) + threadCount++; + // Logback basic threads need to be taken into account + if (threadInfo.getThreadName().matches("logback-\\d+")) + threadCount++; + // Dynamic Attach thread needs to be taken into account, generally it is spawned by IDE + if (threadInfo.getThreadName().equals("Attach Listener")) + threadCount++; + } for (String methodName : new String[]{ "clientInitialization", @@ -228,16 +253,9 @@ public class DatabaseDescriptorRefTest // "applyRequestScheduler", }) { - Method method = cDatabaseDescriptor.getDeclaredMethod(methodName); + Method method = databaseDescriptorClass.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()))