java-topology/defects/proxysql/test/ProxySQLConnectionPoolMetricsTest.java

96 lines
3.7 KiB
Java

import java.util.*;
/**
* CWE-407 unit test for proxysql-0001: connection pool metrics cleanup
* std::find() on vector<string> for each entry in metrics map: O(M * S).
* Fix: std::unordered_set<string> for O(1) lookup.
*
* Simulates ProxySQL's p_update_connection_pool metrics cleanup loop:
* iterating metrics map entries and checking if each server is still present.
*/
public class ProxySQLConnectionPoolMetricsTest {
// --- DEFECTIVE: vector linear scan ---
static List<String> findMissingDefective(Map<String, Object> metricsMap, List<String> curServers) {
List<String> missing = new ArrayList<>();
for (String key : metricsMap.keySet()) {
if (!curServers.contains(key)) { // O(S) per entry
missing.add(key);
}
}
return missing;
}
// --- PATCHED: unordered_set lookup ---
static List<String> findMissingPatched(Map<String, Object> metricsMap, Set<String> curServersSet) {
List<String> missing = new ArrayList<>();
for (String key : metricsMap.keySet()) {
if (!curServersSet.contains(key)) { // O(1) per entry
missing.add(key);
}
}
return missing;
}
public static void main(String[] args) {
int S = 500; // backend servers across hostgroups
// Build current server IDs (as vector/list and set)
List<String> curServersList = new ArrayList<>();
Set<String> curServersSet = new HashSet<>();
for (int i = 0; i < S; i++) {
String id = "hg" + (i % 20) + ":" + "10.0.0." + (i % 256) + ":" + (3306 + i);
curServersList.add(id);
curServersSet.add(id);
}
// Build metrics map (slightly larger than current, some stale entries)
Map<String, Object> metricsMap = new LinkedHashMap<>();
for (String id : curServersList) {
metricsMap.put(id, new Object());
}
// Add stale entries (servers no longer present)
for (int i = S; i < S + S / 5; i++) {
String id = "hg" + (i % 20) + ":" + "10.0.1." + (i % 256) + ":" + (3306 + i);
metricsMap.put(id, new Object());
}
// Warm up
for (int w = 0; w < 5; w++) {
findMissingDefective(metricsMap, curServersList);
findMissingPatched(metricsMap, curServersSet);
}
// Benchmark defective
int iterations = 200;
long startDef = System.nanoTime();
List<String> resultDef = null;
for (int i = 0; i < iterations; i++) {
resultDef = findMissingDefective(metricsMap, curServersList);
}
long defectiveNs = System.nanoTime() - startDef;
// Benchmark patched
long startPat = System.nanoTime();
List<String> resultPat = null;
for (int i = 0; i < iterations; i++) {
resultPat = findMissingPatched(metricsMap, curServersSet);
}
long patchedNs = System.nanoTime() - startPat;
double ratio = (double) defectiveNs / patchedNs;
System.out.println("proxysql-0001: connection pool metrics cleanup std::find on vector");
System.out.println("S=" + S + " servers, M=" + metricsMap.size() + " metrics entries");
System.out.println("Defective missing: " + resultDef.size() + " Patched missing: " + resultPat.size());
System.out.printf("Defective: %.3f ms%n", defectiveNs / 1e6);
System.out.printf("Patched: %.3f ms%n", patchedNs / 1e6);
System.out.printf("Ratio: %.1fx%n", ratio);
assert resultDef.size() == resultPat.size() : "Results must match!";
boolean pass = ratio > 2.0;
System.out.println(pass ? "PASS" : "FAIL");
if (!pass) System.exit(1);
}
}