2.5 KiB
UNDF: UNDF-2026-000000533
solr-001: ClusterStatus O(n²) liveNodes.contains in crossCheckReplicaStateWithLiveNodes
Classification
- CWE: CWE-407 (Inefficient Algorithmic Complexity)
- Severity: HIGH
- Path: Admin API handler — called on every CLUSTERSTATUS request, which monitoring tools typically poll every few seconds
Location
solr/core/src/java/org/apache/solr/handler/admin/ClusterStatus.java:303
Defect
protected void crossCheckReplicaStateWithLiveNodes(
List<String> liveNodes, Map<String, Object> collectionProps) {
var shards = (Map<String, Object>) collectionProps.get("shards");
for (Object nextShard : shards.values()) { // O(shards)
var replicas = (Map<String, Object>) shardMap.get("replicas");
for (Object nextReplica : replicas.values()) { // O(replicas/shard)
// ...
String node_name = (String) replicaMap.get(ZkStateReader.NODE_NAME_PROP);
if (!liveNodes.contains(node_name)) { // O(liveNodes)
replicaMap.put(ZkStateReader.STATE_PROP, Replica.State.DOWN.toString());
}
}
}
}
liveNodes is fetched via zkStateReader.getZkClient().getChildren(ZkStateReader.LIVE_NODES_ZKNODE, null)
which returns a plain List<String>. The double-nested shard/replica loop calls
liveNodes.contains() for every replica.
For a cluster with:
- N live nodes
- S shards
- R replicas/shard
Total comparisons per CLUSTERSTATUS call: O(N × S × R)
A production Solr cluster with 100 nodes, 500 shards, 3 replicas/shard = 150,000 string comparisons per call. With monitoring polling at 5s intervals: 1.8 million string comparisons per minute, completely wasted.
Fix
Convert liveNodes to a HashSet<String> before the nested loops:
Set<String> liveNodeSet = new HashSet<>(liveNodes); // O(N) once
for (Object nextShard : shards.values()) {
for (Object nextReplica : replicas.values()) {
if (!liveNodeSet.contains(node_name)) { // O(1)
...
}
}
}
The caller at line 129 already has liveNodes as a List; the fix can be applied
inside crossCheckReplicaStateWithLiveNodes without changing any callers.
Complexity
| Metric | Before | After |
|---|---|---|
| contains() | O(N) | O(1) |
| Full cross-check | O(N×S×R) | O(N + S×R) |
| At N=100, S=500, R=3 | 150,000 comparisons | ~1,600 |
| Speedup | 1× | ~94× |
Affected Versions
Present in all Solr versions with the ClusterStatus handler (Solr 5+).