wave10b/c: 465/212 hudi/iceberg/scylladb/yugabyte/foundationdb
This commit is contained in:
parent
f7fa333977
commit
70702dff5c
38 changed files with 2073 additions and 5 deletions
239
defects/foundationdb/unit/FoundationdbTest.java
Normal file
239
defects/foundationdb/unit/FoundationdbTest.java
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
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<UID> src;
|
||||
final int priority;
|
||||
final int workFactor;
|
||||
RelocateData(List<UID> 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<UID, Busyness> busymap,
|
||||
List<RelocateData> 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<UID> 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<UID, Busyness> busymap,
|
||||
List<RelocateData> 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<UID, List<Integer>> 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<Integer> 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<UID, Busyness> buildBusymap(List<UID> allServers) {
|
||||
Map<UID, Busyness> 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<UID> 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<RelocateData> 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<UID, Busyness> 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<UID> 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<RelocateData> 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<UID, Busyness> busymapDef = new HashMap<>();
|
||||
Map<UID, Busyness> 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<UID> servers = new ArrayList<>();
|
||||
for (int i = 0; i < 3; i++) servers.add(new UID(i));
|
||||
RelocateData candidate = new RelocateData(servers, 100, 5);
|
||||
Map<UID, Busyness> 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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue