java-topology/defects/artemis/unit/BindingsRouteFromClusterAlgorithm.java

203 lines
7.4 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package unit;
import java.util.*;
/**
* artemis-0001: BindingsImpl routeFromCluster O(R×A) → O(R+A)
*
* Simulates the routing logic in:
* artemis-server/.../core/postoffice/impl/BindingsImpl.java
* Method: routeFromCluster (lines 630664)
*
* Standalone — no JUnit, no Artemis deps.
*/
public class BindingsRouteFromClusterAlgorithm {
// -----------------------------------------------------------------------
// Result types
// -----------------------------------------------------------------------
static class RouteResult {
final long ops;
final List<Long> routedWithAck;
final List<Long> routedWithoutAck;
RouteResult(long ops, List<Long> routedWithAck, List<Long> routedWithoutAck) {
this.ops = ops;
this.routedWithAck = routedWithAck;
this.routedWithoutAck = routedWithoutAck;
}
}
// -----------------------------------------------------------------------
// Slow: ArrayList.contains inside loop (production code)
// -----------------------------------------------------------------------
static RouteResult routeFromClusterSlow(long[] routeIds, long[] ackIds) {
long ops = 0;
// Build idsToAckList as ArrayList (defect)
List<Long> idsToAckList = new ArrayList<>();
for (long id : ackIds) {
idsToAckList.add(id);
}
List<Long> routedWithAck = new ArrayList<>();
List<Long> routedWithoutAck = new ArrayList<>();
for (long bindingID : routeIds) { // O(R) loop
// List.contains: O(A) scan
boolean needsAck = false;
for (Long ackId : idsToAckList) {
ops++;
if (ackId.equals(bindingID)) {
needsAck = true;
break;
}
}
if (needsAck) {
routedWithAck.add(bindingID);
} else {
routedWithoutAck.add(bindingID);
}
}
return new RouteResult(ops, routedWithAck, routedWithoutAck);
}
// -----------------------------------------------------------------------
// Fast: HashSet for O(1) lookup
// -----------------------------------------------------------------------
static RouteResult routeFromClusterFast(long[] routeIds, long[] ackIds) {
long ops = 0;
// Build idsToAckSet as HashSet (fix)
Set<Long> idsToAckSet = new HashSet<>();
for (long id : ackIds) {
ops++;
idsToAckSet.add(id);
}
List<Long> routedWithAck = new ArrayList<>();
List<Long> routedWithoutAck = new ArrayList<>();
for (long bindingID : routeIds) { // O(R) loop
ops++;
if (idsToAckSet.contains(bindingID)) { // O(1)
routedWithAck.add(bindingID);
} else {
routedWithoutAck.add(bindingID);
}
}
return new RouteResult(ops, routedWithAck, routedWithoutAck);
}
// -----------------------------------------------------------------------
// Test harness
// -----------------------------------------------------------------------
public static void main(String[] args) {
int passed = 0;
int total = 0;
// ---- Test 1: basic routing correctness ----
total++;
long[] routes = {1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L};
long[] acks = {2L, 4L, 6L, 8L, 10L};
RouteResult slow1 = routeFromClusterSlow(routes, acks);
RouteResult fast1 = routeFromClusterFast(routes, acks);
boolean match1 = new HashSet<>(slow1.routedWithAck).equals(new HashSet<>(fast1.routedWithAck)) &&
new HashSet<>(slow1.routedWithoutAck).equals(new HashSet<>(fast1.routedWithoutAck));
if (match1) {
System.out.println("PASS test1: basic routing — ack=" + slow1.routedWithAck.size() +
" noack=" + slow1.routedWithoutAck.size());
passed++;
} else {
System.out.println("FAIL test1: routing mismatch");
}
// ---- Test 2: no ACKs required ----
total++;
long[] noAcks = {};
RouteResult slow2 = routeFromClusterSlow(routes, noAcks);
RouteResult fast2 = routeFromClusterFast(routes, noAcks);
boolean match2 = slow2.routedWithAck.isEmpty() && fast2.routedWithAck.isEmpty() &&
slow2.routedWithoutAck.size() == routes.length &&
fast2.routedWithoutAck.size() == routes.length;
if (match2) {
System.out.println("PASS test2: no-ACK case — all routes without ack");
passed++;
} else {
System.out.println("FAIL test2: no-ACK case mismatch");
}
// ---- Test 3: all ACKs required ----
total++;
RouteResult slow3 = routeFromClusterSlow(routes, routes);
RouteResult fast3 = routeFromClusterFast(routes, routes);
boolean match3 = slow3.routedWithAck.size() == routes.length &&
fast3.routedWithAck.size() == routes.length &&
slow3.routedWithoutAck.isEmpty() && fast3.routedWithoutAck.isEmpty();
if (match3) {
System.out.println("PASS test3: all-ACK case — all routes with ack");
passed++;
} else {
System.out.println("FAIL test3: all-ACK case mismatch");
}
// ---- Test 4: large scale ops ratio ----
total++;
int R = 100; // route targets
int A = 50; // ACK targets
long[] bigRoutes = new long[R];
for (int i = 0; i < R; i++) bigRoutes[i] = i;
long[] bigAcks = new long[A];
for (int i = 0; i < A; i++) bigAcks[i] = i * 2; // every other route needs ACK
RouteResult slowBig = routeFromClusterSlow(bigRoutes, bigAcks);
RouteResult fastBig = routeFromClusterFast(bigRoutes, bigAcks);
boolean matchBig = new HashSet<>(slowBig.routedWithAck).equals(new HashSet<>(fastBig.routedWithAck));
if (!matchBig) {
System.out.println("FAIL test4: large scale result mismatch");
} else {
double ratio = (double) slowBig.ops / fastBig.ops;
if (ratio >= 10.0) {
System.out.printf("PASS test4: large scale slow=%d ops, fast=%d ops, ratio=%.1fx%n",
slowBig.ops, fastBig.ops, ratio);
passed++;
} else {
System.out.printf("FAIL test4: ratio=%.1fx (need >=10x) slow=%d fast=%d%n",
ratio, slowBig.ops, fastBig.ops);
}
}
// ---- Test 5: route IDs not in ACK list ----
total++;
long[] disjointAcks = {200L, 201L, 202L}; // none match routes 0..9
RouteResult slow5 = routeFromClusterSlow(routes, disjointAcks);
RouteResult fast5 = routeFromClusterFast(routes, disjointAcks);
boolean match5 = slow5.routedWithAck.isEmpty() && fast5.routedWithAck.isEmpty();
if (match5) {
System.out.println("PASS test5: disjoint ACK IDs — no routes with ack");
passed++;
} else {
System.out.println("FAIL test5: disjoint ACK IDs mismatch");
}
System.out.println("\n" + passed + "/" + total + " PASS");
if (passed != total) {
System.exit(1);
}
}
}