grafana-0001: getDashboardsSharedWithUser dashboard UID dedup O(P^2) MEDIUM 2.5x grafana-0002: folder UID dedup slices.Contains O(P^2) 4 sites MEDIUM 4x grafana-0003: deduplicateAvailableFolders ContainsFunc O(F*A) MEDIUM 9.3x loki-0001: DAG AddEdge/Eliminate slices.Contains O(E^2) LOW 2.1x
125 lines
4.5 KiB
Java
125 lines
4.5 KiB
Java
import java.util.*;
|
|
|
|
/**
|
|
* Java simulation unit test for Loki CWE-407 defect.
|
|
* Simulates the O(E^2) edge dedup pattern in Loki's DAG implementation.
|
|
*
|
|
* loki-0001: DAG AddEdge slices.Contains on children/parents O(E^2)
|
|
*
|
|
* In practice, Loki query plans have small fan-out (1-3 children per node),
|
|
* so this is LOW severity. The test uses synthetic high fan-out to
|
|
* demonstrate the algorithmic defect.
|
|
*/
|
|
public class LokiTest {
|
|
|
|
// ---------------------------------------------------------------
|
|
// loki-0001: DAG AddEdge edge dedup O(E^2) vs O(E)
|
|
// ---------------------------------------------------------------
|
|
|
|
/** BEFORE: linear scan of children list for uniqueness */
|
|
static class DagBefore {
|
|
Map<String, List<String>> children = new HashMap<>();
|
|
Map<String, List<String>> parents = new HashMap<>();
|
|
|
|
void addEdge(String parent, String child) {
|
|
children.computeIfAbsent(parent, k -> new ArrayList<>());
|
|
parents.computeIfAbsent(child, k -> new ArrayList<>());
|
|
|
|
List<String> ch = children.get(parent);
|
|
if (!ch.contains(child)) { // O(E) linear scan
|
|
ch.add(child);
|
|
}
|
|
List<String> pa = parents.get(child);
|
|
if (!pa.contains(parent)) { // O(E) linear scan
|
|
pa.add(parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
/** AFTER: parallel set for O(1) membership */
|
|
static class DagAfter {
|
|
Map<String, List<String>> children = new HashMap<>();
|
|
Map<String, List<String>> parents = new HashMap<>();
|
|
Map<String, Set<String>> childrenSet = new HashMap<>();
|
|
Map<String, Set<String>> parentSet = new HashMap<>();
|
|
|
|
void addEdge(String parent, String child) {
|
|
children.computeIfAbsent(parent, k -> new ArrayList<>());
|
|
parents.computeIfAbsent(child, k -> new ArrayList<>());
|
|
childrenSet.computeIfAbsent(parent, k -> new HashSet<>());
|
|
parentSet.computeIfAbsent(child, k -> new HashSet<>());
|
|
|
|
if (childrenSet.get(parent).add(child)) {
|
|
children.get(parent).add(child);
|
|
}
|
|
if (parentSet.get(child).add(parent)) {
|
|
parents.get(child).add(parent);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void testLoki0001() {
|
|
int E = 500; // edges from one parent to many children
|
|
String parent = "root";
|
|
|
|
// Warm up
|
|
for (int w = 0; w < 3; w++) {
|
|
DagBefore db = new DagBefore();
|
|
DagAfter da = new DagAfter();
|
|
for (int i = 0; i < E; i++) {
|
|
db.addEdge(parent, "child-" + i);
|
|
da.addEdge(parent, "child-" + i);
|
|
}
|
|
}
|
|
|
|
int ITER = 200;
|
|
|
|
long t0 = System.nanoTime();
|
|
for (int it = 0; it < ITER; it++) {
|
|
DagBefore db = new DagBefore();
|
|
for (int i = 0; i < E; i++) {
|
|
db.addEdge(parent, "child-" + i);
|
|
}
|
|
// Also add duplicates to exercise the contains path
|
|
for (int i = 0; i < E; i++) {
|
|
db.addEdge(parent, "child-" + i);
|
|
}
|
|
}
|
|
long befNs = System.nanoTime() - t0;
|
|
|
|
t0 = System.nanoTime();
|
|
for (int it = 0; it < ITER; it++) {
|
|
DagAfter da = new DagAfter();
|
|
for (int i = 0; i < E; i++) {
|
|
da.addEdge(parent, "child-" + i);
|
|
}
|
|
for (int i = 0; i < E; i++) {
|
|
da.addEdge(parent, "child-" + i);
|
|
}
|
|
}
|
|
long aftNs = System.nanoTime() - t0;
|
|
|
|
// Correctness check
|
|
DagBefore db = new DagBefore();
|
|
DagAfter da = new DagAfter();
|
|
for (int i = 0; i < E; i++) {
|
|
db.addEdge(parent, "child-" + i);
|
|
da.addEdge(parent, "child-" + i);
|
|
}
|
|
assert db.children.get(parent).size() == da.children.get(parent).size()
|
|
: "loki-0001 correctness: children sizes differ";
|
|
assert db.children.get(parent).equals(da.children.get(parent))
|
|
: "loki-0001 correctness: children order differs";
|
|
|
|
double ratio = (double) befNs / aftNs;
|
|
System.out.printf("loki-0001 dag-edge-dedup BEFORE=%,dns AFTER=%,dns ratio=%.1fx %s%n",
|
|
befNs, aftNs, ratio, ratio > 2.0 ? "PASS" : "FAIL");
|
|
assert ratio > 2.0 : "loki-0001: expected >2x speedup, got " + ratio;
|
|
}
|
|
|
|
// ---------------------------------------------------------------
|
|
public static void main(String[] args) {
|
|
testLoki0001();
|
|
System.out.println("\nAll 1 Loki CWE-407 test PASSED.");
|
|
}
|
|
}
|