java-topology/defects/clickhouse-java/unit/ClickHouseJavaTest.java
russell@unturf.com e9b981a309 clickhouse-java/freeswitch: CWE-407 findings
clickhouse-java-0001: ClickHouseLoadBalancingPolicy nodes/faultyNodes
LinkedList.contains O(N*F) per node selection — fix: LinkedHashSet O(1).
120x speedup at F=500 faulty nodes.

freeswitch-0001: switch_loadable_module_get_codecs_sorted re-parses
prefs[0..x-1] inside O(N^2) dedup loop — fix: pre-parse once O(N),
then compare pre-parsed structs. 13x speedup at N=50 (SWITCH_MAX_CODECS).
2026-03-30 09:03:28 -04:00

125 lines
5.1 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import java.util.*;
/**
* CWE-407 simulation: ClickHouseNodes load-balancing faulty-node contains scan
*
* Defect: ClickHouseLoadBalancingPolicy.FirstAlivePolicy.get() iterates
* manager.nodes (LinkedList<N>) and calls manager.faultyNodes.contains(node)
* for every iteration — O(N×F) where N = healthy nodes, F = faulty nodes.
*
* Fix: change nodes and faultyNodes to LinkedHashSet<N> for O(1) contains.
*/
public class ClickHouseJavaTest {
// --- Simulated defect: LinkedList for both collections ---
static int simulateDefect(int healthyCount, int faultyCount) {
LinkedList<String> nodes = new LinkedList<>();
LinkedList<String> faultyNodes = new LinkedList<>();
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
int ops = 0;
// Simulate FirstAlivePolicy.get() — finds first healthy non-faulty node
for (String n : nodes) {
ops++;
if (!faultyNodes.contains(n)) { // O(F) per iteration
break;
}
}
return ops;
}
// --- Simulated fix: LinkedHashSet for O(1) contains ---
static int simulateFix(int healthyCount, int faultyCount) {
LinkedHashSet<String> nodes = new LinkedHashSet<>();
LinkedHashSet<String> faultyNodes = new LinkedHashSet<>();
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
int ops = 0;
for (String n : nodes) {
ops++;
if (!faultyNodes.contains(n)) { // O(1) via HashSet
break;
}
}
return ops;
}
// --- Measure wall-clock ops for contains cost comparison ---
static long timeDefect(int healthyCount, int faultyCount, int iterations) {
LinkedList<String> nodes = new LinkedList<>();
LinkedList<String> faultyNodes = new LinkedList<>();
// Make all healthy nodes also appear in faulty (worst case: always scan all)
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
// faultyNodes doesn't contain healthy nodes, but is large enough to cost
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
long start = System.nanoTime();
for (int iter = 0; iter < iterations; iter++) {
for (String n : nodes) {
if (!faultyNodes.contains(n)) break;
}
}
return System.nanoTime() - start;
}
static long timeFix(int healthyCount, int faultyCount, int iterations) {
LinkedHashSet<String> nodes = new LinkedHashSet<>();
LinkedHashSet<String> faultyNodes = new LinkedHashSet<>();
for (int i = 0; i < healthyCount; i++) nodes.add("node-" + i);
for (int i = 0; i < faultyCount; i++) faultyNodes.add("faulty-" + i);
long start = System.nanoTime();
for (int iter = 0; iter < iterations; iter++) {
for (String n : nodes) {
if (!faultyNodes.contains(n)) break;
}
}
return System.nanoTime() - start;
}
public static void main(String[] args) {
System.out.println("=== CWE-407: ClickHouseNodes load-balancing faulty-node O(N×F) ===");
System.out.println();
// --- Correctness: ops to find first healthy node are the same ---
int HEALTHY = 10, FAULTY = 500;
int defectOps = simulateDefect(HEALTHY, FAULTY);
int fixOps = simulateFix(HEALTHY, FAULTY);
System.out.printf("PASS: defect ops=%d fix ops=%d (same result)%n", defectOps, fixOps);
assert defectOps == fixOps : "ops should match";
// --- Performance: warm up then measure ---
int ITER = 50_000;
// Warm up
for (int i = 0; i < 3; i++) { timeDefect(HEALTHY, FAULTY, ITER); timeFix(HEALTHY, FAULTY, ITER); }
long defectNs = timeDefect(HEALTHY, FAULTY, ITER);
long fixNs = timeFix(HEALTHY, FAULTY, ITER);
double ratio = (double) defectNs / fixNs;
System.out.printf("Defect (LinkedList.contains): %,d ns over %,d iterations%n", defectNs, ITER);
System.out.printf("Fix (HashSet.contains): %,d ns over %,d iterations%n", fixNs, ITER);
System.out.printf("Speedup ratio: %.1fx%n", ratio);
// --- Complexity demonstration at N=10, F varies ---
System.out.println();
System.out.println("Complexity demo (N=10 healthy, varies F faulty):");
System.out.printf(" %-6s %-12s %-12s %-8s%n", "F", "Defect(ns)", "Fix(ns)", "Ratio");
for (int f : new int[]{10, 50, 100, 250, 500, 1000}) {
long d = timeDefect(10, f, ITER);
long fx = timeFix(10, f, ITER);
System.out.printf(" %-6d %-12d %-12d %-8.1f%n", f, d, fx, (double) d / fx);
}
// --- Assert speedup is meaningful (at least 2x at F=500) ---
assert ratio >= 2.0 : String.format("Expected speedup >= 2x, got %.1fx", ratio);
System.out.println();
System.out.println("PASS: all assertions satisfied");
}
}