Emit metric to track number of put failures

Emit cache/chroniclemap/store_put_failures_<cache> metric when failing
to put an entry into chronicle-map.

Bug: Issue 15594
Change-Id: I1af1e72e32024234f38a5ec51e47e3fd07438312
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapStore.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapStore.java
index 1f829bb..bd1d730 100644
--- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapStore.java
+++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapStore.java
@@ -15,6 +15,7 @@
 package com.googlesource.gerrit.modules.cache.chroniclemap;
 
 import com.google.common.flogger.FluentLogger;
+import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.MetricMaker;
 import java.io.File;
@@ -40,6 +41,7 @@
 
   private final ChronicleMap<KeyWrapper<K>, TimedValue<V>> store;
   private final ChronicleMapCacheConfig config;
+  private final ChronicleMapStoreMetrics metrics;
 
   ChronicleMapStore(
       ChronicleMap<KeyWrapper<K>, TimedValue<V>> store,
@@ -48,8 +50,7 @@
 
     this.store = store;
     this.config = config;
-
-    ChronicleMapStoreMetrics metrics = new ChronicleMapStoreMetrics(metricMaker);
+    this.metrics = new ChronicleMapStoreMetrics(store.name(), metricMaker);
     metrics.registerCallBackMetrics(this);
   }
 
@@ -65,6 +66,7 @@
     try {
       store.put(wrappedKey, timedVal);
     } catch (IllegalArgumentException | IllegalStateException e) {
+      metrics.incrementPutFailures();
       logger.atWarning().withCause(e).log(
           "[cache %s] Caught exception when inserting entry '%s' in chronicle-map",
           store.name(), wrappedKey.getValue());
@@ -328,16 +330,31 @@
   }
 
   private static class ChronicleMapStoreMetrics {
-
+    private final String sanitizedName;
     private final MetricMaker metricMaker;
+    private final String name;
+    private final Counter0 storePutFailures;
 
-    ChronicleMapStoreMetrics(MetricMaker metricMaker) {
+    ChronicleMapStoreMetrics(String name, MetricMaker metricMaker) {
+      this.name = name;
+      this.sanitizedName = metricMaker.sanitizeMetricName(name);
       this.metricMaker = metricMaker;
+
+      this.storePutFailures =
+          metricMaker.newCounter(
+              "cache/chroniclemap/store_put_failures_" + sanitizedName,
+              new Description(
+                      "The number of errors caught when inserting entries in chronicle-map store: "
+                          + name)
+                  .setCumulative()
+                  .setUnit("errors"));
+    }
+
+    void incrementPutFailures() {
+      storePutFailures.increment();
     }
 
     <K, V> void registerCallBackMetrics(ChronicleMapStore<K, V> store) {
-      String name = store.name();
-      String sanitizedName = metricMaker.sanitizeMetricName(name);
       String PERCENTAGE_FREE_SPACE_METRIC =
           "cache/chroniclemap/percentage_free_space_" + sanitizedName;
       String REMAINING_AUTORESIZES_METRIC =
diff --git a/src/main/resources/Documentation/metrics.md b/src/main/resources/Documentation/metrics.md
index 013a6c8..2928708 100644
--- a/src/main/resources/Documentation/metrics.md
+++ b/src/main/resources/Documentation/metrics.md
@@ -24,4 +24,7 @@
   : Constant number of hot keys for the cache that can be kept in memory.
 
 * cache/chroniclemap/hot_keys_size_<cache-name>
-  : The number of hot keys for the cache that are currently in memory.
\ No newline at end of file
+  : The number of hot keys for the cache that are currently in memory.
+
+* "cache/chroniclemap/store_put_failures_<cache-name>
+  : The number of errors caught when inserting entries in chronicle-map store
\ No newline at end of file
diff --git a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java
index a3a75b6..0041c53 100644
--- a/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java
+++ b/src/test/java/com/googlesource/gerrit/modules/cache/chroniclemap/ChronicleMapCacheTest.java
@@ -17,6 +17,7 @@
 import static com.google.common.truth.Truth.assertWithMessage;
 import static com.google.gerrit.testing.GerritJUnit.assertThrows;
 
+import com.codahale.metrics.Counter;
 import com.codahale.metrics.Gauge;
 import com.codahale.metrics.MetricRegistry;
 import com.google.gerrit.acceptance.AbstractDaemonTest;
@@ -368,12 +369,14 @@
     gerritConfig.setInt("cache", testCacheName, "avgValueSize", uuidSize / 2);
     gerritConfig.save();
 
-    ChronicleMapCacheImpl<String, String> cache = newCacheWithoutLoader();
+    ChronicleMapCacheImpl<String, String> cache = newCacheWithMetrics(testCacheName, value);
 
     cache.put(key, value);
 
     assertThat(cache.getStore().size()).isEqualTo(0);
     assertThat(cache.getIfPresent(key)).isNull();
+    assertThat(getCounter("cache/chroniclemap/store_put_failures_" + testCacheName).getCount())
+        .isEqualTo(1L);
   }
 
   @Test
@@ -395,6 +398,8 @@
 
     assertThat(cache.getStore().size()).isEqualTo(2);
     assertThat(cache.getIfPresent(key)).isNull();
+    assertThat(getCounter("cache/chroniclemap/store_put_failures_" + testCacheName).getCount())
+        .isEqualTo(1L);
   }
 
   @Test
@@ -643,4 +648,10 @@
     assertWithMessage(name).that(gauge).isNotNull();
     return gauge;
   }
+
+  private Counter getCounter(String name) {
+    Counter counter = (Counter) metricRegistry.getMetrics().get(name);
+    assertWithMessage(name).that(counter).isNotNull();
+    return counter;
+  }
 }