java-topology/defects/dragonfly/unit/DragonflyMissingMigrationsAlgorithmTest.java

147 lines
5.2 KiB
Java

package unit;
import java.util.*;
/**
* Models Dragonfly GetMissingMigrations O(M²) std::find → O(M log M) set_difference.
*
* SLOW: O(M²) — for each haystack entry, scan entire needle with std::find.
* FAST: O(M log M) — sort both, use merge-difference.
*
* CWE-407: src/server/cluster/cluster_config.cc:394
*/
public class DragonflyMissingMigrationsAlgorithmTest {
static class MigrationInfo {
final String nodeId;
final int[] slotRanges;
MigrationInfo(String nodeId, int[] slotRanges) {
this.nodeId = nodeId;
this.slotRanges = slotRanges;
}
@Override public boolean equals(Object o) {
if (!(o instanceof MigrationInfo)) return false;
MigrationInfo m = (MigrationInfo) o;
return nodeId.equals(m.nodeId) && Arrays.equals(slotRanges, m.slotRanges);
}
@Override public int hashCode() {
return Objects.hash(nodeId, Arrays.hashCode(slotRanges));
}
}
// -------------------------------------------------------------------------
// Slow — O(M²) std::find analog
// -------------------------------------------------------------------------
static class SlowMigrationDiff {
long cmpOps = 0;
boolean find(List<MigrationInfo> needle, MigrationInfo h) {
for (MigrationInfo n : needle) {
cmpOps++;
if (h.equals(n)) return true;
}
return false;
}
List<MigrationInfo> getMissing(List<MigrationInfo> haystack,
List<MigrationInfo> needle) {
List<MigrationInfo> res = new ArrayList<>();
for (MigrationInfo h : haystack) {
if (!find(needle, h)) res.add(h);
}
return res;
}
long ops(List<MigrationInfo> haystack, List<MigrationInfo> needle) {
cmpOps = 0;
getMissing(haystack, needle);
return cmpOps;
}
}
// -------------------------------------------------------------------------
// Fast — O(M log M) sort + merge difference using HashSet by nodeId
// -------------------------------------------------------------------------
static class FastMigrationDiff {
long cmpOps = 0;
List<MigrationInfo> getMissing(List<MigrationInfo> haystack,
List<MigrationInfo> needle) {
Set<MigrationInfo> needleSet = new HashSet<>(needle);
cmpOps += needle.size(); // build cost
List<MigrationInfo> res = new ArrayList<>();
for (MigrationInfo h : haystack) {
cmpOps++;
if (!needleSet.contains(h)) res.add(h);
}
return res;
}
long ops(List<MigrationInfo> haystack, List<MigrationInfo> needle) {
cmpOps = 0;
getMissing(haystack, needle);
return cmpOps;
}
}
static List<MigrationInfo> makeMigrations(int n, int offset) {
List<MigrationInfo> list = new ArrayList<>();
for (int i = 0; i < n; i++) {
list.add(new MigrationInfo("node-" + (offset + i),
new int[]{(offset + i) * 10, (offset + i) * 10 + 9}));
}
return list;
}
public static void main(String[] args) {
SlowMigrationDiff slow = new SlowMigrationDiff();
FastMigrationDiff fast = new FastMigrationDiff();
int passed = 0, total = 0;
System.out.println("=== dragonfly-0001: GetMissingMigrations O(M²) ===");
int[] sizes = {10, 50, 100, 200};
for (int m : sizes) {
List<MigrationInfo> haystack = makeMigrations(m, 0);
List<MigrationInfo> needle = makeMigrations(m / 2, 0); // half overlap
long s = slow.ops(haystack, needle);
long f = fast.ops(haystack, needle);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 2.0;
System.out.printf("M=%3d slow=%6d fast=%4d ratio=%5.1fx %s%n",
m, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both return same missing entries
List<MigrationInfo> h = makeMigrations(50, 0);
List<MigrationInfo> n = makeMigrations(30, 0); // first 30 overlap
List<MigrationInfo> sr = slow.getMissing(h, n);
List<MigrationInfo> fr = fast.getMissing(h, n);
total++;
boolean correct = sr.size() == fr.size();
System.out.printf("correctness (missing=%d): %s%n", sr.size(), correct ? "PASS" : "FAIL");
if (correct) passed++;
// Ratio check at M=100 >= 5x
List<MigrationInfo> big = makeMigrations(100, 0);
List<MigrationInfo> ref = makeMigrations(50, 100); // fully disjoint
long sOps = slow.ops(big, ref);
long fOps = fast.ops(big, ref);
double ratio = (double) sOps / Math.max(fOps, 1);
total++;
boolean ratioOk = ratio >= 5.0;
System.out.printf("M=100 ratio=%.1fx >= 5x: %s%n", ratio, ratioOk ? "PASS" : "FAIL");
if (ratioOk) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}