109 lines
4.3 KiB
Java
109 lines
4.3 KiB
Java
import java.util.*;
|
||
|
||
/**
|
||
* CWE-407 unit tests for RabbitMQ rabbit_fifo.erl defects.
|
||
*
|
||
* rabbitmq-0001: deps/rabbit/src/rabbit_fifo.erl maybe_queue_consumer()
|
||
* priority_queue:member(Key, ServiceQueue) — O(C) linear scan per message dispatch.
|
||
* Called from update_or_remove_con after checkout_one pops the consumer;
|
||
* the consumer is NEVER in SQ1 at that point — the scan is 100% wasted work.
|
||
* Fix: maintain a shadow map #{consumer_key => true} for O(1) maps:is_key().
|
||
*
|
||
* rabbitmq-0002: deps/rabbit/src/rabbit_queue_consumers.erl is_blocked()
|
||
* priority_queue:member(Consumer, BlockedConsumers) — O(B) per delivery attempt
|
||
* in single-active-consumer mode.
|
||
* Fix: shadow set #{consumer => true} in #cr{} for O(1) maps:is_key().
|
||
*/
|
||
public class RabbitMQTest {
|
||
|
||
// --- rabbitmq-0001 ---
|
||
// Benchmark the core operation: O(C) scan vs O(1) map lookup for member check.
|
||
// In the post-checkout path the consumer is NEVER in the service queue —
|
||
// the scan always does a full traversal and returns false (100% wasted).
|
||
|
||
static boolean pqMember_scan(List<Integer> pq, int key) {
|
||
for (int k : pq) { // O(C) — defect: full scan, always false post-checkout
|
||
if (k == key) return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static boolean pqMember_map(Set<Integer> sqSet, int key) {
|
||
return sqSet.contains(key); // O(1) — fix
|
||
}
|
||
|
||
static void testRabbitMQ0001() throws Exception {
|
||
int C = 500; // consumers in service queue
|
||
List<Integer> sq = new ArrayList<>(C);
|
||
Set<Integer> sqSet = new HashSet<>(C);
|
||
for (int i = 1; i <= C; i++) { sq.add(i); sqSet.add(i); }
|
||
|
||
// The post-checkout consumer key (0) is NOT in the queue — always false
|
||
int popped = 0;
|
||
|
||
assert !pqMember_scan(sq, popped) : "popped consumer must not be in SQ";
|
||
assert !pqMember_map(sqSet, popped) : "popped consumer must not be in SQ";
|
||
|
||
// performance: simulate MSG dispatch cycles — member check fires once per message
|
||
int MSG = 500_000;
|
||
long t0 = System.nanoTime();
|
||
for (int m = 0; m < MSG; m++) pqMember_scan(sq, popped);
|
||
long tScan = System.nanoTime() - t0;
|
||
|
||
t0 = System.nanoTime();
|
||
for (int m = 0; m < MSG; m++) pqMember_map(sqSet, popped);
|
||
long tMap = System.nanoTime() - t0;
|
||
|
||
double ratio = (double) tScan / tMap;
|
||
System.out.printf("rabbitmq-0001: scan=%.3fs map=%.3fs ratio=%.1f×%n",
|
||
tScan / 1e9, tMap / 1e9, ratio);
|
||
assert ratio > 10 : "Expected >10× speedup, got " + ratio;
|
||
System.out.println("PASS rabbitmq-0001");
|
||
}
|
||
|
||
// --- rabbitmq-0002 ---
|
||
|
||
static boolean isBlocked_pqMember(List<Integer> blocked, int consumer) {
|
||
return blocked.contains(consumer); // O(B) — defect
|
||
}
|
||
|
||
static boolean isBlocked_mapKey(Set<Integer> blockedSet, int consumer) {
|
||
return blockedSet.contains(consumer); // O(1) — fix
|
||
}
|
||
|
||
static void testRabbitMQ0002() throws Exception {
|
||
int B = 300; // blocked consumers (high-prefetch setup)
|
||
int MSG = 100_000;
|
||
|
||
List<Integer> blocked = new ArrayList<>(B);
|
||
Set<Integer> blockedSet = new HashSet<>(B);
|
||
for (int i = 0; i < B; i++) { blocked.add(i); blockedSet.add(i); }
|
||
|
||
// target near end (worst case)
|
||
int target = B - 1;
|
||
|
||
// correctness
|
||
assert isBlocked_pqMember(blocked, target) == isBlocked_mapKey(blockedSet, target);
|
||
assert !isBlocked_pqMember(blocked, B + 99) && !isBlocked_mapKey(blockedSet, B + 99);
|
||
|
||
long t0 = System.nanoTime();
|
||
for (int r = 0; r < MSG; r++) isBlocked_pqMember(blocked, target);
|
||
long tMember = System.nanoTime() - t0;
|
||
|
||
t0 = System.nanoTime();
|
||
for (int r = 0; r < MSG; r++) isBlocked_mapKey(blockedSet, target);
|
||
long tMap = System.nanoTime() - t0;
|
||
|
||
double ratio = (double) tMember / tMap;
|
||
System.out.printf("rabbitmq-0002: member=%.3fs map=%.3fs ratio=%.1f×%n",
|
||
tMember / 1e9, tMap / 1e9, ratio);
|
||
assert ratio > 5 : "Expected >5× speedup, got " + ratio;
|
||
System.out.println("PASS rabbitmq-0002");
|
||
}
|
||
|
||
public static void main(String[] args) throws Exception {
|
||
testRabbitMQ0001();
|
||
testRabbitMQ0002();
|
||
System.out.println("ALL PASS");
|
||
}
|
||
}
|