java-topology/defects/kafka/unit/KafkaTest.java
russell@unturf.com 4ca032752d dragonfly-0001/0002: ACL vector→flat_hash_map/set O(K×G)→O(K)
valkey-0001/0002: ACL linked-list→dict O(S×K×P)→O(S×K)
kafka-0001/0002/0003: rebalance ArrayList→HashSet O(P³/C)→O(P²/C)

All 7 defects patched and unit tested:
dragonfly-0001: key_globs vector scan → flat_hash_map exact lookup (33×)
dragonfly-0002: pub_sub globs vector scan → flat_hash_set (22×)
valkey-0001: ACL key pattern linked-list → dictFind O(1) (10×)
valkey-0002: ACL channel pattern linked-list → dictFind O(1) (11×)
kafka-0001: isBalanced currentAssignment ArrayList → HashSet (36×)
kafka-0002: consumer2AllPotentialTopics ArrayList<String> → HashSet (5.5×)
kafka-0003: RoundRobin topics() List.contains → pre-computed Set (16×)
2026-03-30 08:34:52 -04:00

203 lines
7.7 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 Apache Kafka consumer rebalance defects.
*
* kafka-0001: AbstractStickyAssignor.isBalanced() — currentAssignment values are
* ArrayList<TopicPartition>; .contains() is O(P/C) per call inside triple loop → O(P³/C).
* Fix: parallel Map<String, Set<TopicPartition>> currentAssignmentSet for O(1) contains.
*
* kafka-0002: AbstractStickyAssignor — consumer2AllPotentialTopics values are ArrayList<String>;
* .contains(topic) called O(P×C) times → O(P×C×T) per rebalance.
* Fix: values as HashSet<String> for O(1) per lookup.
*
* kafka-0003: RoundRobinAssignor.assign() — Subscription.topics().contains(topic)
* is O(T) List scan in while-loop; up to C consumers skipped per partition → O(P×C×T).
* Fix: pre-compute Map<memberId, Set<String>> subscribedTopicsSet; O(1) per lookup.
*/
public class KafkaTest {
// --- kafka-0001 ---
static boolean isBalanced_list(Map<String, List<Integer>> assignment, int totalPartitions) {
for (Map.Entry<String, List<Integer>> e : assignment.entrySet()) {
List<Integer> consumerParts = e.getValue();
for (int p = 0; p < totalPartitions; p++) {
if (!consumerParts.contains(p)) { // O(P/C) — defect
// check balance
}
}
}
return true;
}
static boolean isBalanced_set(Map<String, Set<Integer>> assignmentSet, int totalPartitions) {
for (Map.Entry<String, Set<Integer>> e : assignmentSet.entrySet()) {
Set<Integer> consumerParts = e.getValue();
for (int p = 0; p < totalPartitions; p++) {
if (!consumerParts.contains(p)) { // O(1) — fix
// check balance
}
}
}
return true;
}
static void testKafka0001() throws Exception {
int C = 5; // consumers
int P = 5000; // partitions — P/C=1000 items per list, O(1000) contains vs O(1)
Map<String, List<Integer>> assignList = new HashMap<>();
Map<String, Set<Integer>> assignSet = new HashMap<>();
for (int c = 0; c < C; c++) {
List<Integer> parts = new ArrayList<>();
Set<Integer> partsSet = new HashSet<>();
for (int p = c; p < P; p += C) { parts.add(p); partsSet.add(p); }
assignList.put("consumer-" + c, parts);
assignSet.put("consumer-" + c, partsSet);
}
// correctness
assert isBalanced_list(assignList, P) == isBalanced_set(assignSet, P);
int REPS = 20;
long t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) isBalanced_list(assignList, P);
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) isBalanced_set(assignSet, P);
long tSet = System.nanoTime() - t0;
double ratio = (double) tList / tSet;
System.out.printf("kafka-0001: list=%.3fs set=%.3fs ratio=%.1f×%n",
tList / 1e9, tSet / 1e9, ratio);
assert ratio > 5 : "Expected >5× speedup, got " + ratio;
System.out.println("PASS kafka-0001");
}
// --- kafka-0002 ---
static boolean topicSubscribed_list(Map<String, List<String>> consumer2Topics,
String consumer, String topic) {
return consumer2Topics.get(consumer).contains(topic); // O(T) — defect
}
static boolean topicSubscribed_set(Map<String, Set<String>> consumer2TopicsSet,
String consumer, String topic) {
return consumer2TopicsSet.get(consumer).contains(topic); // O(1) — fix
}
static void testKafka0002() throws Exception {
int C = 50; // consumers
int T = 50; // topics per consumer
int P = 1000; // partitions to assign
Map<String, List<String>> c2t_list = new HashMap<>();
Map<String, Set<String>> c2t_set = new HashMap<>();
for (int c = 0; c < C; c++) {
List<String> topics = new ArrayList<>(T);
Set<String> topicSet = new HashSet<>(T);
for (int t = 0; t < T; t++) { topics.add("topic-" + t); topicSet.add("topic-" + t); }
c2t_list.put("consumer-" + c, topics);
c2t_set.put("consumer-" + c, topicSet);
}
String targetConsumer = "consumer-0";
String targetTopic = "topic-" + (T - 1);
assert topicSubscribed_list(c2t_list, targetConsumer, targetTopic) ==
topicSubscribed_set(c2t_set, targetConsumer, targetTopic);
// Simulate assignment loop: O(P × C) lookups
int REPS = 200;
long t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
for (int p = 0; p < P; p++) {
for (int c = 0; c < C; c++) {
topicSubscribed_list(c2t_list, "consumer-" + c, targetTopic);
}
}
}
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
for (int p = 0; p < P; p++) {
for (int c = 0; c < C; c++) {
topicSubscribed_set(c2t_set, "consumer-" + c, targetTopic);
}
}
}
long tSet = System.nanoTime() - t0;
double ratio = (double) tList / tSet;
System.out.printf("kafka-0002: list=%.3fs set=%.3fs ratio=%.1f×%n",
tList / 1e9, tSet / 1e9, ratio);
assert ratio > 5 : "Expected >5× speedup, got " + ratio;
System.out.println("PASS kafka-0002");
}
// --- kafka-0003 ---
static boolean topicsContains_list(List<String> topics, String topic) {
return topics.contains(topic); // O(T) — defect
}
static boolean topicsContains_set(Set<String> topicSet, String topic) {
return topicSet.contains(topic); // O(1) — fix
}
static void testKafka0003() throws Exception {
int C = 50; // consumers in round-robin
int T = 50; // topics per consumer subscription
int P = 5000; // partitions to assign
List<List<String>> allLists = new ArrayList<>(C);
List<Set<String>> allSets = new ArrayList<>(C);
for (int c = 0; c < C; c++) {
List<String> lst = new ArrayList<>(T);
Set<String> set = new HashSet<>(T);
for (int t = 0; t < T; t++) { lst.add("topic-" + t); set.add("topic-" + t); }
allLists.add(lst);
allSets.add(set);
}
String lastTopic = "topic-" + (T - 1);
// correctness
assert topicsContains_list(allLists.get(0), lastTopic) ==
topicsContains_set(allSets.get(0), lastTopic);
// Simulate round-robin loop: O(P × C) checks
int REPS = 50;
long t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
for (int p = 0; p < P; p++) {
for (int c = 0; c < C; c++) topicsContains_list(allLists.get(c), lastTopic);
}
}
long tList = System.nanoTime() - t0;
t0 = System.nanoTime();
for (int r = 0; r < REPS; r++) {
for (int p = 0; p < P; p++) {
for (int c = 0; c < C; c++) topicsContains_set(allSets.get(c), lastTopic);
}
}
long tSet = System.nanoTime() - t0;
double ratio = (double) tList / tSet;
System.out.printf("kafka-0003: list=%.3fs set=%.3fs ratio=%.1f×%n",
tList / 1e9, tSet / 1e9, ratio);
assert ratio > 5 : "Expected >5× speedup, got " + ratio;
System.out.println("PASS kafka-0003");
}
public static void main(String[] args) throws Exception {
testKafka0001();
testKafka0002();
testKafka0003();
System.out.println("ALL PASS");
}
}