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
177
defects/opensearch/unit/IndexGraveyardDanglingContains.java
Normal file
177
defects/opensearch/unit/IndexGraveyardDanglingContains.java
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
package unit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* CWE-407 unit test: opensearch-005
|
||||
* IndexGraveyard.containsIndex — List<Tombstone> linear scan called per-index
|
||||
* inside DanglingIndicesState loop → O(I × T).
|
||||
*
|
||||
* Mirrors elasticsearch-004 — OpenSearch is a fork with the identical defect.
|
||||
*
|
||||
* Slow path: for (Tombstone t : tombstones) { if t.equals(index) ... } — O(T) per call.
|
||||
* Fast path: HashSet<Index>.contains() — O(1) per call.
|
||||
*
|
||||
* Compile: javac -d . IndexGraveyardDanglingContains.java
|
||||
* Run: java -ea unit.IndexGraveyardDanglingContains
|
||||
*/
|
||||
public class IndexGraveyardDanglingContains {
|
||||
|
||||
static final class Index {
|
||||
final String name;
|
||||
final String uuid;
|
||||
|
||||
Index(String name, String uuid) {
|
||||
this.name = name;
|
||||
this.uuid = uuid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (!(o instanceof Index)) return false;
|
||||
Index other = (Index) o;
|
||||
return name.equals(other.name) && uuid.equals(other.uuid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(name, uuid);
|
||||
}
|
||||
}
|
||||
|
||||
static final class Tombstone {
|
||||
final Index index;
|
||||
Tombstone(Index index) { this.index = index; }
|
||||
Index getIndex() { return index; }
|
||||
}
|
||||
|
||||
// ---- Slow: O(T) per call ----
|
||||
|
||||
static boolean slowContainsIndex(List<Tombstone> tombstones, Index index) {
|
||||
for (Tombstone tombstone : tombstones) {
|
||||
if (tombstone.getIndex().equals(index)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static List<Index> slowFindDangling(List<Index> diskIndices, List<Tombstone> tombstones) {
|
||||
List<Index> dangling = new ArrayList<>();
|
||||
for (Index index : diskIndices) {
|
||||
if (!slowContainsIndex(tombstones, index)) dangling.add(index);
|
||||
}
|
||||
return dangling;
|
||||
}
|
||||
|
||||
// ---- Fast: O(1) per call after O(T) setup ----
|
||||
|
||||
static List<Index> fastFindDangling(List<Index> diskIndices, List<Tombstone> tombstones) {
|
||||
Set<Index> graveyardSet = new HashSet<>(tombstones.size() * 2);
|
||||
for (Tombstone t : tombstones) graveyardSet.add(t.getIndex());
|
||||
List<Index> dangling = new ArrayList<>();
|
||||
for (Index index : diskIndices) {
|
||||
if (!graveyardSet.contains(index)) dangling.add(index);
|
||||
}
|
||||
return dangling;
|
||||
}
|
||||
|
||||
static long countSlowOps(List<Index> diskIndices, List<Tombstone> tombstones) {
|
||||
long ops = 0;
|
||||
for (Index index : diskIndices) {
|
||||
for (Tombstone t : tombstones) {
|
||||
ops++;
|
||||
if (t.getIndex().equals(index)) break;
|
||||
}
|
||||
}
|
||||
return ops;
|
||||
}
|
||||
|
||||
static long countFastOps(List<Index> diskIndices, List<Tombstone> tombstones) {
|
||||
long ops = 0;
|
||||
for (Tombstone t : tombstones) ops++; // build set: O(T)
|
||||
for (Index ignored : diskIndices) ops++; // lookup: O(1) each
|
||||
return ops;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
|
||||
// Test 1: correctness
|
||||
{
|
||||
List<Tombstone> tombstones = new ArrayList<>();
|
||||
List<Index> diskIndices = new ArrayList<>();
|
||||
for (int i = 0; i < 15; i++) {
|
||||
tombstones.add(new Tombstone(new Index("idx-" + i, "u" + i)));
|
||||
}
|
||||
for (int i = 0; i < 25; i++) {
|
||||
diskIndices.add(new Index("idx-" + i, "u" + i));
|
||||
}
|
||||
List<Index> slow = slowFindDangling(diskIndices, tombstones);
|
||||
List<Index> fast = fastFindDangling(diskIndices, tombstones);
|
||||
assert slow.size() == fast.size()
|
||||
: "FAIL: mismatch slow=" + slow.size() + " fast=" + fast.size();
|
||||
System.out.println("PASS test1: correctness — " + fast.size() + " dangling");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 2: all tombstoned
|
||||
{
|
||||
List<Tombstone> tombstones = new ArrayList<>();
|
||||
List<Index> diskIndices = new ArrayList<>();
|
||||
for (int i = 0; i < 30; i++) {
|
||||
tombstones.add(new Tombstone(new Index("idx-" + i, "u" + i)));
|
||||
diskIndices.add(new Index("idx-" + i, "u" + i));
|
||||
}
|
||||
assert slowFindDangling(diskIndices, tombstones).size() == 0 : "FAIL slow";
|
||||
assert fastFindDangling(diskIndices, tombstones).size() == 0 : "FAIL fast";
|
||||
System.out.println("PASS test2: all tombstoned");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 3: operation count ratio >= 10x
|
||||
{
|
||||
int I = 500, T = 500;
|
||||
List<Tombstone> tombstones = new ArrayList<>();
|
||||
List<Index> diskIndices = new ArrayList<>();
|
||||
for (int i = 0; i < T; i++) tombstones.add(new Tombstone(new Index("t" + i, "ut" + i)));
|
||||
for (int i = 0; i < I; i++) diskIndices.add(new Index("d" + i, "ud" + i));
|
||||
|
||||
long slowOps = countSlowOps(diskIndices, tombstones);
|
||||
long fastOps = countFastOps(diskIndices, tombstones);
|
||||
double ratio = (double) slowOps / fastOps;
|
||||
System.out.printf("PASS test3: I=%d T=%d slow=%d fast=%d ratio=%.1fx%n",
|
||||
I, T, slowOps, fastOps, ratio);
|
||||
assert ratio >= 10.0 : "FAIL: ratio " + ratio + " < 10x";
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 4: timing benchmark
|
||||
{
|
||||
int I = 2000, T = 500;
|
||||
List<Tombstone> tombstones = new ArrayList<>();
|
||||
List<Index> diskIndices = new ArrayList<>();
|
||||
for (int i = 0; i < T; i++) tombstones.add(new Tombstone(new Index("t" + i, "ut" + i)));
|
||||
for (int i = 0; i < I; i++) diskIndices.add(new Index("d" + i, "ud" + i));
|
||||
|
||||
int reps = 200;
|
||||
long t0 = System.nanoTime();
|
||||
for (int r = 0; r < reps; r++) slowFindDangling(diskIndices, tombstones);
|
||||
long slowNs = System.nanoTime() - t0;
|
||||
|
||||
long t1 = System.nanoTime();
|
||||
for (int r = 0; r < reps; r++) fastFindDangling(diskIndices, tombstones);
|
||||
long fastNs = System.nanoTime() - t1;
|
||||
|
||||
double ratio = (double) slowNs / fastNs;
|
||||
System.out.printf("PASS test4: timing I=%d T=%d slow=%.1fms fast=%.1fms ratio=%.1fx%n",
|
||||
I, T, 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