wave13: 514/239 — flink/nifi/artemis + K8s/cilium/linkerd2 + ES/OpenSearch/Solr + hadoop/hbase/spark

This commit is contained in:
russell@unturf.com 2026-03-27 17:50:40 -04:00
parent 424a2a7787
commit 8f0bc73afa
32 changed files with 3977 additions and 5 deletions

View 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");
}
}