package unit; import java.util.*; /** * foundationdb-0001: canLaunchSrc std::count nested loop O(S × R × S') * * Models the data structures in DDRelocationQueue.actor.cpp: * outer loop: relocation.src (S servers) * inner loop: cancellableRelocations (R entries) * innermost: std::count on servers vector (S') * * Compile: javac -d . FoundationdbTest.java * Run: java unit.FoundationdbTest */ public class FoundationdbTest { // Simulate a UID (storage server ID) static class UID { final long v; UID(long v) { this.v = v; } @Override public boolean equals(Object o) { return o instanceof UID && ((UID)o).v == v; } @Override public int hashCode() { return Long.hashCode(v); } @Override public String toString() { return "UID(" + v + ")"; } } // Simulate RelocateData.src (list of source server UIDs) static class RelocateData { final List src; final int priority; final int workFactor; RelocateData(List src, int priority, int workFactor) { this.src = src; this.priority = priority; this.workFactor = workFactor; } } // Simulate Busyness.addWork / removeWork static class Busyness { int load = 0; Busyness(int load) { this.load = load; } Busyness copy() { return new Busyness(load); } void removeWork(int priority, int workFactor) { load = Math.max(0, load - workFactor); } boolean canLaunch(int priority, int workFactor) { return load + workFactor <= 100; } } // ---- DEFECTIVE implementation (O(S × R × S')) ---- static boolean canLaunchSrc_defective( RelocateData relocation, Map busymap, List cancellableRelocations, int[] comparisonCount) { int workFactor = 10; int neededServers = Math.max(1, relocation.src.size() - 3 + 1); for (int i = 0; i < relocation.src.size(); i++) { // O(S) Busyness busyCopy = busymap.get(relocation.src.get(i)).copy(); for (int j = 0; j < cancellableRelocations.size(); j++) { // O(R) List servers = cancellableRelocations.get(j).src; // std::count equivalent — O(S') linear scan boolean found = false; for (UID uid : servers) { // O(S') comparisonCount[0]++; if (uid.equals(relocation.src.get(i))) { found = true; break; } } if (found) { busyCopy.removeWork(cancellableRelocations.get(j).priority, cancellableRelocations.get(j).workFactor); } } if (busyCopy.canLaunch(relocation.priority, workFactor)) { --neededServers; if (neededServers == 0) return true; } } return false; } // ---- FIXED implementation: pre-build map, O(R×S' + S) ---- static boolean canLaunchSrc_fixed( RelocateData relocation, Map busymap, List cancellableRelocations, int[] comparisonCount) { int workFactor = 10; int neededServers = Math.max(1, relocation.src.size() - 3 + 1); // Build inverted index: UID -> list of cancellable relocation indices O(R×S') Map> cancellableByServer = new HashMap<>(); for (int j = 0; j < cancellableRelocations.size(); j++) { for (UID uid : cancellableRelocations.get(j).src) { comparisonCount[0]++; cancellableByServer.computeIfAbsent(uid, k -> new ArrayList<>()).add(j); } } for (int i = 0; i < relocation.src.size(); i++) { // O(S) Busyness busyCopy = busymap.get(relocation.src.get(i)).copy(); List indices = cancellableByServer.get(relocation.src.get(i)); if (indices != null) { for (int j : indices) { busyCopy.removeWork(cancellableRelocations.get(j).priority, cancellableRelocations.get(j).workFactor); } } if (busyCopy.canLaunch(relocation.priority, workFactor)) { --neededServers; if (neededServers == 0) return true; } } return false; } // Build a test scenario static Map buildBusymap(List allServers) { Map map = new HashMap<>(); for (UID uid : allServers) map.put(uid, new Busyness(50)); return map; } public static void main(String[] args) { int pass = 0, fail = 0; // -- Test 1: correctness — small scenario { List allServers = new ArrayList<>(); for (int i = 0; i < 5; i++) allServers.add(new UID(i)); // The relocation being considered RelocateData candidate = new RelocateData( Arrays.asList(allServers.get(0), allServers.get(1), allServers.get(2)), 100, 10); // Some cancellable in-flight moves that involve the same servers List cancellable = new ArrayList<>(); cancellable.add(new RelocateData(Arrays.asList(allServers.get(0), allServers.get(3)), 50, 30)); cancellable.add(new RelocateData(Arrays.asList(allServers.get(1), allServers.get(4)), 50, 30)); Map busymap = buildBusymap(allServers); int[] cmpDefective = {0}; int[] cmpFixed = {0}; boolean resultDef = canLaunchSrc_defective(candidate, busymap, cancellable, cmpDefective); boolean resultFix = canLaunchSrc_fixed(candidate, busymap, cancellable, cmpFixed); if (resultDef == resultFix) { System.out.printf("PASS test1: correctness — defective=%b fixed=%b%n", resultDef, resultFix); pass++; } else { System.out.printf("FAIL test1: defective=%b fixed=%b%n", resultDef, resultFix); fail++; } } // -- Test 2: complexity — large R queue demonstrates O(S×R×S') vs O(R×S' + S) // Use servers at max load so canLaunch always fails, forcing the full loop { int S = 3; // team size (relocation.src) int R = 500; // queue depth (cancellable relocations) int S2 = 3; // src team size of each cancellable relocation // Need enough servers: 3 (candidate) + R*S2 (cancellable) = 3 + 500*3 = 1503 List servers = new ArrayList<>(); for (int i = 0; i < 3000; i++) servers.add(new UID(i)); RelocateData candidate = new RelocateData( servers.subList(0, S), 100, 10); // cancellable relocations — use disjoint servers so no work is removed // and servers stay busy, forcing all S iterations to complete List cancellable = new ArrayList<>(); for (int j = 0; j < R; j++) { int base = 100 + j * S2; // disjoint from candidate servers (0,1,2) cancellable.add(new RelocateData( servers.subList(base, base + S2), 50, 5)); } // Overload the source servers so canLaunch never returns true early Map busymapDef = new HashMap<>(); Map busymapFix = new HashMap<>(); for (UID uid : servers) { busymapDef.put(uid, new Busyness(95)); // high load — can't launch busymapFix.put(uid, new Busyness(95)); } int[] cmpDefective = {0}; int[] cmpFixed = {0}; boolean resDef = canLaunchSrc_defective(candidate, busymapDef, cancellable, cmpDefective); boolean resFix = canLaunchSrc_fixed(candidate, busymapFix, cancellable, cmpFixed); System.out.printf("test2: defective comparisons=%d fixed comparisons=%d (S=%d R=%d S2=%d)%n", cmpDefective[0], cmpFixed[0], S, R, S2); System.out.printf("test2: defective result=%b fixed result=%b%n", resDef, resFix); // Fixed must produce same result with fewer comparisons (no S multiplier) // Defective: S * R * S2 = 3 * 500 * 3 = 4500 comparisons // Fixed: R * S2 (build) = 500 * 3 = 1500 comparisons (no outer S factor) boolean sameResult = (resDef == resFix); boolean moreEfficient = cmpFixed[0] < cmpDefective[0]; if (sameResult && moreEfficient) { System.out.println("PASS test2: fixed is more efficient and produces same result"); pass++; } else { System.out.printf("FAIL test2: sameResult=%b moreEfficient=%b%n", sameResult, moreEfficient); fail++; } int expectedDefective = S * R * S2; int expectedFixed = R * S2; // build phase dominates System.out.printf(" Expected defective ~O(S×R×S')=%d, actual=%d%n", expectedDefective, cmpDefective[0]); System.out.printf(" Expected fixed ~O(R×S')=%d, actual=%d%n", expectedFixed, cmpFixed[0]); } // -- Test 3: no cancellable relocations — both return same result { List servers = new ArrayList<>(); for (int i = 0; i < 3; i++) servers.add(new UID(i)); RelocateData candidate = new RelocateData(servers, 100, 5); Map busymap = buildBusymap(servers); int[] c1 = {0}, c2 = {0}; boolean r1 = canLaunchSrc_defective(candidate, busymap, new ArrayList<>(), c1); boolean r2 = canLaunchSrc_fixed(candidate, busymap, new ArrayList<>(), c2); if (r1 == r2) { System.out.printf("PASS test3: empty cancellable — both=%b%n", r1); pass++; } else { System.out.printf("FAIL test3: mismatch on empty cancellable%n"); fail++; } } System.out.printf("%nResults: %d passed, %d failed%n", pass, fail); if (fail > 0) System.exit(1); } }