wave6a/6b: elasticsearch/opensearch/solr/llvm-0004-5/linux-0005-6/gcc-0002/tokio/actix-web + love2d/raylib new defects
This commit is contained in:
parent
a4b0cf4edd
commit
3eebda37e1
38 changed files with 3651 additions and 0 deletions
|
|
@ -0,0 +1,73 @@
|
|||
# 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
|
||||
|
||||
```java
|
||||
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:
|
||||
|
||||
```java
|
||||
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+).
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
# solr-002: ActiveReplicaWatcher O(n²) replicaIds/solrCoreNames.contains in state-change loop
|
||||
|
||||
## Classification
|
||||
- **CWE**: CWE-407 (Inefficient Algorithmic Complexity)
|
||||
- **Severity**: MEDIUM
|
||||
- **Path**: ZooKeeper state-change callback — fires on every cluster state update
|
||||
|
||||
## Location
|
||||
`solr/core/src/java/org/apache/solr/cloud/ActiveReplicaWatcher.java:161,169`
|
||||
|
||||
## Defect
|
||||
|
||||
```java
|
||||
private final List<String> replicaIds = new ArrayList<>();
|
||||
private final List<String> solrCoreNames = new ArrayList<>();
|
||||
|
||||
// Called on every ZK state change:
|
||||
for (Slice slice : collectionState.getSlices()) { // O(shards)
|
||||
for (Replica replica : slice.getReplicas()) { // O(replicas/shard)
|
||||
if (replicaIds.contains(replica.getName())) { // O(replicaIds) = O(n)
|
||||
...
|
||||
} else if (solrCoreNames.contains( // O(solrCoreNames) = O(n)
|
||||
replica.getStr(ZkStateReader.CORE_NAME_PROP))) {
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Both `replicaIds` and `solrCoreNames` are `ArrayList`. The watcher fires on every
|
||||
ZooKeeper cluster-state change event (node joins, replica state transitions, shard
|
||||
splits, etc.). For a collection with S shards × R replicas and N watched IDs:
|
||||
|
||||
Total comparisons per event: **O(S × R × N)** for each list independently.
|
||||
|
||||
## Fix
|
||||
|
||||
Convert to `HashSet` at construction time (the lists are populated once and then only
|
||||
shrink via `remove()`):
|
||||
|
||||
```java
|
||||
private final Set<String> replicaIds = new HashSet<>();
|
||||
private final Set<String> solrCoreNames = new HashSet<>();
|
||||
```
|
||||
|
||||
`HashSet.remove()` is also O(1), so the existing removal calls in the loop body
|
||||
(`replicaIds.remove(replica.getName())`) remain correct and become faster.
|
||||
|
||||
The public getters at lines 93/98 return the field directly as `List`; the return type
|
||||
would need to change to `Collection` or the getter can wrap with `new ArrayList<>(replicaIds)`.
|
||||
|
||||
## Complexity
|
||||
| Metric | Before | After |
|
||||
|--------|--------|-------|
|
||||
| contains() | O(N) | O(1) |
|
||||
| Per ZK event | O(S×R×N) | O(S×R) |
|
||||
| remove() | O(N) | O(1) |
|
||||
|
||||
## Affected Versions
|
||||
Present in all Solr versions with `ActiveReplicaWatcher` (Solr 7+).
|
||||
Loading…
Add table
Add a link
Reference in a new issue