java-topology/defects/pulsar/patch/pulsar-0001-gettopicsresult-grouped-linkedhashset.md

1.5 KiB
Raw Blame History

pulsar-0001: GetTopicsResult.getTopics() — ArrayList.contains() inside dedup for loop

Defect ID

pulsar-0001

File:Line

pulsar-common/src/main/java/org/apache/pulsar/common/lookup/GetTopicsResult.java:117

Description

getTopics() deduplicates partitioned topic names:

List<String> grouped = new ArrayList<>();
for (String topic : nonPartitionedOrPartitionTopics) {   // O(N)
    String partitionedTopic = TopicName.get(topic).getPartitionedTopicName();
    if (!grouped.contains(partitionedTopic)) {           // O(N) linear scan
        grouped.add(partitionedTopic);
    }
}

grouped is an ArrayList<String>. Each .contains() is O(N). Total: O(N²).

This method is called on every consumer subscription lookup, broker topic listing, and namespace topic query. For a namespace with 1,000 topic partitions (e.g., a topic with 1000 partitions), this scans up to 1000×1000 = 1M comparisons instead of 1000 with a LinkedHashSet.

Complexity

  • Slow: O(N²) — ArrayList.contains() = O(N)
  • Fast: O(N) — LinkedHashSet.add() = O(1) with preserved insertion order

Severity

HIGH

Speedup Estimate

~N× improvement = 1000x at N=1000 partitions.

Fix

Replace ArrayList with LinkedHashSet<String> to preserve order while giving O(1) dedup, then convert to List for the return type:

LinkedHashSet<String> grouped = new LinkedHashSet<>();
for (String topic : nonPartitionedOrPartitionTopics) {
    grouped.add(TopicName.get(topic).getPartitionedTopicName());
}
topics = new ArrayList<>(grouped);