import java.util.*; /** * Unit test for pinot-0002: PartialUpsertHandler + ColumnarMerger List.contains per column * * Simulates the O(C×P) defective pattern vs O(C) fixed pattern. */ public class TestPartialUpsertPrimaryKeyContains { // --- Defective: List.contains per column --- static int defectiveMerge(List allColumns, List primaryKeyColumns, List comparisonColumns) { int cost = 0; for (String column : allColumns) { cost += primaryKeyColumns.size(); // cost of List.contains boolean isPK = primaryKeyColumns.contains(column); cost += comparisonColumns.size(); // cost of List.contains boolean isCmp = comparisonColumns.contains(column); if (isPK || isCmp) continue; // process column (no-op in test) } return cost; } // --- Fixed: Set.contains per column --- static int fixedMerge(List allColumns, Set primaryKeySet, Set comparisonSet) { int cost = 0; for (String column : allColumns) { cost += 1; // O(1) HashSet.contains boolean isPK = primaryKeySet.contains(column); cost += 1; // O(1) HashSet.contains boolean isCmp = comparisonSet.contains(column); if (isPK || isCmp) continue; // process column } return cost; } static List buildColumns(int count, String prefix) { List cols = new ArrayList<>(); for (int i = 0; i < count; i++) cols.add(prefix + i); return cols; } public static void main(String[] args) { int pass = 0, fail = 0; // Test 1: basic correctness - same columns skipped { List pk = Arrays.asList("id", "ts"); List cmp = Arrays.asList("version"); List all = new ArrayList<>(pk); all.addAll(cmp); all.addAll(Arrays.asList("col1", "col2", "col3", "col4", "col5")); Set pkSet = new HashSet<>(pk); Set cmpSet = new HashSet<>(cmp); // Count how many non-key columns processed in each version int defSkipped = 0, fixSkipped = 0; for (String col : all) { if (pk.contains(col) || cmp.contains(col)) defSkipped++; if (pkSet.contains(col) || cmpSet.contains(col)) fixSkipped++; } boolean ok = (defSkipped == fixSkipped); System.out.printf("[%s] Correctness: defective skipped=%d, fixed skipped=%d%n", ok ? "PASS" : "FAIL", defSkipped, fixSkipped); if (ok) pass++; else fail++; } // Test 2: cost ratio at C=50, P=3, K=2 { int C = 50, P = 3, K = 2; List pk = buildColumns(P, "pk"); List cmp = buildColumns(K, "cmp"); List all = buildColumns(C, "col"); all.addAll(pk); all.addAll(cmp); Set pkSet = new HashSet<>(pk); Set cmpSet = new HashSet<>(cmp); int defCost = defectiveMerge(all, pk, cmp); int fixCost = fixedMerge(all, pkSet, cmpSet); double ratio = (double) defCost / fixCost; boolean ok = ratio >= (P + K) * 0.8; // ratio should approach (P+K) System.out.printf("[%s] Cost ratio C=%d P=%d K=%d: defective=%d, fixed=%d, ratio=%.1fx%n", ok ? "PASS" : "FAIL", C, P, K, defCost, fixCost, ratio); if (ok) pass++; else fail++; } // Test 3: cost ratio at C=100, P=5, K=3 { int C = 100, P = 5, K = 3; List pk = buildColumns(P, "pk"); List cmp = buildColumns(K, "cmp"); List all = buildColumns(C, "col"); all.addAll(pk); all.addAll(cmp); Set pkSet = new HashSet<>(pk); Set cmpSet = new HashSet<>(cmp); int defCost = defectiveMerge(all, pk, cmp); int fixCost = fixedMerge(all, pkSet, cmpSet); double ratio = (double) defCost / fixCost; boolean ok = ratio >= (P + K) * 0.8; System.out.printf("[%s] Cost ratio C=%d P=%d K=%d: defective=%d, fixed=%d, ratio=%.1fx%n", ok ? "PASS" : "FAIL", C, P, K, defCost, fixCost, ratio); if (ok) pass++; else fail++; } // Test 4: throughput model - at 100k rows/sec, cost reduction { int C = 100, P = 3, K = 2; long rowsPerSec = 100_000L; List pk = buildColumns(P, "pk"); List cmp = buildColumns(K, "cmp"); List all = buildColumns(C, "col"); Set pkSet = new HashSet<>(pk); Set cmpSet = new HashSet<>(cmp); int defCostPerRow = defectiveMerge(all, pk, cmp); int fixCostPerRow = fixedMerge(all, pkSet, cmpSet); long defTotal = defCostPerRow * rowsPerSec; long fixTotal = fixCostPerRow * rowsPerSec; double ratio = (double) defTotal / fixTotal; boolean ok = ratio >= 3.0; System.out.printf("[%s] Throughput C=%d P=%d K=%d: defective=%d ops/row, fixed=%d ops/row, ratio=%.1fx%n", ok ? "PASS" : "FAIL", C, P, K, defCostPerRow, fixCostPerRow, ratio); if (ok) pass++; else fail++; } // Test 5: edge case - no primary keys (degenerate) { List pk = new ArrayList<>(); List cmp = new ArrayList<>(); List all = buildColumns(10, "col"); Set pkSet = new HashSet<>(pk); Set cmpSet = new HashSet<>(cmp); int defCost = defectiveMerge(all, pk, cmp); int fixCost = fixedMerge(all, pkSet, cmpSet); // With empty lists, defective cost = 0 (P+K=0), fixed cost = 2*C (two O(1) calls each) // But logically same — just verify no crash boolean ok = true; System.out.printf("[%s] Edge empty PK/CMP: defective=%d, fixed=%d%n", ok ? "PASS" : "FAIL", defCost, fixCost); if (ok) pass++; else fail++; } System.out.printf("%nResult: %d PASS, %d FAIL%n", pass, fail); System.exit(fail > 0 ? 1 : 0); } }