java-topology/docs/tickets/jitsi-videobridge-0003-conference-speech-activity-arraylist-contains.md

1.6 KiB
Raw Permalink Blame History

jitsi-videobridge-0003 — ConferenceSpeechActivity.java: ArrayList.contains() in endpointsChanged (O(n²))

Severity: HIGH File: jvb/src/main/java/org/jitsi/videobridge/ConferenceSpeechActivity.java Lines: 326, 331

Pattern

// line 326 — removeIf with lambda calling conferenceEndpoints.contains() — O(n) per element
endpointsListChanged = endpointsBySpeechActivity.removeIf(ep -> !conferenceEndpoints.contains(ep));

// line 329-335 — for loop calling endpointsBySpeechActivity.contains() — O(n) per element
for (AbstractEndpoint conferenceEndpoint : conferenceEndpoints) {
    if (!endpointsBySpeechActivity.contains(conferenceEndpoint))  // O(n)
        endpointsBySpeechActivity.add(conferenceEndpoint);
}

endpointsBySpeechActivity is ArrayList<AbstractEndpoint>. conferenceEndpoints is List<AbstractEndpoint> (also ArrayList). Both .contains() calls are O(n) — total is O(n²).

Complexity

O(|endpointsBySpeechActivity| × |conferenceEndpoints|). Called on every endpoint join/leave event. In large conferences (100+ participants) this fires frequently.

Fix

Pre-build Set<AbstractEndpoint> conferenceSet = new HashSet<>(conferenceEndpoints) before the loops. Use conferenceSet.contains(ep) at line 326 and !conferenceSet.contains(...) at line 331.

A LinkedHashSet for endpointsBySpeechActivity would make line 331 O(1) always, but the ordered-by-speech-activity requirement means the list must stay ordered by recency — use a LinkedHashSet<AbstractEndpoint> for the membership check only.

Speedup (estimated)

~80× at N=100 endpoints. O(n²) → O(n).