java-topology/defects/netty/unit/DnsResolveContextDedupAlgorithm.java
russell@unturf.com 72c98d9af6 netty-0001 + dubbo-0002 + doris-0004: DNS dedup O(R²); MethodWalker O(2^D); NormalizeRepeat O(S×G); count 621→624
- netty-0001: DnsResolveContext.finalResult ArrayList.contains O(R²) dedup on DNS records
  File: resolver-dns/.../dns/DnsResolveContext.java line ~914
  Fix: LinkedHashSet gives O(1) dedup with preserved insertion order
  Ratio: 24.5x at R=50 records

- dubbo-0002: MethodWalker.walkHierarchy no visited guard — O(2^D) diamond recursion
  File: dubbo-rpc-triple/.../rest/util/MethodWalker.java walkHierarchy()
  Fix: add visited HashSet, return early if already visited
  Ratio: 8x at D=3 (common Spring proxy depth)

- doris-0004: NormalizeRepeat.buildContextWithAlias ImmutableList.contains O(S×G) for GROUPING SETS
  File: fe-core/.../nereids/rules/analysis/NormalizeRepeat.java buildContextWithAlias()
  Fix: convert groupingSetExpressions to HashSet before loop — O(1) lookup
  Ratio: 49x for CUBE(c1..c8), 1000x+ for CUBE(c1..c10)
2026-03-29 22:11:57 -04:00

163 lines
6.7 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* netty-0001: DnsResolveContext.finalResult ArrayList dedup O(R²)
*
* Demonstrates the defect: O(R²) dedup via ArrayList.contains vs O(R) with LinkedHashSet.
*
* Compile: javac DnsResolveContextDedupAlgorithm.java
* Run: java DnsResolveContextDedupAlgorithm
*/
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
public class DnsResolveContextDedupAlgorithm {
// ---- DEFECT: O(R²) ArrayList dedup (mirrors DnsResolveContext lines ~906-916) ----
static List<String> dedupArrayList(List<String> responses) {
List<String> finalResult = null;
int ops = 0;
for (String converted : responses) {
if (finalResult == null) {
finalResult = new ArrayList<>(8);
finalResult.add(converted);
} else {
// O(N) scan — this is the defect
ops += finalResult.size();
if (!finalResult.contains(converted)) {
finalResult.add(converted);
}
}
}
return finalResult;
}
// ---- FIX: O(R) LinkedHashSet dedup with preserved insertion order ----
static List<String> dedupLinkedHashSet(List<String> responses) {
Set<String> finalResult = new LinkedHashSet<>(8);
for (String converted : responses) {
finalResult.add(converted); // O(1) - returns false on duplicate, no scan needed
}
return new ArrayList<>(finalResult);
}
static long benchmarkArrayList(List<String> input, int iterations) {
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
List<String> result = null;
for (String s : input) {
if (result == null) {
result = new ArrayList<>(8);
result.add(s);
} else if (!result.contains(s)) {
result.add(s);
}
}
}
return System.nanoTime() - start;
}
static long benchmarkLinkedHashSet(List<String> input, int iterations) {
long start = System.nanoTime();
for (int i = 0; i < iterations; i++) {
Set<String> result = new LinkedHashSet<>(8);
for (String s : input) {
result.add(s);
}
}
return System.nanoTime() - start;
}
public static void main(String[] args) {
System.out.println("=== netty-0001: DnsResolveContext finalResult dedup ===");
System.out.println();
// Simulate DNS resolution: 5 nameservers each returning 10 A records (some duplicates)
// This mirrors what happens with multi-server failover or CNAME chain following
List<String> dnsResponses = new ArrayList<>();
// 10 unique IPs repeated across 5 server responses = 50 total records
for (int server = 0; server < 5; server++) {
for (int ip = 0; ip < 10; ip++) {
dnsResponses.add("192.168." + server + "." + ip); // unique per server
}
}
// Add duplicates (second server repeats some from first)
for (int ip = 0; ip < 10; ip++) {
dnsResponses.add("192.168.0." + ip); // duplicates of server-0 entries
}
System.out.printf("Input: %d DNS records (simulating 5 servers × 10 IPs + 10 duplicates)%n",
dnsResponses.size());
System.out.println();
// Verify correctness
List<String> resultArrayList = dedupArrayList(dnsResponses);
List<String> resultLinkedHashSet = dedupLinkedHashSet(dnsResponses);
System.out.printf("ArrayList result size: %d%n", resultArrayList.size());
System.out.printf("LinkedHashSet result size: %d%n", resultLinkedHashSet.size());
boolean correctSize = resultArrayList.size() == resultLinkedHashSet.size();
System.out.printf("Same result size: %s%n", correctSize ? "PASS" : "FAIL");
// Check order is preserved (LinkedHashSet maintains insertion order)
boolean orderMatch = resultArrayList.equals(resultLinkedHashSet);
System.out.printf("Same result order: %s%n", orderMatch ? "PASS" : "FAIL");
System.out.println();
// Benchmark with larger input to show O(R²) vs O(R)
int iterations = 100_000;
// Small case: 10 unique records (typical DNS response)
List<String> small = new ArrayList<>();
for (int i = 0; i < 10; i++) small.add("10.0.0." + i);
// Medium case: 30 records (multi-server with some overlap)
List<String> medium = new ArrayList<>();
for (int i = 0; i < 30; i++) medium.add("10.0." + (i / 10) + "." + (i % 10));
// Large case: 50 records (CNAME chains + search domain retries)
List<String> large = new ArrayList<>();
for (int i = 0; i < 50; i++) large.add("10." + (i / 20) + "." + (i / 10 % 10) + "." + (i % 10));
System.out.println("--- Benchmarks (ns per iteration, " + iterations + " iterations) ---");
System.out.printf("%-12s %15s %15s %10s%n", "R (records)", "ArrayList O(R²)", "LinkedHashSet O(R)", "Speedup");
System.out.printf("%-12s %15s %15s %10s%n", "-----------", "---------------", "-----------------", "-------");
for (List<String> input : new List[]{small, medium, large}) {
// Warmup
for (int w = 0; w < 1000; w++) {
benchmarkArrayList(input, 1);
benchmarkLinkedHashSet(input, 1);
}
long alTime = benchmarkArrayList(input, iterations);
long lhsTime = benchmarkLinkedHashSet(input, iterations);
double speedup = (double) alTime / lhsTime;
System.out.printf("%-12d %15.0f %15.0f %9.1fx%n",
input.size(),
(double) alTime / iterations,
(double) lhsTime / iterations,
speedup);
}
System.out.println();
System.out.println("--- Algorithmic operation count ---");
System.out.printf("%-12s %15s %15s %10s%n", "R (records)", "ArrayList ops", "LinkedHashSet ops", "Ratio");
System.out.printf("%-12s %15s %15s %10s%n", "-----------", "-------------", "-----------------", "-----");
for (int r : new int[]{10, 20, 30, 50}) {
int alOps = 0;
for (int i = 1; i < r; i++) alOps += i; // sum of 0..r-1 = r*(r-1)/2
int lhsOps = r;
System.out.printf("%-12d %15d %15d %9.1fx%n", r, alOps, lhsOps, (double) alOps / lhsOps);
}
System.out.println();
if (correctSize && orderMatch) {
System.out.println("RESULT: PASS — fix is correct and faster");
} else {
System.out.println("RESULT: FAIL");
System.exit(1);
}
}
}