java-topology/defects/ray/unit/RayTest.java

186 lines
7.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.

package unit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
/**
* RayTest
*
* Models CWE-407 defects in ray-project/ray:
*
* RAY-001 (MEDIUM) — local/node_provider.py ClusterState.__init__ and
* OnPremCoordinatorState.__init__: `list_of_node_ips = list(...)` followed
* by `for worker_ip in workers: if worker_ip not in list_of_node_ips`.
* O(N) linear scan per node × O(N) nodes = O(N²) during cluster reconciliation.
* Fix: set(worker_ips) for O(1) average membership.
*
* All measurements are instrumented operation counts, not wall-clock timing.
*/
public class RayTest {
// -----------------------------------------------------------------------
// RAY-001 modelling helpers
//
// Defective: ArrayList (list_of_node_ips) membership — O(N) scan per node
// Fixed: HashSet (node_ip_set) membership — O(1) average per node
//
// Returns total membership-test cost across all nodes.
// -----------------------------------------------------------------------
/**
* Models ClusterState.__init__ reconciliation — defective path.
*
* list_of_node_ips is built as a List, then each of the N tracked workers
* is tested against it with `not in`, costing O(N) per check → O(N²) total.
*/
static long ray001Defective(int numWorkers, int numNodeIps) {
// Build list_of_node_ips (contains the valid IPs)
ArrayList<Integer> listOfNodeIps = new ArrayList<>();
for (int i = 0; i < numNodeIps; i++) {
listOfNodeIps.add(i);
}
// workers dict: tracked workers include some not in list_of_node_ips
// (half valid, half stale)
ArrayList<Integer> workers = new ArrayList<>();
for (int i = 0; i < numWorkers; i++) {
workers.add(i); // some overlap with listOfNodeIps, some don't
}
long comparisons = 0;
for (int workerIp : workers) {
// model `if worker_ip not in list_of_node_ips` — O(N) scan
comparisons += listOfNodeIps.size(); // worst-case linear scan cost
// actual check (for correctness)
if (!listOfNodeIps.contains(workerIp)) {
// would del workers[worker_ip]
}
}
return comparisons;
}
/**
* Models ClusterState.__init__ reconciliation — fixed path.
*
* node_ip_set is a HashSet; each membership test is O(1).
*/
static long ray001Fixed(int numWorkers, int numNodeIps) {
HashSet<Integer> nodeIpSet = new HashSet<>();
for (int i = 0; i < numNodeIps; i++) {
nodeIpSet.add(i);
}
ArrayList<Integer> workers = new ArrayList<>();
for (int i = 0; i < numWorkers; i++) {
workers.add(i);
}
long lookups = 0;
for (int workerIp : workers) {
// model `if worker_ip not in node_ip_set` — O(1)
lookups++; // one hash lookup per worker
if (!nodeIpSet.contains(workerIp)) {
// would del workers[worker_ip]
}
}
return lookups;
}
// -----------------------------------------------------------------------
// Test 1 — RAY-001: defective O(N²) vs fixed O(N) at N=200 nodes
// -----------------------------------------------------------------------
static void test1_ray001_quadraticVsLinear() {
int N = 200;
long defectCost = ray001Defective(N, N);
long fixedCost = ray001Fixed(N, N);
System.out.printf("test1 RAY-001: N=%d nodes defect=%d fixed=%d%n",
N, defectCost, fixedCost);
assert defectCost > fixedCost
: "defect must be more expensive than fix at N=" + N;
// defective: each of N workers scans list of size N → N*N total
long expectedDefect = (long) N * N;
assert defectCost == expectedDefect
: "expected defect cost=" + expectedDefect + " got=" + defectCost;
double ratio = (double) defectCost / Math.max(1, fixedCost);
assert ratio > 10.0
: "expected ratio>10x for N=" + N + ", got " + ratio;
}
// -----------------------------------------------------------------------
// Test 2 — RAY-001: scaling — doubling N grows defect quadratically
// -----------------------------------------------------------------------
static void test2_ray001_scalingGrowth() {
int N1 = 100;
int N2 = 200; // double N
long d1 = ray001Defective(N1, N1);
long d2 = ray001Defective(N2, N2);
long f1 = ray001Fixed(N1, N1);
long f2 = ray001Fixed(N2, N2);
double defectGrowth = (double) d2 / Math.max(1, d1);
double fixedGrowth = (double) f2 / Math.max(1, f1);
System.out.printf("test2 RAY-001: N1=%d N2=%d defect_growth=%.2fx fixed_growth=%.2fx%n",
N1, N2, defectGrowth, fixedGrowth);
// defect should grow ~4x when N doubles (O(N²))
assert defectGrowth > 3.5
: "defect should grow ~4x when N doubles (O(N²)), got " + defectGrowth;
// fixed should grow ~2x when N doubles (O(N))
assert fixedGrowth >= 1.8 && fixedGrowth <= 2.2
: "fixed should grow ~2x when N doubles (O(N)), got " + fixedGrowth;
assert defectGrowth > fixedGrowth
: "defect growth must exceed fixed growth";
}
// -----------------------------------------------------------------------
// Test 3 — RAY-001: OnPremCoordinatorState same pattern, N=300 nodes
// -----------------------------------------------------------------------
static void test3_ray001_onPremCoordinator() {
// Models OnPremCoordinatorState.__init__ — same list vs set pattern
// for node_ip in list(nodes): if node_ip not in list_of_node_ips
int N = 300;
long defectCost = ray001Defective(N, N);
long fixedCost = ray001Fixed(N, N);
double ratio = (double) defectCost / Math.max(1, fixedCost);
System.out.printf("test3 RAY-001 OnPrem: N=%d defect=%d fixed=%d ratio=%.0fx%n",
N, defectCost, fixedCost, ratio);
assert defectCost > fixedCost
: "defect must be more expensive at N=" + N;
assert ratio > 50.0
: "expected ratio>50x at N=300, got " + ratio;
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
System.out.println("=== RayTest ===");
System.out.println("Modelling CWE-407: RAY-001 local node_provider list_of_node_ips O(N²) scan");
System.out.println();
test1_ray001_quadraticVsLinear();
System.out.println(" PASS test1_ray001_quadraticVsLinear");
test2_ray001_scalingGrowth();
System.out.println(" PASS test2_ray001_scalingGrowth");
test3_ray001_onPremCoordinator();
System.out.println(" PASS test3_ray001_onPremCoordinator");
System.out.println();
System.out.println("3/3 PASS");
}
}