Allow to configure maxBloatFactor
Allow maxBloatFactor to be configured by reading
cache.<name>.maxBloatFactor configuration from gerrit.config
Tuning this value allow to specify how much the actual size of the cache
can differ from the configured number of entries.
Bug: Issue 13416
Change-Id: Ib9a95ec2ef3945a5e87a250b777ab41f97a18cb1
diff --git a/config.md b/config.md
index 5bbea2b..c74c012 100644
--- a/config.md
+++ b/config.md
@@ -44,4 +44,20 @@
[Official docs](
https://www.javadoc.io/doc/net.openhft/chronicle-map/3.8.0/net/openhft/chronicle/map/ChronicleMapBuilder.html#entries-long-
+)
+
+```cache.<name>.maxBloatFactor```
+: the maximum number of times this cache is allowed to grow in size beyond the
+configured target number of entries.
+
+Chronicle Map will allocate memory until the actual number of entries inserted
+divided by the number configured through ChronicleMapBuilder.entries() is not
+higher than the configured `maxBloatFactor`.
+
+Chronicle Map works progressively slower when the actual size grows far beyond
+the configured size, so the maximum possible maxBloatFactor() is artificially
+limited to 1000. Default: *1*
+
+[Official docs](
+https://www.javadoc.io/doc/net.openhft/chronicle-map/3.8.0/net/openhft/chronicle/hash/ChronicleHashBuilder.html#maxBloatFactor-double-
)
\ No newline at end of file
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfig.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfig.java
index cb345aa..782c216 100644
--- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfig.java
+++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfig.java
@@ -39,12 +39,15 @@
private final long averageValueSize;
private final Duration expireAfterWrite;
private final Duration refreshAfterWrite;
+ private final int maxBloatFactor;
public static final long DEFAULT_MAX_ENTRIES = 1000;
public static final long DEFAULT_AVG_KEY_SIZE = 128;
public static final long DEFAULT_AVG_VALUE_SIZE = 2048;
+ public static final int DEFAULT_MAX_BLOAT_FACTOR = 1;
+
public interface Factory {
ChronicleMapCacheConfig create(
@Assisted("Name") String name,
@@ -84,6 +87,9 @@
"refreshAfterWrite",
toSeconds(refreshAfterWrite),
SECONDS));
+
+ this.maxBloatFactor =
+ cfg.getInt("cache", configKey, "maxBloatFactor", DEFAULT_MAX_BLOAT_FACTOR);
}
public Duration getExpireAfterWrite() {
@@ -114,6 +120,10 @@
return diskLimit;
}
+ public int getMaxBloatFactor() {
+ return maxBloatFactor;
+ }
+
private static Path getCacheDir(SitePaths site, String name) {
if (name == null) {
return null;
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java
index c90381d..25d66e4 100644
--- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java
+++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheImpl.java
@@ -74,6 +74,8 @@
// avgValueSize)
mapBuilder.entries(config.getMaxEntries());
+ mapBuilder.maxBloatFactor(config.getMaxBloatFactor());
+
if (config.getPersistedFile() == null || config.getDiskLimit() < 0) {
store = mapBuilder.create();
} else {
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfigTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfigTest.java
index 5f003d7..5eaeabb 100644
--- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfigTest.java
+++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheConfigTest.java
@@ -16,6 +16,7 @@
import static com.google.common.truth.Truth.assertThat;
import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheConfig.DEFAULT_AVG_KEY_SIZE;
import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheConfig.DEFAULT_AVG_VALUE_SIZE;
+import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheConfig.DEFAULT_MAX_BLOAT_FACTOR;
import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheConfig.DEFAULT_MAX_ENTRIES;
import com.google.gerrit.server.config.SitePaths;
@@ -131,6 +132,21 @@
}
@Test
+ public void shouldProvideMaxDefaultBloatFactorWhenNotConfigured() {
+ assertThat(configUnderTest(gerritConfig).getMaxBloatFactor())
+ .isEqualTo(DEFAULT_MAX_BLOAT_FACTOR);
+ }
+
+ @Test
+ public void shouldProvideMaxBloatFactorWhenConfigured() throws Exception {
+ int bloatFactor = 3;
+ gerritConfig.setInt("cache", cacheKey, "maxBloatFactor", bloatFactor);
+ gerritConfig.save();
+
+ assertThat(configUnderTest(gerritConfig).getMaxBloatFactor()).isEqualTo(bloatFactor);
+ }
+
+ @Test
public void shouldProvideExpireAfterWriteWhenMaxAgeIsConfgured() throws Exception {
String maxAge = "3 minutes";
gerritConfig.setString("cache", cacheKey, "maxAge", maxAge);