wave12: 501/237 — ClickHouse/Druid/Pinot + Ansible/OpenTofu/Pulumi + Celery/Camel + VictoriaMetrics/Ceph
This commit is contained in:
parent
19333b378e
commit
424a2a7787
31 changed files with 2994 additions and 5 deletions
191
defects/druid/unit/ScanQueryAlgorithm.java
Normal file
191
defects/druid/unit/ScanQueryAlgorithm.java
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
package unit;
|
||||
|
||||
// NO JUnit. Compile: javac -d . *.java Run: java -ea unit.ScanQueryAlgorithm
|
||||
//
|
||||
// Druid druid-0001: ScanQuery columns List.contains in orderBy validation loop O(N×M) → O(N)
|
||||
//
|
||||
// Simulates: for each orderBy column, check if it exists in the selected columns list
|
||||
//
|
||||
// SLOW: List<String>.contains() per orderBy column — O(N × M)
|
||||
// FAST: HashSet<String> built once, .contains() — O(N)
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ScanQueryAlgorithm {
|
||||
|
||||
// Simulates the defective ScanQuery constructor validation:
|
||||
// for each orderBy column, call List<String>.contains()
|
||||
static class SlowImpl {
|
||||
long opCount = 0;
|
||||
|
||||
// Returns list of invalid orderBy columns (those not in selectedColumns)
|
||||
// O(N × M) where N=orderBys, M=selectedColumns
|
||||
List<String> validateOrderBy(List<String> selectedColumns, List<String> orderBys) {
|
||||
List<String> invalid = new ArrayList<>();
|
||||
for (String orderByCol : orderBys) {
|
||||
// Simulate List.contains: linear scan
|
||||
boolean found = false;
|
||||
for (int i = 0; i < selectedColumns.size(); i++) {
|
||||
opCount++;
|
||||
if (selectedColumns.get(i).equals(orderByCol)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
invalid.add(orderByCol);
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
}
|
||||
|
||||
// Simulates the fixed HashSet pattern:
|
||||
// build set once, O(1) per orderBy column check
|
||||
static class FastImpl {
|
||||
long opCount = 0;
|
||||
|
||||
// Returns list of invalid orderBy columns (those not in selectedColumns)
|
||||
// O(N) after O(M) set construction
|
||||
List<String> validateOrderBy(List<String> selectedColumns, List<String> orderBys) {
|
||||
// Build O(1) lookup set once
|
||||
Set<String> colSet = new HashSet<>(selectedColumns);
|
||||
opCount += selectedColumns.size(); // count set-build cost
|
||||
|
||||
List<String> invalid = new ArrayList<>();
|
||||
for (String orderByCol : orderBys) {
|
||||
opCount++;
|
||||
if (!colSet.contains(orderByCol)) {
|
||||
invalid.add(orderByCol);
|
||||
}
|
||||
}
|
||||
return invalid;
|
||||
}
|
||||
}
|
||||
|
||||
static List<String> buildColumns(int M) {
|
||||
List<String> cols = new ArrayList<>();
|
||||
for (int i = 0; i < M; i++) {
|
||||
cols.add("col_" + i);
|
||||
}
|
||||
return cols;
|
||||
}
|
||||
|
||||
static List<String> buildOrderBys(List<String> columns, int N) {
|
||||
// orderBy columns are at the end of the selected list (worst case for linear scan)
|
||||
List<String> orderBys = new ArrayList<>();
|
||||
for (int i = columns.size() - N; i < columns.size(); i++) {
|
||||
orderBys.add(columns.get(i));
|
||||
}
|
||||
return orderBys;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
int passed = 0;
|
||||
int total = 0;
|
||||
|
||||
// Test 1: correctness — both impls agree on valid/invalid columns
|
||||
{
|
||||
total++;
|
||||
List<String> cols = buildColumns(20);
|
||||
List<String> validOrderBys = buildOrderBys(cols, 3);
|
||||
List<String> invalidOrderBys = new ArrayList<>(validOrderBys);
|
||||
invalidOrderBys.add("nonexistent_col");
|
||||
|
||||
SlowImpl slow = new SlowImpl();
|
||||
FastImpl fast = new FastImpl();
|
||||
|
||||
List<String> slowInvalid = slow.validateOrderBy(cols, invalidOrderBys);
|
||||
List<String> fastInvalid = fast.validateOrderBy(cols, invalidOrderBys);
|
||||
|
||||
assert slowInvalid.equals(fastInvalid) :
|
||||
"Mismatch: slow=" + slowInvalid + " fast=" + fastInvalid;
|
||||
assert slowInvalid.size() == 1 :
|
||||
"Expected 1 invalid column, got " + slowInvalid.size();
|
||||
assert slowInvalid.get(0).equals("nonexistent_col") :
|
||||
"Expected nonexistent_col, got " + slowInvalid.get(0);
|
||||
|
||||
System.out.println("Test 1 PASS: correctness verified, invalid=[" + slowInvalid.get(0) + "]");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 2: op ratio at M=500 selected columns, N=5 orderBy columns
|
||||
// ScanQuery is constructed per segment scan — with M=500 wide-table analytics
|
||||
{
|
||||
total++;
|
||||
int M = 500;
|
||||
int N = 5;
|
||||
List<String> cols = buildColumns(M);
|
||||
// orderBy columns at the end — worst case for linear scan
|
||||
List<String> orderBys = buildOrderBys(cols, N);
|
||||
|
||||
SlowImpl slow = new SlowImpl();
|
||||
slow.validateOrderBy(cols, orderBys);
|
||||
long slowOps = slow.opCount;
|
||||
|
||||
FastImpl fast = new FastImpl();
|
||||
fast.validateOrderBy(cols, orderBys);
|
||||
long fastOps = fast.opCount;
|
||||
|
||||
System.out.println("Test 2: M=" + M + ", N=" + N + ", slow_ops=" + slowOps + ", fast_ops=" + fastOps);
|
||||
|
||||
// Slow: each of N orderBys must scan to end of M (they're at the end)
|
||||
// worst case: N*M = 5*500 = 2500 ops
|
||||
long minQuadratic = (long) N * (M / 2); // at least half-scan
|
||||
assert slowOps >= minQuadratic :
|
||||
"slow_ops=" + slowOps + " should be >= " + minQuadratic;
|
||||
|
||||
// Fast: M (set build) + N (lookups) = 505
|
||||
long maxLinear = M + N + 10;
|
||||
assert fastOps <= maxLinear :
|
||||
"fast_ops=" + fastOps + " should be <= " + maxLinear;
|
||||
|
||||
// Ratio: N*M / (M+N) = 5*500/505 ≈ 4.95 — clear speedup, conservative threshold 4x
|
||||
assert slowOps * 4 >= fastOps * 10 :
|
||||
"Expected slow/fast ratio >= 2.5x, got slow=" + slowOps + " fast=" + fastOps;
|
||||
|
||||
System.out.println("Test 2 PASS: slow_ops=" + slowOps + " (fast=" + fastOps + ")");
|
||||
passed++;
|
||||
}
|
||||
|
||||
// Test 3: large schema M=2000, N=10 (analytical query over very wide table)
|
||||
{
|
||||
total++;
|
||||
int M = 2000;
|
||||
int N = 10;
|
||||
List<String> cols = buildColumns(M);
|
||||
List<String> orderBys = buildOrderBys(cols, N);
|
||||
|
||||
SlowImpl slow = new SlowImpl();
|
||||
slow.validateOrderBy(cols, orderBys);
|
||||
long slowOps = slow.opCount;
|
||||
|
||||
FastImpl fast = new FastImpl();
|
||||
fast.validateOrderBy(cols, orderBys);
|
||||
long fastOps = fast.opCount;
|
||||
|
||||
System.out.println("Test 3: M=" + M + ", N=" + N + ", slow_ops=" + slowOps + ", fast_ops=" + fastOps);
|
||||
|
||||
// Slow: N*M minimum (all orderBys at end, full scan)
|
||||
long minQuadratic = (long) N * (M / 2);
|
||||
assert slowOps >= minQuadratic :
|
||||
"slow_ops=" + slowOps + " should be >= " + minQuadratic;
|
||||
|
||||
long maxLinear = M + N + 10;
|
||||
assert fastOps <= maxLinear :
|
||||
"fast_ops=" + fastOps + " should be <= " + maxLinear;
|
||||
|
||||
// At M=2000, N=10: ratio = N*M/(M+N) = 10*2000/2010 ≈ 9.95x
|
||||
assert slowOps >= fastOps * 9 :
|
||||
"Expected >= 9x op ratio, got slow=" + slowOps + " fast=" + fastOps;
|
||||
|
||||
System.out.println("Test 3 PASS: " + (slowOps / fastOps) + "x speedup at M=" + M + ", N=" + N);
|
||||
passed++;
|
||||
}
|
||||
|
||||
System.out.println(passed + "/" + total + " PASS");
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue