wave13: 514/239 — flink/nifi/artemis + K8s/cilium/linkerd2 + ES/OpenSearch/Solr + hadoop/hbase/spark
This commit is contained in:
parent
424a2a7787
commit
8f0bc73afa
32 changed files with 3977 additions and 5 deletions
276
defects/cilium/unit/Cilium0003Algorithm.java
Normal file
276
defects/cilium/unit/Cilium0003Algorithm.java
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
package unit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Cilium0003Algorithm — CWE-407 unit test for cilium-0003
|
||||
*
|
||||
* cilium-0003: node/manager/manager.go:977-1073
|
||||
* for _, address := range oldNode.IPAddresses { // O(A) addresses
|
||||
* if slices.Contains(nodeIPsAdded, prefix) { // O(A) linear scan
|
||||
*
|
||||
* nodeIPsAdded is a []netip.Prefix built by appending one entry per address —
|
||||
* scanning it per-address is O(A²) total. Five such slices are scanned.
|
||||
*
|
||||
* Called from NodeUpdated() on every node add/update event.
|
||||
* In large ClusterMesh deployments (500+ nodes), every node event pays O(A²).
|
||||
*
|
||||
* SLOW: slices.Contains([]Prefix, p) — O(A) linear scan per address
|
||||
* FAST: map[Prefix]struct{} pre-built — O(1) per address
|
||||
*
|
||||
* No JUnit. Run: javac -d . Cilium0003Algorithm.java && java -ea unit.Cilium0003Algorithm
|
||||
*/
|
||||
public class Cilium0003Algorithm {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Data model — mirrors netip.Prefix (modeled as String for simplicity)
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
// Each "address" is a string like "10.0.0.1/32" or "fd00::1/128"
|
||||
// "nodeIPsAdded" is a list of such strings (the new addresses)
|
||||
// "oldIPAddresses" is the old list to diff against
|
||||
|
||||
static long slowOps = 0;
|
||||
static long fastOps = 0;
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SLOW: O(A²) — models removeNodeFromIPCache with slices.Contains per address
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* For each old address, check if it's in the added set.
|
||||
* Models: for _, addr := range oldIPAddresses { slices.Contains(nodeIPsAdded, addr) }
|
||||
* Returns the list of addresses to remove (not in added set).
|
||||
*/
|
||||
static List<String> computeRemovedSlow(List<String> oldAddresses, List<String> addedPrefixes) {
|
||||
List<String> toRemove = new ArrayList<>();
|
||||
for (String addr : oldAddresses) { // O(A)
|
||||
boolean found = false;
|
||||
for (String added : addedPrefixes) { // O(A) linear scan — defect
|
||||
slowOps++;
|
||||
if (added.equals(addr)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
toRemove.add(addr);
|
||||
}
|
||||
}
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
/**
|
||||
* Models the full removeNodeFromIPCache pattern:
|
||||
* five separate slices each scanned linearly.
|
||||
*/
|
||||
static int removeNodeFromIPCacheSlow(
|
||||
List<String> oldAddresses,
|
||||
List<String> nodeIPsAdded,
|
||||
List<String> ipsetEntries,
|
||||
List<String> healthIPsAdded,
|
||||
List<String> ingressIPsAdded,
|
||||
List<String> podCIDRsAdded
|
||||
) {
|
||||
int removeOps = 0;
|
||||
for (String addr : oldAddresses) { // O(A)
|
||||
// slices.Contains(nodeIPsAdded, prefix)
|
||||
boolean inNodeIPs = false;
|
||||
for (String p : nodeIPsAdded) { slowOps++; if (p.equals(addr)) { inNodeIPs = true; break; } }
|
||||
|
||||
// slices.Contains(ipsetEntries, ...)
|
||||
boolean inIpset = false;
|
||||
for (String p : ipsetEntries) { slowOps++; if (p.equals(addr)) { inIpset = true; break; } }
|
||||
|
||||
if (!inNodeIPs) { removeOps++; }
|
||||
}
|
||||
// pod CIDR removals
|
||||
for (String cidr : podCIDRsAdded) { // O(CIDR)
|
||||
boolean inPodCIDRs = false;
|
||||
for (String p : podCIDRsAdded) { slowOps++; if (p.equals(cidr)) { inPodCIDRs = true; break; } }
|
||||
}
|
||||
// health/ingress IPs
|
||||
for (String addr : oldAddresses) {
|
||||
for (String p : healthIPsAdded) { slowOps++; if (p.equals(addr)) break; }
|
||||
for (String p : ingressIPsAdded) { slowOps++; if (p.equals(addr)) break; }
|
||||
}
|
||||
return removeOps;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// FAST: O(A) — pre-build map[string]struct{} for each slice
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static List<String> computeRemovedFast(List<String> oldAddresses, List<String> addedPrefixes) {
|
||||
Set<String> addedSet = new HashSet<>(addedPrefixes.size() * 2);
|
||||
for (String p : addedPrefixes) { fastOps++; addedSet.add(p); } // O(A) one-time build
|
||||
|
||||
List<String> toRemove = new ArrayList<>();
|
||||
for (String addr : oldAddresses) { // O(A)
|
||||
fastOps++;
|
||||
if (!addedSet.contains(addr)) { // O(1) lookup
|
||||
toRemove.add(addr);
|
||||
}
|
||||
}
|
||||
return toRemove;
|
||||
}
|
||||
|
||||
static int removeNodeFromIPCacheFast(
|
||||
List<String> oldAddresses,
|
||||
List<String> nodeIPsAdded,
|
||||
List<String> ipsetEntries,
|
||||
List<String> healthIPsAdded,
|
||||
List<String> ingressIPsAdded,
|
||||
List<String> podCIDRsAdded
|
||||
) {
|
||||
// Pre-build all five sets — O(A) total one-time cost
|
||||
Set<String> nodeIPsSet = new HashSet<>(nodeIPsAdded);
|
||||
Set<String> ipsetSet = new HashSet<>(ipsetEntries);
|
||||
Set<String> healthSet = new HashSet<>(healthIPsAdded);
|
||||
Set<String> ingressSet = new HashSet<>(ingressIPsAdded);
|
||||
Set<String> podCIDRsSet = new HashSet<>(podCIDRsAdded);
|
||||
|
||||
int removeOps = 0;
|
||||
for (String addr : oldAddresses) { // O(A)
|
||||
fastOps++;
|
||||
if (!nodeIPsSet.contains(addr)) { // O(1)
|
||||
removeOps++;
|
||||
}
|
||||
if (!ipsetSet.contains(addr)) fastOps++;
|
||||
if (!healthSet.contains(addr)) fastOps++;
|
||||
if (!ingressSet.contains(addr)) fastOps++;
|
||||
}
|
||||
return removeOps;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/** Build A addresses. First half overlap with 'added', second half are old-only. */
|
||||
static List<String> buildAddresses(int A, String prefix) {
|
||||
List<String> addrs = new ArrayList<>(A);
|
||||
for (int i = 0; i < A; i++) {
|
||||
addrs.add(prefix + "10." + (i / 256) + "." + (i % 256) + ".1/32");
|
||||
}
|
||||
return addrs;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Tests
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
static void testCorrectness() {
|
||||
List<String> old = Arrays.asList("10.0.0.1/32", "10.0.0.2/32", "10.0.0.3/32");
|
||||
List<String> added = Arrays.asList("10.0.0.1/32", "10.0.0.3/32"); // 2 is removed
|
||||
|
||||
List<String> slowResult = computeRemovedSlow(old, added);
|
||||
List<String> fastResult = computeRemovedFast(old, added);
|
||||
|
||||
assert slowResult.size() == 1 : "slow: expected 1 removal, got " + slowResult.size();
|
||||
assert fastResult.size() == 1 : "fast: expected 1 removal, got " + fastResult.size();
|
||||
assert slowResult.get(0).equals("10.0.0.2/32") : "slow: wrong removal " + slowResult.get(0);
|
||||
assert fastResult.get(0).equals("10.0.0.2/32") : "fast: wrong removal " + fastResult.get(0);
|
||||
System.out.println("PASS correctness: removal diff verified");
|
||||
}
|
||||
|
||||
static void testOpsCount_A50() {
|
||||
int A = 50;
|
||||
List<String> old = buildAddresses(A, "old-");
|
||||
List<String> added = buildAddresses(A / 2, "old-"); // first half overlap
|
||||
|
||||
slowOps = 0;
|
||||
List<String> slowResult = computeRemovedSlow(old, added);
|
||||
long measuredSlowOps = slowOps;
|
||||
|
||||
fastOps = 0;
|
||||
List<String> fastResult = computeRemovedFast(old, added);
|
||||
long measuredFastOps = fastOps;
|
||||
|
||||
assert slowResult.size() == fastResult.size()
|
||||
: "sizes differ: " + slowResult.size() + " vs " + fastResult.size();
|
||||
|
||||
double ratio = (double) measuredSlowOps / Math.max(measuredFastOps, 1);
|
||||
System.out.printf("PASS ops_count A=%d: slowOps=%d fastOps=%d ratio=%.1fx%n",
|
||||
A, measuredSlowOps, measuredFastOps, ratio);
|
||||
assert measuredSlowOps > measuredFastOps * 5
|
||||
: "expected slowOps >> fastOps, got slow=" + measuredSlowOps + " fast=" + measuredFastOps;
|
||||
}
|
||||
|
||||
static void testPerf_A100_NodeUpdate() {
|
||||
int A = 100;
|
||||
List<String> oldAddrs = buildAddresses(A, "");
|
||||
List<String> nodeIPsAdded = buildAddresses(A / 2, "");
|
||||
List<String> ipsetEntries = buildAddresses(A / 4, "");
|
||||
List<String> healthIPs = buildAddresses(2, "h-");
|
||||
List<String> ingressIPs = buildAddresses(2, "i-");
|
||||
List<String> podCIDRs = buildAddresses(10, "c-");
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
int slowResult = 0;
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
slowResult += removeNodeFromIPCacheSlow(
|
||||
oldAddrs, nodeIPsAdded, ipsetEntries, healthIPs, ingressIPs, podCIDRs);
|
||||
}
|
||||
long slowMs = (System.nanoTime() - t0) / 1_000_000;
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
int fastResult = 0;
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
fastResult += removeNodeFromIPCacheFast(
|
||||
oldAddrs, nodeIPsAdded, ipsetEntries, healthIPs, ingressIPs, podCIDRs);
|
||||
}
|
||||
long fastMs = (System.nanoTime() - t1) / 1_000_000;
|
||||
|
||||
assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult;
|
||||
System.out.printf("PASS perf A=%d 5000 node-updates: slow=%dms fast=%dms ratio=%.1fx%n",
|
||||
A, slowMs, fastMs, (double) slowMs / Math.max(fastMs, 1));
|
||||
assert slowMs >= fastMs :
|
||||
"expected slow >= fast, got slow=" + slowMs + "ms fast=" + fastMs + "ms";
|
||||
}
|
||||
|
||||
static void testPerf_A300_ClusterMesh_stress() {
|
||||
int A = 300;
|
||||
List<String> oldAddrs = buildAddresses(A, "");
|
||||
List<String> nodeIPsAdded = buildAddresses(A, ""); // all added (worst-case scan)
|
||||
List<String> ipsetEntries = buildAddresses(A / 2, "");
|
||||
List<String> healthIPs = buildAddresses(2, "h-");
|
||||
List<String> ingressIPs = buildAddresses(2, "i-");
|
||||
List<String> podCIDRs = buildAddresses(A / 3, "c-");
|
||||
|
||||
long t0 = System.nanoTime();
|
||||
int slowResult = 0;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
slowResult += removeNodeFromIPCacheSlow(
|
||||
oldAddrs, nodeIPsAdded, ipsetEntries, healthIPs, ingressIPs, podCIDRs);
|
||||
}
|
||||
long slowMs = (System.nanoTime() - t0) / 1_000_000;
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
int fastResult = 0;
|
||||
for (int i = 0; i < 1000; i++) {
|
||||
fastResult += removeNodeFromIPCacheFast(
|
||||
oldAddrs, nodeIPsAdded, ipsetEntries, healthIPs, ingressIPs, podCIDRs);
|
||||
}
|
||||
long fastMs = (System.nanoTime() - t1) / 1_000_000;
|
||||
|
||||
assert slowResult == fastResult : "results differ: " + slowResult + " vs " + fastResult;
|
||||
System.out.printf("PASS stress A=%d 1000 node-updates: slow=%dms fast=%dms ratio=%.1fx%n",
|
||||
A, slowMs, fastMs, (double) slowMs / Math.max(fastMs, 1));
|
||||
assert slowMs >= fastMs :
|
||||
"expected slow >= fast, got slow=" + slowMs + "ms fast=" + fastMs + "ms";
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Main
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("=== Cilium0003Algorithm: node manager IP address dedup (cilium-0003) ===");
|
||||
testCorrectness();
|
||||
testOpsCount_A50();
|
||||
testPerf_A100_NodeUpdate();
|
||||
testPerf_A300_ClusterMesh_stress();
|
||||
System.out.println("4/4 PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue