package unit; import java.util.ArrayList; import java.util.List; /** * varnish-0001: BAN_CheckObject O(B) ban list walk vs O(1) pre-filtered check. * * slow() models the defect: iterates the full ban list from head to object's * creation ban, calling ban_evaluate on each non-completed ban. * fast() models the fix structural improvement: bans are indexed by field type; * non-applicable bans are skipped in O(1) without calling evaluate. * * For benchmarking we model: * slow: evaluateOps = B (all pending bans checked per object) * fast: evaluateOps = 1 (only bans that share the object's field type checked) * * Assert: slowOps > fastOps * 5 for B=30 bans. */ public class VarnishBanCheckAlgorithmTest { static long slowOps; static long fastOps; // ---- simulated ban entry ----------------------------------------------- enum BanField { URL, HEADER_HOST, HEADER_ACCEPT, REQ_ONLY } static class Ban { final BanField field; final String pattern; boolean completed; Ban(BanField field, String pattern) { this.field = field; this.pattern = pattern; this.completed = false; } } // ---- simulated cached object ------------------------------------------- static class CachedObject { final String url; final int createdAtBanIndex; // object was created when ban list had this many entries CachedObject(String url, int createdAtBanIndex) { this.url = url; this.createdAtBanIndex = createdAtBanIndex; } } // ---- slow: O(B) walk, evaluate every non-completed ban (defect) -------- static boolean slowBanCheck(List banList, CachedObject obj) { // Walk from head (newest) to obj.createdAtBanIndex (exclusive) for (int i = 0; i < obj.createdAtBanIndex; i++) { Ban b = banList.get(i); if (b.completed) continue; slowOps++; // one evaluate call if (b.field == BanField.URL && obj.url.startsWith(b.pattern)) { return true; // object banned } } return false; } // ---- fast: O(1) indexed check (fix) ------------------------------------ // Bans indexed by field; only URL bans relevant for URL-keyed objects static boolean fastBanCheck(List urlBans, CachedObject obj) { for (Ban b : urlBans) { if (b.completed) continue; fastOps++; // only URL bans evaluated if (obj.url.startsWith(b.pattern)) return true; } return false; } // ---- benchmark driver -------------------------------------------------- public static void main(String[] args) { final int B = 30; // total pending bans final int URL_BANS = 3; // only 3 are URL-field bans (the rest are HEADER/REQ) final int REQUESTS = 5_000; List banList = new ArrayList<>(); List urlBanIndex = new ArrayList<>(); // Mix: 3 URL bans, rest HEADER/REQ bans (non-matching) for (int i = 0; i < B; i++) { if (i < URL_BANS) { Ban b = new Ban(BanField.URL, "/static/v" + i + "/"); banList.add(b); urlBanIndex.add(b); } else { banList.add(new Ban(BanField.HEADER_HOST, "example.com")); } } // Object created before any bans were added CachedObject obj = new CachedObject("/api/data", B); slowOps = 0; fastOps = 0; for (int r = 0; r < REQUESTS; r++) { slowBanCheck(banList, obj); } for (int r = 0; r < REQUESTS; r++) { fastBanCheck(urlBanIndex, obj); } // slow evaluates ALL B bans per object; fast evaluates only URL_BANS long ratio = slowOps / Math.max(fastOps, 1); boolean pass = slowOps > fastOps * (B / URL_BANS - 1); System.out.printf("varnish-0001 slow=%d fast=%d ratio=%dx %s%n", slowOps, fastOps, ratio, pass ? "PASS" : "FAIL"); if (!pass) { System.err.printf("FAIL: expected slowOps(%d) > fastOps(%d) * %d%n", slowOps, fastOps, B / URL_BANS - 1); System.exit(1); } } }