redis-0004 + dragonfly-0001: ACL channel O(C×P) + cluster migration O(M²); memcached CLEAN; count 599→601
This commit is contained in:
parent
ff7bd27654
commit
1d6a8b8ccd
8 changed files with 413 additions and 4 deletions
|
|
@ -0,0 +1,147 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue