java-topology/defects/rabbitmq/unit/ExportBindingAlgorithm.java

165 lines
5.2 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.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* rmq-0005: rabbit_mgmt_wm_definitions export_binding O(B×Q) → O(B+Q)
*
* Simulates the binding export scan in rabbit_mgmt_wm_definitions.erl.
* SLOW: export_binding uses List.contains({name,vhost}) for each of B bindings.
* FAST: pre-build a HashSet<String> of queue keys, then O(1) lookup per binding.
*/
public class ExportBindingAlgorithm {
// Simulated binding entry: destination name and vhost
static final class BindingEntry {
final String dest;
final String vhost;
final boolean isQueue; // dest_type == queue
BindingEntry(String dest, String vhost, boolean isQueue) {
this.dest = dest;
this.vhost = vhost;
this.isQueue = isQueue;
}
}
// Simulated queue-name tuple: {name, vhost}
static final class QueueKey {
final String name;
final String vhost;
QueueKey(String name, String vhost) {
this.name = name;
this.vhost = vhost;
}
@Override
public boolean equals(Object o) {
if (!(o instanceof QueueKey)) return false;
QueueKey q = (QueueKey) o;
return name.equals(q.name) && vhost.equals(q.vhost);
}
@Override
public int hashCode() {
return 31 * name.hashCode() + vhost.hashCode();
}
}
// SLOW: O(B×Q) — lists:member({Dest,VHost}, QNames) inside list comprehension
static long slowOps = 0;
static boolean exportBindingSlow(BindingEntry b, List<QueueKey> qnames) {
if (!b.isQueue) return true; // exchange binding, always export
for (QueueKey q : qnames) {
slowOps++;
if (q.name.equals(b.dest) && q.vhost.equals(b.vhost)) {
return true;
}
}
return false;
}
static List<BindingEntry> slowExport(List<BindingEntry> bindings, List<QueueKey> qnames) {
List<BindingEntry> result = new ArrayList<>();
for (BindingEntry b : bindings) {
if (exportBindingSlow(b, qnames)) {
result.add(b);
}
}
return result;
}
// FAST: O(B+Q) — pre-build HashSet, then O(1) per binding
static long fastOps = 0;
static Set<String> buildQNamesSet(List<QueueKey> qnames) {
Set<String> s = new HashSet<>(qnames.size() * 2);
for (QueueKey q : qnames) {
fastOps++; // one insertion per queue
s.add(q.name + "\0" + q.vhost);
}
return s;
}
static boolean exportBindingFast(BindingEntry b, Set<String> qnamesSet) {
if (!b.isQueue) return true;
fastOps++;
return qnamesSet.contains(b.dest + "\0" + b.vhost);
}
static List<BindingEntry> fastExport(List<BindingEntry> bindings, List<QueueKey> qnames) {
Set<String> qnamesSet = buildQNamesSet(qnames);
List<BindingEntry> result = new ArrayList<>();
for (BindingEntry b : bindings) {
if (exportBindingFast(b, qnamesSet)) {
result.add(b);
}
}
return result;
}
public static void main(String[] args) {
// Build test data: Q queues, B bindings (all queue-type, all matching)
int Q = 1000; // queue count
int B = 5000; // binding count
List<QueueKey> qnames = new ArrayList<>(Q);
for (int i = 0; i < Q; i++) {
qnames.add(new QueueKey("queue-" + i, "vhost-" + (i % 5)));
}
List<BindingEntry> bindings = new ArrayList<>(B);
for (int i = 0; i < B; i++) {
// Each binding points to a queue that exists — worst-case scan to find it
int idx = (i * 7 + 3) % Q; // spread across queues
bindings.add(new BindingEntry(
qnames.get(idx).name,
qnames.get(idx).vhost,
true
));
}
// SLOW run
slowOps = 0;
List<BindingEntry> slowResult = slowExport(bindings, qnames);
// FAST run
fastOps = 0;
List<BindingEntry> fastResult = fastExport(bindings, qnames);
// Correctness check
boolean pass = true;
if (slowResult.size() != fastResult.size()) {
System.out.println("FAIL: result size mismatch: slow=" + slowResult.size() + " fast=" + fastResult.size());
pass = false;
}
// Verify same bindings exported (order may differ, but all B bindings match)
if (slowResult.size() != B) {
System.out.println("FAIL: expected all " + B + " bindings exported, got slow=" + slowResult.size());
pass = false;
}
// Ratio check
double ratio = (double) slowOps / (double) fastOps;
System.out.printf("N=%d/%d B=%d Q=%d slow=%d ops fast=%d ops ratio=%.1fx%n",
B, Q, B, Q, slowOps, fastOps, ratio);
if (ratio < 5.0) {
System.out.println("FAIL: ratio too low (expected >= 5x)");
pass = false;
}
if (pass) {
System.out.println("1/1 PASS");
} else {
System.exit(1);
}
}
}