Fix avg value and key size calculation when auto-adjusting caches

Current logic is broken and doesn't calculate an average

Change-Id: Ib6ab3aba74a6989c4add84150674b967fe383c65
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCaches.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCaches.java
index a07d8c4..a6ccb01 100644
--- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCaches.java
+++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCaches.java
@@ -15,6 +15,7 @@
 
 import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheFactory.getCacheDir;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.cache.Cache;
 import com.google.common.flogger.FluentLogger;
 import com.google.gerrit.common.Nullable;
@@ -185,29 +186,31 @@
       String cacheName,
       ConcurrentMap<KeyWrapper<Object>, TimedValue<Object>> store,
       ProgressMonitor progressMonitor) {
-    long kAvg = 0;
-    long vAvg = 0;
+    long kTotal = 0;
+    long vTotal = 0;
 
-    if (store.isEmpty()) return ImmutablePair.of(kAvg, vAvg);
+    if (store.isEmpty()) return ImmutablePair.of(kTotal, vTotal);
 
     progressMonitor.beginTask(
         String.format("[%s] calculate average key/value size", cacheName), store.size());
 
-    int i = 1;
     for (Map.Entry<KeyWrapper<Object>, TimedValue<Object>> entry : store.entrySet()) {
-      kAvg = kAvg + (serializedKeyLength(cacheName, entry.getKey()) - kAvg) / i;
-      vAvg = vAvg + (serializedValueLength(cacheName, entry.getValue()) - vAvg) / i;
+      kTotal += serializedKeyLength(cacheName, entry.getKey());
+      vTotal += serializedValueLength(cacheName, entry.getValue());
       progressMonitor.update(1);
     }
     progressMonitor.endTask();
-    return ImmutablePair.of(kAvg, vAvg);
+    long numCacheEntries = store.entrySet().size();
+    return ImmutablePair.of(kTotal / numCacheEntries, vTotal / numCacheEntries);
   }
 
-  private static int serializedKeyLength(String cacheName, KeyWrapper<Object> keyWrapper) {
+  @VisibleForTesting
+  static int serializedKeyLength(String cacheName, KeyWrapper<Object> keyWrapper) {
     return CacheSerializers.getKeySerializer(cacheName).serialize(keyWrapper.getValue()).length;
   }
 
-  private static int serializedValueLength(String cacheName, TimedValue<Object> timedValue) {
+  @VisibleForTesting
+  static int serializedValueLength(String cacheName, TimedValue<Object> timedValue) {
     return CacheSerializers.getValueSerializer(cacheName).serialize(timedValue.getValue()).length;
   }
 
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCachesIT.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCachesIT.java
index 5e4fe22..21e62b1 100644
--- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCachesIT.java
+++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/AutoAdjustCachesIT.java
@@ -17,6 +17,8 @@
 import static com.google.common.truth.Truth.assertThat;
 import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCaches.MAX_ENTRIES_MULTIPLIER;
 import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCaches.PERCENTAGE_SIZE_INCREASE_THRESHOLD;
+import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCaches.serializedKeyLength;
+import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCaches.serializedValueLength;
 import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCachesCommand.CONFIG_HEADER;
 import static com.googlesource.gerrit.modules.cache.chroniclemap.AutoAdjustCachesCommand.TUNED_INFIX;
 import static com.googlesource.gerrit.modules.cache.chroniclemap.ChronicleMapCacheConfig.Defaults.maxBloatFactorFor;
@@ -134,13 +136,20 @@
   @Test
   @GerritConfig(name = "cache.test_cache.maxEntries", value = "10")
   @GerritConfig(name = "cache.test_cache.maxBloatFactor", value = "1")
-  public void shouldIncreaseCacheSizeWhenIsGettingFull() throws Exception {
+  public void shouldCorrectlyIncreaseCacheSizeWhenIsGettingFull() throws Exception {
     ChronicleMapCacheImpl<String, String> chronicleMapCache =
         (ChronicleMapCacheImpl<String, String>) testCache;
 
+    long elemsAdded = 0;
+    long totalKeySize = 0;
+    long totalValueSize = 0;
     while (chronicleMapCache.percentageUsedAutoResizes() < PERCENTAGE_SIZE_INCREASE_THRESHOLD) {
-      String aString = UUID.randomUUID().toString();
-      testCache.put(aString, aString);
+      String key = UUID.randomUUID() + "someExtraValue";
+      String value = UUID.randomUUID().toString();
+      elemsAdded += 1;
+      totalKeySize += serializedKeyLength(TEST_CACHE_NAME, new KeyWrapper<>(key));
+      totalValueSize += serializedValueLength(TEST_CACHE_NAME, new TimedValue<>(value));
+      testCache.put(key, value);
     }
 
     String tuneResult = adminSshSession.exec(SSH_CMD + " " + TEST_CACHE_NAME);
@@ -150,6 +159,10 @@
     assertThat(tunedConfig.getSubsections("cache")).contains(TEST_CACHE_NAME);
     assertThat(tunedConfig.getLong("cache", TEST_CACHE_NAME, "maxEntries", 0))
         .isEqualTo(chronicleMapCache.getConfig().getMaxEntries() * MAX_ENTRIES_MULTIPLIER);
+    assertThat(tunedConfig.getLong("cache", TEST_CACHE_NAME, "avgKeySize", 0))
+        .isEqualTo(totalKeySize / elemsAdded);
+    assertThat(tunedConfig.getLong("cache", TEST_CACHE_NAME, "avgValueSize", 0))
+        .isEqualTo(totalValueSize / elemsAdded);
   }
 
   @Test