redis-0004 + dragonfly-0001: ACL channel O(C×P) + cluster migration O(M²); memcached CLEAN; count 599→601

This commit is contained in:
russell@unturf.com 2026-03-27 22:34:04 -04:00
parent ff7bd27654
commit 1d6a8b8ccd
8 changed files with 413 additions and 4 deletions

View file

@ -0,0 +1,119 @@
package unit;
import java.util.*;
/**
* Models Redis ACLCheckChannelAgainstList per-command channel pattern check.
*
* SLOW: O(C×P) for each channel arg, scan all P patterns linearly via linked list.
* FAST: O(C+P) pre-build HashSet<exact> + List<glob> from selector; O(1) exact lookup.
*
* CWE-407: src/acl.c:1652,1694 (ACLCheckChannelAgainstList, ACLSelectorCheckCmd)
*/
public class RedisAclChannelCheckAlgorithmTest {
// Simplified match: exact match only (no glob) for benchmark purposes
// In production, glob patterns need fnmatch we model exact-pattern dominance
static class SlowAclSelector {
final List<String> patterns; // linked-list analog
long cmpOps = 0;
SlowAclSelector(List<String> patterns) { this.patterns = patterns; }
boolean channelAllowed(String channel) {
for (String pat : patterns) {
cmpOps++;
if (pat.equals(channel)) return true; // exact match (simplification)
}
return false;
}
long checkCommand(List<String> channels) {
cmpOps = 0;
for (String ch : channels) channelAllowed(ch);
return cmpOps;
}
}
static class FastAclSelector {
final Set<String> exactPatterns; // hash set for O(1) exact match
long cmpOps = 0;
FastAclSelector(List<String> patterns) {
exactPatterns = new HashSet<>(patterns);
cmpOps = patterns.size(); // build cost
}
boolean channelAllowed(String channel) {
cmpOps++;
return exactPatterns.contains(channel);
}
long checkCommand(List<String> channels) {
cmpOps = 0;
for (String ch : channels) channelAllowed(ch);
return cmpOps;
}
}
static List<String> makePatterns(int n) {
List<String> p = new ArrayList<>();
for (int i = 0; i < n; i++) p.add("channel:" + i);
return p;
}
static List<String> makeChannels(int c, int offset) {
List<String> ch = new ArrayList<>();
for (int i = 0; i < c; i++) ch.add("channel:" + (offset + i));
return ch;
}
public static void main(String[] args) {
int passed = 0, total = 0;
System.out.println("=== redis-0004: ACLCheckChannelAgainstList O(C×P) ===");
// Test various (P, C) combinations
int[][] cases = {{100, 10}, {500, 10}, {1000, 10}, {500, 50}};
for (int[] cs : cases) {
int p = cs[0], c = cs[1];
List<String> patterns = makePatterns(p);
// Channels not in patterns (worst case full scan each time)
List<String> channels = makeChannels(c, p + 1000);
SlowAclSelector slow = new SlowAclSelector(patterns);
FastAclSelector fast = new FastAclSelector(patterns);
long s = slow.checkCommand(channels);
long f = fast.checkCommand(channels);
double ratio = (double) s / Math.max(f, 1);
total++;
boolean ok = s > f && ratio >= 5.0;
System.out.printf("P=%4d C=%3d slow=%8d fast=%5d ratio=%7.1fx %s%n",
p, c, s, f, ratio, ok ? "PASS" : "FAIL");
if (ok) passed++;
}
// Correctness: both allow/deny same channels
List<String> pats = makePatterns(100);
SlowAclSelector slow = new SlowAclSelector(pats);
FastAclSelector fast = new FastAclSelector(pats);
// Allowed channel
total++;
boolean correct1 = slow.channelAllowed("channel:42") == fast.channelAllowed("channel:42");
System.out.printf("allow correctness (channel:42): %s%n", correct1 ? "PASS" : "FAIL");
if (correct1) passed++;
// Denied channel
total++;
boolean correct2 = slow.channelAllowed("channel:9999") == fast.channelAllowed("channel:9999");
System.out.printf("deny correctness (channel:9999): %s%n", correct2 ? "PASS" : "FAIL");
if (correct2) passed++;
System.out.printf("%n%d/%d PASS%n", passed, total);
if (passed < total) System.exit(1);
}
}