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

109 lines
4.3 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.

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");
}
}