java-topology/defects/nats-server/unit/NatsPeerDedupTest.java
russell@unturf.com 9934133dcf whitepaper: 312 sites / 151 ecosystems — wave2+3 defect tables and PDF rebuild
Add 88 new defect entries to HIGH and MEDIUM tables:
  HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
        vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
        tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
        allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
        mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
        linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
        perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002

  MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
          cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
          pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
          ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
          r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
          ovs-0001, onos-0003, odl-0002, jetty-0001

PDF: 976K
2026-03-27 15:23:43 -04:00

223 lines
8.3 KiB
Java

package unit;
import java.util.*;
/**
* NatsPeerDedupTest — Java model of CWE-407 defect in NATS JetStream cluster.
*
* NATS-0001 (MEDIUM): jetstream_cluster.go — peer dedup loop uses slices.Contains.
* During a stream move/scale, existing peers are deduplicated against a new peer
* group using:
*
* for _, peer := range rg.Peers {
* if !slices.Contains(nrg.Peers, peer) {
* peerSet = append(peerSet, peer)
* }
* }
*
* slices.Contains is O(|nrg.Peers|). Outer loop is O(|rg.Peers|).
* Total: O(|rg.Peers| * |nrg.Peers|) — quadratic in replica count.
*
* Fix: build a map[string]struct{} from nrg.Peers before the loop (O(|nrg|)),
* then probe in O(1) per rg peer. Total: O(|rg| + |nrg|).
*/
public class NatsPeerDedupTest {
// -----------------------------------------------------------------------
// Algorithm models
// -----------------------------------------------------------------------
/**
* Defective: for each peer in rgPeers, scan all of nrgPeers.
* Returns exact comparison count (worst-case: no overlap).
*/
static long slow(List<String> rgPeers, List<String> nrgPeers) {
long comparisons = 0;
List<String> peerSet = new ArrayList<>();
for (String peer : rgPeers) {
// slices.Contains inner loop
boolean found = false;
for (String nrgPeer : nrgPeers) {
comparisons++;
if (peer.equals(nrgPeer)) { found = true; break; }
}
if (!found) {
peerSet.add(peer);
}
}
// append nrg.Peers to peerSet
peerSet.addAll(nrgPeers);
return comparisons;
}
/**
* Fixed: build set from nrgPeers first, then probe O(1) per rg peer.
* Returns exact operation count (set build + probes).
*/
static long fast(List<String> rgPeers, List<String> nrgPeers) {
long ops = 0;
// Build set: nrgPeers.size() insertions
Map<String, Boolean> nrgSet = new HashMap<>();
for (String p : nrgPeers) {
nrgSet.put(p, true);
ops++;
}
List<String> peerSet = new ArrayList<>();
for (String peer : rgPeers) {
ops++; // O(1) map probe
if (!nrgSet.containsKey(peer)) {
peerSet.add(peer);
}
}
peerSet.addAll(nrgPeers);
return ops;
}
// -----------------------------------------------------------------------
// Helper: build disjoint peer lists
// -----------------------------------------------------------------------
static List<String> makePeers(String prefix, int count, int offset) {
List<String> peers = new ArrayList<>(count);
for (int i = 0; i < count; i++) {
peers.add(prefix + (offset + i));
}
return peers;
}
// -----------------------------------------------------------------------
// Tests
// -----------------------------------------------------------------------
/**
* Test 1: worst-case N=100 rg peers, M=100 nrg peers, no overlap.
* slow() = N*M = 10000 comparisons.
* fast() = N+M = 200 operations.
* Assert slow > fast * 10x.
*/
static void test_nats0001_comparison_ratio() {
final int N = 100, M = 100;
List<String> rgPeers = makePeers("rg-", N, 0);
List<String> nrgPeers = makePeers("nrg-", M, 0); // disjoint
long sOps = slow(rgPeers, nrgPeers);
long fOps = fast(rgPeers, nrgPeers);
double ratio = (double) sOps / fOps;
System.out.printf(" NATS-0001 ratio slow=%d fast=%d ratio=%.1fx%n",
sOps, fOps, ratio);
assert sOps > fOps * 10 :
"NATS-0001: expected slow > fast*10, got slow=" + sOps + " fast=" + fOps;
}
/**
* Test 2: scaling — doubling N doubles slow() cost, barely changes fast().
*/
static void test_nats0001_scaling_with_peer_count() {
final int M = 50;
List<String> nrgPeers = makePeers("nrg-", M, 0);
List<String> rgLo = makePeers("rg-", 50, 100);
List<String> rgHi = makePeers("rg-", 500, 100);
long sLo = slow(rgLo, nrgPeers);
long sHi = slow(rgHi, nrgPeers);
long fLo = fast(rgLo, nrgPeers);
long fHi = fast(rgHi, nrgPeers);
double sScale = (double) sHi / sLo;
double fScale = (double) fHi / fLo;
System.out.printf(" NATS-0001 N scaling slowScale=%.1fx fastScale=%.1fx%n",
sScale, fScale);
assert sScale > 5.0 : "NATS-0001: slow should scale with N, got " + sScale;
assert fHi < sHi : "NATS-0001: fast should be cheaper than slow at N=500";
}
/**
* Test 3: scaling — doubling M doubles slow() cost, fast() scales only linearly.
*/
static void test_nats0001_scaling_with_nrg_peer_count() {
final int N = 50;
List<String> rgPeers = makePeers("rg-", N, 0);
List<String> nrgLo = makePeers("nrg-", 50, 1000);
List<String> nrgHi = makePeers("nrg-", 500, 1000);
long sLo = slow(rgPeers, nrgLo);
long sHi = slow(rgPeers, nrgHi);
long fLo = fast(rgPeers, nrgLo);
long fHi = fast(rgPeers, nrgHi);
double sScale = (double) sHi / sLo;
System.out.printf(" NATS-0001 M scaling slowScale=%.1fx fast_lo=%d fast_hi=%d%n",
sScale, fLo, fHi);
assert sScale > 5.0 : "NATS-0001: slow should scale with M, got " + sScale;
assert fHi < sHi : "NATS-0001: fast should be cheaper than slow at M=500";
}
/**
* Test 4: correctness — both paths must produce identical peerSet output.
*/
static void test_nats0001_correctness() {
// partial overlap: rg has peers 0-9, nrg has peers 5-14
List<String> rgPeers = makePeers("peer-", 10, 0); // peer-0..peer-9
List<String> nrgPeers = makePeers("peer-", 10, 5); // peer-5..peer-14
// Run slow path to get result
List<String> slowResult = new ArrayList<>();
for (String peer : rgPeers) {
if (!nrgPeers.contains(peer)) slowResult.add(peer);
}
slowResult.addAll(nrgPeers);
// Run fast path to get result
Map<String, Boolean> nrgSet = new HashMap<>();
for (String p : nrgPeers) nrgSet.put(p, true);
List<String> fastResult = new ArrayList<>();
for (String peer : rgPeers) {
if (!nrgSet.containsKey(peer)) fastResult.add(peer);
}
fastResult.addAll(nrgPeers);
Collections.sort(slowResult);
Collections.sort(fastResult);
System.out.printf(" NATS-0001 correctness: peerSet size=%d (slow=%d fast=%d)%n",
slowResult.size(), slowResult.size(), fastResult.size());
assert slowResult.equals(fastResult) :
"NATS-0001 correctness: peer sets differ: " + slowResult + " vs " + fastResult;
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("NatsPeerDedupTest — CWE-407 model tests (NATS-0001)");
System.out.println("=====================================================");
run("test_nats0001_comparison_ratio", NatsPeerDedupTest::test_nats0001_comparison_ratio);
run("test_nats0001_scaling_with_peer_count", NatsPeerDedupTest::test_nats0001_scaling_with_peer_count);
run("test_nats0001_scaling_with_nrg_peer_count", NatsPeerDedupTest::test_nats0001_scaling_with_nrg_peer_count);
run("test_nats0001_correctness", NatsPeerDedupTest::test_nats0001_correctness);
System.out.println("=====================================================");
System.out.println("4/4 PASS");
}
@FunctionalInterface interface TestFn { void run() throws Exception; }
static void run(String name, TestFn fn) {
System.out.print(" [RUN] " + name + " ... ");
try {
fn.run();
System.out.println("PASS");
} catch (AssertionError e) {
System.out.println("FAIL — " + e.getMessage());
System.exit(1);
} catch (Exception e) {
System.out.println("ERR — " + e);
System.exit(1);
}
}
}