diff --git a/batch-publish/build.gradle b/batch-publish/build.gradle index 2355212..1967e94 100644 --- a/batch-publish/build.gradle +++ b/batch-publish/build.gradle @@ -39,7 +39,7 @@ dependencies { implementation 'io.nats:jnats:2.26.3-SNAPSHOT' implementation 'org.jspecify:jspecify:1.0.0' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' diff --git a/chaos-runner/build.gradle b/chaos-runner/build.gradle index 438c58e..51979bb 100644 --- a/chaos-runner/build.gradle +++ b/chaos-runner/build.gradle @@ -41,7 +41,7 @@ repositories { } dependencies { - implementation 'io.nats:jnats-server-runner:3.1.1' + implementation 'io.nats:jnats-server-runner:4.0.2' // this is only for the example and the uber jar won't include it implementation 'io.nats:jnats:2.26.2' diff --git a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java index 8cc1a35..844caea 100644 --- a/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java +++ b/chaos-runner/src/main/java/io/synadia/chaos/ChaosRunner.java @@ -6,6 +6,7 @@ import io.nats.ClusterDefaults; import io.nats.ClusterInsert; import io.nats.ClusterNode; +import io.nats.JsConfig; import io.nats.NatsRunnerUtils; import io.nats.NatsServerRunner; @@ -93,11 +94,10 @@ private NatsServerRunner createRunner(int index) throws Exception { ClusterInsert ci = clusterInserts.get(index); NatsServerRunner.Builder b = NatsServerRunner.builder() .debug(false) - .jetstream(true) + .jetstream(js) .configInserts(ci.configInserts) .port(ci.node.port) - .skipConnectValidate() - ; + .skipConnectValidate(); return b.build(); } @@ -110,11 +110,19 @@ private void scheduleUp() { } private void downTask() { + // natsServerRunners is an ArrayList and shutdownServers() iterates it under this + // lock. executor.shutdown() does not interrupt a task already running, so without + // the lock a structural change here can race that iteration. + INSTANCE_LOCK.lock(); try { + if (INSTANCE == null) { + // shut down before this task got to run + return; + } if (specificPort != -1) { for (int i = 0; i < natsServerRunners.size(); i++) { NatsServerRunner nsr = natsServerRunners.get(i); - if (nsr.getPort() == specificPort) { + if (nsr.getNatsPort() == specificPort) { downIx = i; break; } @@ -125,22 +133,39 @@ else if (random) { } NatsServerRunner runner = natsServerRunners.remove(downIx); - printer.out(CR_LABEL, "DOWN", runner.getPort()); + printer.out(CR_LABEL, "DOWN", runner.getNatsPort()); clusterInserts.add(clusterInserts.remove(downIx)); runner.close(); scheduleUp(); } catch (Throwable e) { - printer.out(CR_LABEL, "DOWN/EX", e); + printer.out(CR_LABEL, "DOWN/EX", e); + } + finally { + INSTANCE_LOCK.unlock(); } } private void upTask() { try { NatsServerRunner runner = createRunner(servers - 1); - printer.out(CR_LABEL, "UP", runner.getPort()); - natsServerRunners.add(runner); - scheduleDown(delay); + INSTANCE_LOCK.lock(); + try { + if (INSTANCE == null) { + // Shut down while this server was starting. executor.shutdown() does not + // interrupt a task already running, so we got here after shutdownServers() + // had already closed out the list and the jvm hook was removed. Close it + // here, or it outlives the jvm still holding its port. + try { runner.close(); } catch (Exception ignore) {} + return; + } + printer.out(CR_LABEL, "UP", runner.getNatsPort()); + natsServerRunners.add(runner); + scheduleDown(delay); + } + finally { + INSTANCE_LOCK.unlock(); + } } catch (Throwable e) { printer.out(CR_LABEL, "UP/EX: ", e); @@ -148,7 +173,7 @@ private void upTask() { } } - private static void deleteDirContents(Path dir, boolean alsoDeleteSelf) throws IOException { + private static void deleteDirContents(Path dir, boolean alsoDeleteSelf) { File fDir = dir.toFile(); if (fDir.exists()) { File[] items = fDir.listFiles(); @@ -208,9 +233,10 @@ else if (!a.workDirectory.toFile().exists()) { throw new IllegalArgumentException("Invalid specific port"); } List inserts = new ArrayList<>(); - ClusterNode cn; - Path jsStorePath = Paths.get(jsStoreDirBase.toString(), "" + port); - cn = ClusterNode.builder() + // jsStoreDirBase is only set when js is on, and ClusterNode takes a null + // jsStoreDir, which is what the cluster branch ends up with in that case too + Path jsStorePath = js ? Paths.get(jsStoreDirBase.toString(), "" + port) : null; + ClusterNode cn = ClusterNode.builder() .port(port) .listen(listen) .monitor(monitor < 1 ? null : monitor) @@ -221,16 +247,9 @@ else if (!a.workDirectory.toFile().exists()) { inserts.add("http: " + monitor); } if (js) { - String storeDir = jsStorePath.toString(); - if (File.separatorChar == '\\') { - storeDir = storeDir.replace("\\", "\\\\").replace("/", "\\\\"); - } - else { - storeDir = storeDir.replace("\\", "/"); - } - inserts.add("jetstream {"); - inserts.add(" store_dir=" + storeDir); - inserts.add("}"); + // as of jnats-server-runner 4.0.2 JsConfig cleans and escapes the dir it is + // given, the same as the cluster branch gets by way of createClusterInserts + inserts.addAll(new JsConfig(jsStorePath).configInserts); } inserts.add("server_name=" + serverNamePrefix); @@ -266,7 +285,10 @@ else if (!a.workDirectory.toFile().exists()) { // delete jsStoreDirs for clean start if (js) { for (ClusterInsert ci : clusterInserts) { - deleteDirContents(ci.node.jsStoreDir, false); + // jsStoreDir is nullable on ClusterNode, so it might not have been given one + if (ci.node.jsStoreDir != null) { + deleteDirContents(ci.node.jsStoreDir, false); + } } } @@ -307,12 +329,13 @@ public static ChaosRunner start(ChaosArguments a, ChaosPrinter printer) throws E } INSTANCE = new ChaosRunner(a, finalPrinter); + INSTANCE_ARGUMENTS = a; APP_SHUTDOWN_HOOK_THREAD = new Thread("app-shutdown-hook") { @Override public void run() { - shutdownServers(); shutdownExecutor(); + shutdownServers(); finalPrinter.out(CR_LABEL, "EXIT"); } }; @@ -356,7 +379,11 @@ public static void shutdown() { public static void shutdownExecutor() { INSTANCE_LOCK.lock(); try { - INSTANCE.executor.shutdown(); + // guard matches shutdownServers(). This is public and is also reachable a + // second time when the jvm hook and an explicit shutdown() overlap. + if (INSTANCE != null) { + INSTANCE.executor.shutdown(); + } } finally { INSTANCE_LOCK.unlock(); @@ -384,6 +411,7 @@ private static void shutdownServers() { try { runner.close(); } catch (Exception ignore) {} } INSTANCE = null; + INSTANCE_ARGUMENTS = null; } } finally { diff --git a/counters/build.gradle b/counters/build.gradle index 2166dda..ca124e8 100644 --- a/counters/build.gradle +++ b/counters/build.gradle @@ -40,7 +40,7 @@ dependencies { implementation 'io.synadia:direct-batch:0.1.4' implementation 'org.jspecify:jspecify:1.0.0' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'commons-codec:commons-codec:1.20.0' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' testImplementation 'nl.jqno.equalsverifier:equalsverifier:4.2.3' diff --git a/direct-batch/build.gradle b/direct-batch/build.gradle index c350383..95b281a 100644 --- a/direct-batch/build.gradle +++ b/direct-batch/build.gradle @@ -44,7 +44,7 @@ dependencies { implementation 'io.nats:jnats:2.26.2' implementation 'org.jspecify:jspecify:1.0.0' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' diff --git a/encoded-kv/build.gradle b/encoded-kv/build.gradle index d08683b..edab389 100644 --- a/encoded-kv/build.gradle +++ b/encoded-kv/build.gradle @@ -44,7 +44,7 @@ dependencies { implementation 'io.nats:jnats:2.26.2' testImplementation 'commons-codec:commons-codec:1.18.0' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' diff --git a/js-publish-extensions/build.gradle b/js-publish-extensions/build.gradle index 18a1df2..c2182b1 100644 --- a/js-publish-extensions/build.gradle +++ b/js-publish-extensions/build.gradle @@ -51,7 +51,7 @@ dependencies { implementation 'io.nats:jnats:2.26.2' implementation 'io.synadia:retrier:0.2.1' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' diff --git a/pcgroups-cli/build.gradle b/pcgroups-cli/build.gradle index d92d1e1..17757c4 100644 --- a/pcgroups-cli/build.gradle +++ b/pcgroups-cli/build.gradle @@ -20,7 +20,7 @@ dependencies { implementation project(':') // ':' means root project which is 'pcgroups' implementation 'info.picocli:picocli:4.7.5' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } diff --git a/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar b/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index 8bdaf60..0000000 Binary files a/pcgroups-cli/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties b/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index 2a84e18..0000000 --- a/pcgroups-cli/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/pcgroups/build.gradle b/pcgroups/build.gradle index 979c336..976de84 100644 --- a/pcgroups/build.gradle +++ b/pcgroups/build.gradle @@ -35,7 +35,7 @@ dependencies { api 'io.nats:jnats:2.26.2' api 'org.jspecify:jspecify:1.0.0' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' } diff --git a/request-many/build.gradle b/request-many/build.gradle index 7ce6627..77d7bc4 100644 --- a/request-many/build.gradle +++ b/request-many/build.gradle @@ -43,7 +43,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.26.2' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' diff --git a/retrier/build.gradle b/retrier/build.gradle index 006fd53..74284ad 100644 --- a/retrier/build.gradle +++ b/retrier/build.gradle @@ -43,7 +43,7 @@ repositories { dependencies { implementation 'io.nats:jnats:2.26.2' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' testImplementation 'com.github.stefanbirkner:system-lambda:1.2.1' diff --git a/schedule-message/build.gradle b/schedule-message/build.gradle index a5ae443..3a892ae 100644 --- a/schedule-message/build.gradle +++ b/schedule-message/build.gradle @@ -41,7 +41,7 @@ dependencies { implementation 'org.jspecify:jspecify:1.0.0' implementation 'io.synadia:counters:0.2.2' - testImplementation 'io.nats:jnats-server-runner:3.1.1' + testImplementation 'io.nats:jnats-server-runner:4.0.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.14.1' testImplementation 'org.junit.platform:junit-platform-launcher:1.14.3' }