wave13: 514/239 — flink/nifi/artemis + K8s/cilium/linkerd2 + ES/OpenSearch/Solr + hadoop/hbase/spark
This commit is contained in:
parent
424a2a7787
commit
8f0bc73afa
32 changed files with 3977 additions and 5 deletions
163
defects/solr/unit/SplitShardSubSlicesContains.java
Normal file
163
defects/solr/unit/SplitShardSubSlicesContains.java
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* CWE-407 unit test: solr-003
|
||||
* SplitShardCmd.cleanupAfterFailedSplit — subSlices.contains(s.getName()) (List)
|
||||
* inside a loop over all collection slices — O(S × n).
|
||||
*
|
||||
* Slow path: List<String>.contains() — O(n) per slice.
|
||||
* Fast path: HashSet<String>.contains() — O(1) per slice.
|
||||
*
|
||||
* Compile: javac -d . SplitShardSubSlicesContains.java
|
||||
* Run: java -ea unit.SplitShardSubSlicesContains
|
||||
*/
|
||||
public class SplitShardSubSlicesContains {
|
||||
|
||||
static final class Slice {
|
||||
final String name;
|
||||
final String state;
|
||||
Slice(String name, String state) {
|
||||
this.name = name;
|
||||
this.state = state;
|
||||
}
|
||||
String getName() { return name; }
|
||||
}
|
||||
|
||||
// ---- Slow: List.contains O(n) per slice ----
|
||||
|
||||
static Map<String, String> slowCleanupStateUpdate(List<Slice> allSlices, List<String> subSlices) {
|
||||
Map<String, String> propMap = new HashMap<>();
|
||||
for (Slice s : allSlices) {
|
||||
if (!subSlices.contains(s.getName())) { // O(n) per iteration
|
||||
continue;
|
||||
}
|
||||
propMap.put(s.getName(), "CONSTRUCTION");
|
||||
}
|
||||
return propMap;
|
||||
}
|
||||
|
||||
static long countSlowOps(List<Slice> allSlices, List<String> subSlices) {
|
||||
long ops = 0;
|
||||
for (Slice s : allSlices) {
|
||||
// each contains() scan is O(n) — count all comparisons
|
||||
for (String sub : subSlices) {
|
||||
ops++;
|
||||
if (sub.equals(s.getName())) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
// ---- Fast: HashSet.contains O(1) per slice ----
|
||||
|
||||
static Map<String, String> fastCleanupStateUpdate(List<Slice> allSlices, List<String> subSlices) {
|
||||
Set<String> subSliceSet = new HashSet<>(subSlices);
|
||||
Map<String, String> propMap = new HashMap<>();
|
||||
for (Slice s : allSlices) {
|
||||
if (!subSliceSet.contains(s.getName())) { // O(1)
|
||||
continue;
|
||||
}
|
||||
propMap.put(s.getName(), "CONSTRUCTION");
|
||||
}
|
||||
return propMap;
|
||||
}
|
||||
|
||||
static long countFastOps(List<Slice> allSlices, List<String> subSlices) {
|
||||
long ops = subSlices.size(); // build set: O(n)
|
||||
ops += allSlices.size(); // O(1) per lookup
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
|
||||
// ---- Test 1: correctness at small scale ----
|
||||
{
|
||||
List<Slice> allSlices = new ArrayList<>();
|
||||
List<String> subSlices = new ArrayList<>();
|
||||
// 20 total slices, 3 sub-shards from split
|
||||
for (int i = 0; i < 20; i++) allSlices.add(new Slice("shard" + i, "ACTIVE"));
|
||||
subSlices.add("shard5_0");
|
||||
subSlices.add("shard5_1");
|
||||
subSlices.add("shard5_2");
|
||||
// Add the sub-slices to allSlices too
|
||||
for (String s : subSlices) allSlices.add(new Slice(s, "INACTIVE"));
|
||||
|
||||
Map<String, String> slow = slowCleanupStateUpdate(allSlices, subSlices);
|
||||
Map<String, String> fast = fastCleanupStateUpdate(allSlices, subSlices);
|
||||
assert slow.equals(fast) : "FAIL: slow=" + slow + " fast=" + fast;
|
||||
assert slow.size() == 3 : "FAIL: expected 3 in propMap, got " + slow.size();
|
||||
System.out.println("PASS test1: correctness — propMap size=" + fast.size());
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ---- Test 2: no subslices found ----
|
||||
{
|
||||
List<Slice> allSlices = new ArrayList<>();
|
||||
List<String> subSlices = new ArrayList<>();
|
||||
for (int i = 0; i < 50; i++) allSlices.add(new Slice("shard" + i, "ACTIVE"));
|
||||
subSlices.add("nonexistent_0");
|
||||
subSlices.add("nonexistent_1");
|
||||
|
||||
Map<String, String> slow = slowCleanupStateUpdate(allSlices, subSlices);
|
||||
Map<String, String> fast = fastCleanupStateUpdate(allSlices, subSlices);
|
||||
assert slow.isEmpty() && fast.isEmpty() : "FAIL: expected empty maps";
|
||||
System.out.println("PASS test2: no subslices found");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ---- Test 3: operation count ratio >= 5x (theoretical max ~n for n subslices) ----
|
||||
{
|
||||
int S = 5000; // large collection with many shards
|
||||
int n = 8; // MAX_NUM_SUB_SHARDS
|
||||
List<Slice> allSlices = new ArrayList<>();
|
||||
List<String> subSlices = new ArrayList<>();
|
||||
for (int i = 0; i < S; i++) allSlices.add(new Slice("shard" + i, "ACTIVE"));
|
||||
for (int i = 0; i < n; i++) subSlices.add("newshard" + i);
|
||||
// none overlap — worst case for slow: each contains() exhausts all n items
|
||||
long slowOps = countSlowOps(allSlices, subSlices);
|
||||
long fastOps = countFastOps(allSlices, subSlices);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
System.out.printf("PASS test3: S=%d n=%d slow=%d fast=%d ratio=%.1fx%n",
|
||||
S, n, slowOps, fastOps, ratio);
|
||||
assert ratio >= 5.0 : "FAIL: ratio " + ratio + " < 5x";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// ---- Test 4: timing benchmark (larger subSlices list to amplify O(n) factor) ----
|
||||
{
|
||||
// Use a larger synthetic n (100 sub-shards) to show timing ratio clearly.
|
||||
// The real cap is MAX_NUM_SUB_SHARDS=8 but the algorithmic structure is identical.
|
||||
int S = 5000;
|
||||
int n = 100;
|
||||
List<Slice> allSlices = new ArrayList<>();
|
||||
List<String> subSlices = new ArrayList<>();
|
||||
for (int i = 0; i < S; i++) allSlices.add(new Slice("shard" + i, "ACTIVE"));
|
||||
for (int i = 0; i < n; i++) subSlices.add("newshard" + i);
|
||||
|
||||
int reps = 500;
|
||||
long t0 = System.nanoTime();
|
||||
for (int r = 0; r < reps; r++) slowCleanupStateUpdate(allSlices, subSlices);
|
||||
long slowNs = System.nanoTime() - t0;
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
for (int r = 0; r < reps; r++) fastCleanupStateUpdate(allSlices, subSlices);
|
||||
long fastNs = System.nanoTime() - t1;
|
||||
|
||||
double ratio = (double) slowNs / fastNs;
|
||||
System.out.printf("PASS test4: timing S=%d n=%d slow=%.2fms fast=%.2fms ratio=%.1fx%n",
|
||||
S, n, slowNs / 1e6 / reps, fastNs / 1e6 / reps, ratio);
|
||||
assert ratio >= 10.0 : "FAIL: timing ratio " + ratio + " < 10x";
|
||||
passed++;
|
||||
}
|
||||
|
||||
System.out.println(passed + "/" + passed + " PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue