diff --git a/build.xml b/build.xml index 043188d61c..13e0a9ebd8 100644 --- a/build.xml +++ b/build.xml @@ -1797,6 +1797,8 @@ + + diff --git a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java index 026078c9b8..5542d0d1e2 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java +++ b/test/simulator/main/org/apache/cassandra/simulator/SimulationRunner.java @@ -433,13 +433,16 @@ public class SimulationRunner } - private static Optional parseHex(Optional value) + public static Optional parseHex(Optional value) { - return value.map(s -> { - if (s.startsWith("0x")) - return Hex.parseLong(s, 2, s.length()); - throw new IllegalArgumentException("Invalid hex string: " + s); - }); + return value.map(SimulationRunner::parseHex); + } + + public static long parseHex(String s) + { + if (s.startsWith("0x")) + return Hex.parseLong(s, 2, s.length()); + throw new IllegalArgumentException("Invalid hex string: " + s); } private static final Pattern CHANCE_PATTERN = Pattern.compile("(uniform|(?qlog(\\((?[0-9]+)\\))?):)?(?0(\\.[0-9]+)?)(..(?0\\.[0-9]+))?", Pattern.CASE_INSENSITIVE); diff --git a/test/simulator/main/org/apache/cassandra/simulator/harry/HarryValidatingQuery.java b/test/simulator/main/org/apache/cassandra/simulator/harry/HarryValidatingQuery.java index 1ac7b550f6..05620b5517 100644 --- a/test/simulator/main/org/apache/cassandra/simulator/harry/HarryValidatingQuery.java +++ b/test/simulator/main/org/apache/cassandra/simulator/harry/HarryValidatingQuery.java @@ -113,8 +113,9 @@ public class HarryValidatingQuery extends SimulatedAction if (!throwables.isEmpty()) { - logger.error(String.format("Could not validate %d out of %d replicas %s", throwables.size(), replicas.size(), replicas), throwables.get(0)); - System.exit(0); + AssertionError error = new AssertionError(String.format("Could not validate %d out of %d replicas %s", throwables.size(), replicas.size(), replicas)); + throwables.forEach(error::addSuppressed); + throw error; } } @@ -137,9 +138,8 @@ public class HarryValidatingQuery extends SimulatedAction catch (Throwable t) { logger.error("Caught an exception while validating", t); - t.printStackTrace(); + throw t; } - finally { } } }; } diff --git a/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java b/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java index 81e70079aa..358faec3de 100644 --- a/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java +++ b/test/simulator/test/org/apache/cassandra/simulator/test/HarrySimulatorTest.java @@ -27,6 +27,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; @@ -80,6 +82,7 @@ import org.apache.cassandra.simulator.OrderOn; import org.apache.cassandra.simulator.RandomSource; import org.apache.cassandra.simulator.RunnableActionScheduler; import org.apache.cassandra.simulator.Simulation; +import org.apache.cassandra.simulator.SimulationException; import org.apache.cassandra.simulator.SimulationRunner; import org.apache.cassandra.simulator.SimulatorUtils; import org.apache.cassandra.simulator.cluster.ClusterActionListener.NoOpListener; @@ -187,6 +190,8 @@ public class HarrySimulatorTest public int rowsPerPhase = 10; @Option(name = {"--nodes-per-dc"}, description = "How many nodes per dc for replication") public int nodesPerDc = 3; + @Option(name = {"-s", "--seed"}, title = "0x", description = "What seed to run with; in hex format... example: 0x190e6ff01d6") + public String seed = null; public static void main(String... args) throws Throwable { @@ -201,6 +206,8 @@ public class HarrySimulatorTest public void test() throws Exception { rowsPerPhase = 1; + // To rerun a failing test for a given seed, uncomment the below and set the seed +// this.seed = ""; harryTest(); } @@ -436,7 +443,23 @@ public class HarrySimulatorTest try (CloseableIterator iter = iterator()) { while (iter.hasNext()) + { + checkForErrors(); iter.next(); + } + checkForErrors(); + } + } + + private void checkForErrors() + { + if (simulated.failures.hasFailure()) + { + AssertionError error = new AssertionError("Errors detected during simulation"); + // don't care about the stack trace... the issue is the errors found and not what part of the scheduler we stopped + error.setStackTrace(new StackTraceElement[0]); + simulated.failures.get().forEach(error::addSuppressed); + throw error; } } @@ -504,43 +527,38 @@ public class HarrySimulatorTest /** * Simulation entrypoint; syntax sugar for creating a simulation. */ - static void simulate(Consumer> configure, - Consumer instanceConfigUpdater, - Configuration.ConfigurationBuilder harryConfig, - String[] properties, - Function... phases) throws IOException + void simulate(Consumer> configure, + Consumer instanceConfigUpdater, + Configuration.ConfigurationBuilder harryConfig, + String[] properties, + Function... phases) throws IOException { try (WithProperties p = new WithProperties().with(properties)) { HarrySimulationBuilder factory = new HarrySimulationBuilder(harryConfig, instanceConfigUpdater); SimulationRunner.beforeAll(); - long seed = System.currentTimeMillis(); - // Development seed: - //long seed = 1687184561194L; - System.out.println("Simulation seed: " + seed + "L"); + long seed = SimulationRunner.parseHex(Optional.ofNullable(this.seed)).orElseGet(() -> new Random().nextLong()); + logger.info("Seed 0x{}", Long.toHexString(seed)); configure.accept(factory); try (ClusterSimulation clusterSimulation = factory.create(seed)) { - try - { - HarrySimulation simulation = clusterSimulation.simulation(); + HarrySimulation simulation = clusterSimulation.simulation(); - // For better determinism during startup, we allow instances to fully start (including daemon work) - for (int i = 0; i < phases.length; i++) - { - HarrySimulation current = simulation; - if (i == 0) - current = current.withScheduler(new RunnableActionScheduler.Immediate()).withSchedulers((s) -> Collections.emptyMap()); - current.withSchedule(phases[i]).run(); - } - } - catch (Throwable t) + // For better determinism during startup, we allow instances to fully start (including daemon work) + for (int i = 0; i < phases.length; i++) { - throw new AssertionError(String.format("Failed on seed %s", Long.toHexString(seed)), - t); + HarrySimulation current = simulation; + if (i == 0) + current = current.withScheduler(new RunnableActionScheduler.Immediate()).withSchedulers((s) -> Collections.emptyMap()); + current.withSchedule(phases[i]).run(); } } + catch (Throwable t) + { + if (t instanceof SimulationException) throw t; + throw new SimulationException(seed, "Failure creating the simulation", t); + } } } @@ -976,9 +994,7 @@ public class HarrySimulatorTest public void onFailure(Throwable t) { super.onFailure(t); - t.printStackTrace(); logger.error("Caught an exception, going to halt", t); - //while (true) { } } } } diff --git a/test/simulator/test/org/apache/cassandra/simulator/test/SimulationTestBase.java b/test/simulator/test/org/apache/cassandra/simulator/test/SimulationTestBase.java index d1ebbf89cd..88aaf5374c 100644 --- a/test/simulator/test/org/apache/cassandra/simulator/test/SimulationTestBase.java +++ b/test/simulator/test/org/apache/cassandra/simulator/test/SimulationTestBase.java @@ -206,7 +206,7 @@ public class SimulationTestBase long seed = System.currentTimeMillis(); // Development seed: //long seed = 1687184561194L; - System.out.println("Simulation seed: " + seed + "L"); + logger.info("Simulation seed: {}L", seed); configure.accept(factory); try (ClusterSimulation cluster = factory.create(seed)) {