Skip to content

Commit

Permalink
Add support for tinylfu admission jitter to the simulator
Browse files Browse the repository at this point in the history
This mirrors the library support which is used to protect the cache
policy from hash flooding attacks. It is now a configuration option
for use with the reference implementation and enabled by default.
  • Loading branch information
ben-manes committed Jan 9, 2024
1 parent 3d1a63a commit 363fdb8
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 18 deletions.
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ snakeyaml = "2.2"
sonarqube = "4.4.1.3373"
spotbugs-contrib = "7.6.4"
spotbugs-core = "4.8.3"
spotbugs-plugin = "6.0.4"
spotbugs-plugin = "6.0.6"
stream = "2.9.8"
tcache = "2.0.1"
testng = "7.9.0"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,27 @@ public String sketch() {
public boolean conservative() {
return config().getBoolean("tiny-lfu.count-min.conservative");
}
public JitterSettings jitter() {
return new JitterSettings();
}
public CountMin4Settings countMin4() {
return new CountMin4Settings();
}
public CountMin64Settings countMin64() {
return new CountMin64Settings();
}

public final class JitterSettings {
public boolean enabled() {
return config().getBoolean("tiny-lfu.jitter.enabled");
}
public int threshold() {
return config().getInt("tiny-lfu.jitter.threshold");
}
public double probability() {
return config().getDouble("tiny-lfu.jitter.probability");
}
}
public final class CountMin4Settings {
public String reset() {
return config().getString("tiny-lfu.count-min-4.reset");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package com.github.benmanes.caffeine.cache.simulator.admission;

import java.util.Random;

import com.github.benmanes.caffeine.cache.simulator.BasicSettings;
import com.github.benmanes.caffeine.cache.simulator.admission.Admittor.KeyOnlyAdmittor;
import com.github.benmanes.caffeine.cache.simulator.admission.countmin4.ClimberResetCountMin4;
Expand All @@ -36,34 +38,48 @@
public final class TinyLfu implements KeyOnlyAdmittor {
private final PolicyStats policyStats;
private final Frequency sketch;
private final Random random;

private final double probability;
private final int threshold;

public TinyLfu(Config config, PolicyStats policyStats) {
var settings = new BasicSettings(config);
this.random = new Random(settings.randomSeed());
this.sketch = makeSketch(settings);
this.policyStats = policyStats;
this.sketch = makeSketch(config);
if (settings.tinyLfu().jitter().enabled()) {
this.threshold = settings.tinyLfu().jitter().threshold();
this.probability = settings.tinyLfu().jitter().probability();
} else {
this.threshold = Integer.MAX_VALUE;
this.probability = 1.0;
}
}

private Frequency makeSketch(Config config) {
BasicSettings settings = new BasicSettings(config);
private Frequency makeSketch(BasicSettings settings) {
String type = settings.tinyLfu().sketch();
if (type.equalsIgnoreCase("count-min-4")) {
String reset = settings.tinyLfu().countMin4().reset();
if (reset.equalsIgnoreCase("periodic")) {
return new PeriodicResetCountMin4(config);
return new PeriodicResetCountMin4(settings.config());
} else if (reset.equalsIgnoreCase("incremental")) {
return new IncrementalResetCountMin4(config);
return new IncrementalResetCountMin4(settings.config());
} else if (reset.equalsIgnoreCase("climber")) {
return new ClimberResetCountMin4(config);
return new ClimberResetCountMin4(settings.config());
} else if (reset.equalsIgnoreCase("indicator")) {
return new IndicatorResetCountMin4(config);
return new IndicatorResetCountMin4(settings.config());
} else {
throw new IllegalStateException("Unknown reset type: " + reset);
}
} else if (type.equalsIgnoreCase("count-min-64")) {
return new CountMin64TinyLfu(config);
return new CountMin64TinyLfu(settings.config());
} else if (type.equalsIgnoreCase("random-table")) {
return new RandomRemovalFrequencyTable(config);
return new RandomRemovalFrequencyTable(settings.config());
} else if (type.equalsIgnoreCase("tiny-table")) {
return new TinyCacheAdapter(config);
return new TinyCacheAdapter(settings.config());
} else if (type.equalsIgnoreCase("perfect-table")) {
return new PerfectFrequency(config);
return new PerfectFrequency(settings.config());
}
throw new IllegalStateException("Unknown sketch type: " + type);
}
Expand All @@ -81,9 +97,10 @@ public void record(long key) {
public boolean admit(long candidateKey, long victimKey) {
sketch.reportMiss();

long candidateFreq = sketch.frequency(candidateKey);
long victimFreq = sketch.frequency(victimKey);
if (candidateFreq > victimFreq) {
int victimFreq = sketch.frequency(victimKey);
int candidateFreq = sketch.frequency(candidateKey);
if ((candidateFreq > victimFreq)
|| ((candidateFreq >= threshold) && (random.nextFloat() < probability))) {
policyStats.recordAdmission();
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,14 @@ public void increment(long e) {
}

private void reset() {
for (Long2IntMap.Entry entry : counts.long2IntEntrySet()) {
entry.setValue(entry.getIntValue() / 2);
for (var iterator = counts.long2IntEntrySet().iterator(); iterator.hasNext();) {
var entry = iterator.next();
int newValue = entry.getIntValue() / 2;
if (newValue == 0) {
iterator.remove();
} else {
entry.setValue(newValue);
}
}
size /= 2;
}
Expand Down
11 changes: 10 additions & 1 deletion simulator/src/main/resources/reference.conf
Original file line number Diff line number Diff line change
Expand Up @@ -196,15 +196,24 @@ caffeine.simulator {
# If increments are conservative by only updating the minimum counters for CountMin sketches
count-min.conservative = false

jitter {
# When enabled an otherwise rejected candidate has a random chance of being admitted
enabled = true
# The threshold frequency of a warm candidate to give it a random admission
threshold = 6
# The admission probability
probability = 0.0078125
}

count-min-64 {
eps = 0.0001
confidence = 0.99
}

count-min-4 {
# periodic: Resets by periodically halving all counters
# adaptive: Resets periodically at an adaptive interval
# incremental: Resets by halving counters in an incremental sweep
# climber or indicator: Resets periodically at an adaptive interval
reset = periodic
# The multiple of the maximum size determining the number of counters
counters-multiplier = 1.0
Expand Down

0 comments on commit 363fdb8

Please sign in to comment.