java-topology/defects/sqlalchemy/unit/EvaluatedKeysAlgorithm.java

220 lines
9.3 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.

package unit;
import java.util.*;
/**
* sqlalchemy-0003: evaluated_keys list → set in _apply_evaluators()
*
* Models SQLAlchemy bulk_persistence.py _apply_evaluators():
* evaluated_keys = list(value_evaluators.keys())
* then: c.key not in evaluated_keys (inside set comprehension over prefetch_cols)
* and: .difference(evaluated_keys) (set subtraction)
*
* Fix: evaluated_keys = set(value_evaluators)
*
* Standalone Java — no JUnit required.
*/
public class EvaluatedKeysAlgorithm {
// -----------------------------------------------------------------------
// Simulated column model
// -----------------------------------------------------------------------
static class Column {
final String key;
Column(String key) { this.key = key; }
@Override public boolean equals(Object o) {
return o instanceof Column && ((Column) o).key.equals(this.key);
}
@Override public int hashCode() { return key.hashCode(); }
}
// -----------------------------------------------------------------------
// SLOW: evaluated_keys as plain List
// -----------------------------------------------------------------------
static Result slowApplyEvaluators(Map<String, Object> valueEvaluators,
List<Column> prefetchCols,
List<Column> postfetchCols) {
List<String> evaluatedKeys = new ArrayList<>(valueEvaluators.keySet()); // ← plain list
// to_prefetch: {c for c in prefetch_cols if c.key not in evaluated_keys}
Set<Column> toPrefetch = new HashSet<>();
for (Column c : prefetchCols) {
if (!evaluatedKeys.contains(c.key)) { // ← O(K) scan
toPrefetch.add(c);
}
}
// to_expire = {col.key for col in postfetch_cols}.difference(evaluated_keys)
Set<String> toExpire = new HashSet<>();
for (Column c : postfetchCols) {
toExpire.add(c.key);
}
toExpire.removeAll(evaluatedKeys); // removeAll(List) = O(P×K)
return new Result(toPrefetch, toExpire);
}
// -----------------------------------------------------------------------
// FAST: evaluated_keys as HashSet
// -----------------------------------------------------------------------
static Result fastApplyEvaluators(Map<String, Object> valueEvaluators,
List<Column> prefetchCols,
List<Column> postfetchCols) {
Set<String> evaluatedKeys = new HashSet<>(valueEvaluators.keySet()); // ← set (or just use valueEvaluators directly)
Set<Column> toPrefetch = new HashSet<>();
for (Column c : prefetchCols) {
if (!evaluatedKeys.contains(c.key)) { // ← O(1)
toPrefetch.add(c);
}
}
Set<String> toExpire = new HashSet<>();
for (Column c : postfetchCols) {
toExpire.add(c.key);
}
toExpire.removeAll(evaluatedKeys); // removeAll(Set) = O(P)
return new Result(toPrefetch, toExpire);
}
static class Result {
final Set<Column> toPrefetch;
final Set<String> toExpire;
Result(Set<Column> toPrefetch, Set<String> toExpire) {
this.toPrefetch = toPrefetch;
this.toExpire = toExpire;
}
}
// -----------------------------------------------------------------------
// Test helpers
// -----------------------------------------------------------------------
static Map<String, Object> makeEvaluators(int n) {
Map<String, Object> m = new LinkedHashMap<>();
for (int i = 0; i < n; i++) {
m.put("eval_key_" + i, new Object());
}
return m;
}
static List<Column> makeCols(int n, String prefix) {
List<Column> cols = new ArrayList<>();
for (int i = 0; i < n; i++) {
cols.add(new Column(prefix + i));
}
return cols;
}
// -----------------------------------------------------------------------
// Main
// -----------------------------------------------------------------------
public static void main(String[] args) {
int passed = 0;
int total = 0;
// ---- Correctness: basic case ----
{
Map<String, Object> evals = new LinkedHashMap<>();
evals.put("col_a", new Object());
evals.put("col_b", new Object());
List<Column> prefetch = Arrays.asList(
new Column("col_a"), // in evaluatedKeys → NOT in toPrefetch
new Column("col_c"), // not in evaluatedKeys → in toPrefetch
new Column("col_d") // not in evaluatedKeys → in toPrefetch
);
List<Column> postfetch = Arrays.asList(
new Column("col_a"), // in evaluatedKeys → NOT in toExpire
new Column("col_b"), // in evaluatedKeys → NOT in toExpire
new Column("col_e") // not in evaluatedKeys → in toExpire
);
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
total++;
assert slow.toPrefetch.size() == fast.toPrefetch.size()
: "toPrefetch size: slow=" + slow.toPrefetch.size() + " fast=" + fast.toPrefetch.size();
assert slow.toExpire.size() == fast.toExpire.size()
: "toExpire size: slow=" + slow.toExpire.size() + " fast=" + fast.toExpire.size();
assert slow.toPrefetch.size() == 2 : "expected 2 in toPrefetch, got " + slow.toPrefetch.size();
assert slow.toExpire.size() == 1 : "expected 1 in toExpire, got " + slow.toExpire.size();
System.out.println("PASS 1/4: correctness basic case");
passed++;
}
// ---- Correctness: empty evaluators ----
{
Map<String, Object> evals = new HashMap<>();
List<Column> prefetch = Arrays.asList(new Column("c1"), new Column("c2"));
List<Column> postfetch = Arrays.asList(new Column("c3"));
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
total++;
assert slow.toPrefetch.size() == fast.toPrefetch.size() : "empty evals toPrefetch mismatch";
assert slow.toExpire.size() == fast.toExpire.size() : "empty evals toExpire mismatch";
// When no evaluators, all cols go to prefetch, all to expire
assert slow.toPrefetch.size() == 2 : "expected 2 prefetch";
assert slow.toExpire.size() == 1 : "expected 1 expire";
System.out.println("PASS 2/4: correctness empty evaluators");
passed++;
}
// ---- Performance: N=1000 evaluated keys, 2000 cols ----
{
int K = 1000;
int C = 2000;
Map<String, Object> evals = makeEvaluators(K);
// half prefetch cols are in evaluated keys
List<Column> prefetch = new ArrayList<>();
for (int i = 0; i < C/2; i++) prefetch.add(new Column("eval_key_" + i));
for (int i = 0; i < C/2; i++) prefetch.add(new Column("other_" + i));
List<Column> postfetch = new ArrayList<>();
for (int i = 0; i < C/2; i++) postfetch.add(new Column("eval_key_" + i));
for (int i = 0; i < C/2; i++) postfetch.add(new Column("post_" + i));
long t0 = System.nanoTime();
for (int iter = 0; iter < 10; iter++) slowApplyEvaluators(evals, prefetch, postfetch);
long slowNs = (System.nanoTime() - t0) / 10;
long t1 = System.nanoTime();
for (int iter = 0; iter < 10; iter++) fastApplyEvaluators(evals, prefetch, postfetch);
long fastNs = (System.nanoTime() - t1) / 10;
double ratio = (double) slowNs / fastNs;
total++;
System.out.printf("PERF 3/4: sqlalchemy-0003 K=%d C=%d slow=%dms fast=%dms ratio=%.1fx%n",
K, C, slowNs/1_000_000, fastNs/1_000_000, ratio);
assert ratio >= 5.0 : "ratio too low: " + ratio;
System.out.println("PASS 3/4: sqlalchemy-0003 ratio >= 5x");
passed++;
}
// ---- Edge case: all cols in evaluated_keys ----
{
Map<String, Object> evals = makeEvaluators(50);
List<Column> prefetch = new ArrayList<>();
List<Column> postfetch = new ArrayList<>();
for (int i = 0; i < 50; i++) {
prefetch.add(new Column("eval_key_" + i));
postfetch.add(new Column("eval_key_" + i));
}
Result slow = slowApplyEvaluators(evals, prefetch, postfetch);
Result fast = fastApplyEvaluators(evals, prefetch, postfetch);
total++;
assert slow.toPrefetch.isEmpty() && fast.toPrefetch.isEmpty() : "all-in-evals toPrefetch not empty";
assert slow.toExpire.isEmpty() && fast.toExpire.isEmpty() : "all-in-evals toExpire not empty";
System.out.println("PASS 4/4: edge case all cols in evaluated_keys");
passed++;
}
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) throw new AssertionError("Some tests failed");
}
}