package unit; import java.util.*; /** * Linkerd2Test — CWE-407 benchmark for linkerd2-0001 * * linkerd2-0001: federatedService.update() slices.Contains O(n) inside two * for-range loops over remoteDiscovery slices → O(n²) diff computation * * Model: * N = number of remote discovery IDs (cluster service references) * * SLOW: for each new ID, slices.Contains(oldSlice) → O(N²) diff * FAST: map-based set for O(N) diff */ public class Linkerd2Test { // ------------------------------------------------------------------------- // Simulated remoteDiscoveryID type (comparable struct in Go) // ------------------------------------------------------------------------- static class RemoteDiscoveryID { final String cluster; final String service; final String namespace; RemoteDiscoveryID(String cluster, String service, String namespace) { this.cluster = cluster; this.service = service; this.namespace = namespace; } @Override public boolean equals(Object o) { if (!(o instanceof RemoteDiscoveryID)) return false; RemoteDiscoveryID r = (RemoteDiscoveryID) o; return cluster.equals(r.cluster) && service.equals(r.service) && namespace.equals(r.namespace); } @Override public int hashCode() { return Objects.hash(cluster, service, namespace); } } // ------------------------------------------------------------------------- // SLOW: slices.Contains inside for-range loops → O(N²) // ------------------------------------------------------------------------- static long updateFederatedService_slow(List oldSlice, List newSlice) { long ops = 0; // adds: for each new, scan old for membership for (RemoteDiscoveryID id : newSlice) { for (RemoteDiscoveryID old : oldSlice) { ops++; if (old.equals(id)) break; } } // removes: for each old, scan new for membership for (RemoteDiscoveryID id : oldSlice) { for (RemoteDiscoveryID nw : newSlice) { ops++; if (nw.equals(id)) break; } } return ops; } // ------------------------------------------------------------------------- // FAST: map-based set → O(N) diff // ------------------------------------------------------------------------- static long updateFederatedService_fast(List oldSlice, List newSlice) { long ops = 0; // Build new set: O(N) Set newSet = new HashSet<>(newSlice); Set oldSet = new HashSet<>(oldSlice); // adds: for each new, O(1) map lookup for (RemoteDiscoveryID id : newSet) { ops++; oldSet.contains(id); } // removes: for each old, O(1) map lookup for (RemoteDiscoveryID id : oldSet) { ops++; newSet.contains(id); } return ops; } // ------------------------------------------------------------------------- // Helpers // ------------------------------------------------------------------------- static List makeIDs(int count) { List ids = new ArrayList<>(count); for (int i = 0; i < count; i++) { ids.add(new RemoteDiscoveryID( "cluster-" + (i % 5), "svc-" + i, "ns-" + (i % 10) )); } return ids; } static void bench(String label, long sOps, long fOps) { System.out.printf(" %-55s slow=%9d fast=%7d ratio=%5.1fx%n", label, sOps, fOps, (double) sOps / Math.max(fOps, 1)); } // ------------------------------------------------------------------------- // Main // ------------------------------------------------------------------------- public static void main(String[] args) { System.out.println("Linkerd2Test — CWE-407 linkerd2-0001 federated service discovery quadratic dedup"); System.out.println(); // --- N=100 IDs --- { int N = 100; List oldIDs = makeIDs(N); // new = old + 10 additions - 10 removals → simulate update List newIDs = new ArrayList<>(makeIDs(N)); newIDs.subList(0, 10).clear(); for (int i = N; i < N + 10; i++) { newIDs.add(new RemoteDiscoveryID("cluster-0", "svc-" + i, "ns-0")); } long sOps = updateFederatedService_slow(oldIDs, newIDs); long fOps = updateFederatedService_fast(oldIDs, newIDs); bench("N=100 IDs (10 adds, 10 removes)", sOps, fOps); assert sOps > fOps * 10 : "Expected slow >> fast, got slow=" + sOps + " fast=" + fOps; } // --- N=500 IDs --- { int N = 500; List oldIDs = makeIDs(N); List newIDs = new ArrayList<>(makeIDs(N)); newIDs.subList(0, 50).clear(); for (int i = N; i < N + 50; i++) { newIDs.add(new RemoteDiscoveryID("cluster-0", "svc-" + i, "ns-0")); } long sOps = updateFederatedService_slow(oldIDs, newIDs); long fOps = updateFederatedService_fast(oldIDs, newIDs); bench("N=500 IDs", sOps, fOps); assert sOps > fOps * 25 : "Expected slow >> fast, got slow=" + sOps + " fast=" + fOps; } // --- N=1000 IDs --- { int N = 1000; List oldIDs = makeIDs(N); List newIDs = new ArrayList<>(makeIDs(N)); newIDs.subList(0, 100).clear(); for (int i = N; i < N + 100; i++) { newIDs.add(new RemoteDiscoveryID("cluster-0", "svc-" + i, "ns-0")); } long sOps = updateFederatedService_slow(oldIDs, newIDs); long fOps = updateFederatedService_fast(oldIDs, newIDs); bench("N=1000 IDs", sOps, fOps); assert sOps > fOps * 50 : "Expected slow >> fast, got slow=" + sOps + " fast=" + fOps; } // --- N=3000 IDs (multi-cluster stress) --- { int N = 3000; List oldIDs = makeIDs(N); List newIDs = new ArrayList<>(makeIDs(N)); newIDs.subList(0, 300).clear(); for (int i = N; i < N + 300; i++) { newIDs.add(new RemoteDiscoveryID("cluster-0", "svc-" + i, "ns-0")); } long sOps = updateFederatedService_slow(oldIDs, newIDs); long fOps = updateFederatedService_fast(oldIDs, newIDs); bench("N=3000 IDs (multi-cluster stress)", sOps, fOps); assert sOps > fOps * 100 : "Expected slow >> fast, got slow=" + sOps + " fast=" + fOps; } System.out.println(); System.out.println("All assertions passed."); } }