Add restore/persist operations related metrics

The following metrics were added:
* cache/chroniclemap/keys_index_restore_latency_<cache-name> - The
  latency of restoring an index from a file (performed once during
  the plugin start).
* cache/chroniclemap/keys_index_persist_latency_<cache-name> - The
  latency of persisting an index to a file.
* cache/chroniclemap/keys_index_restore_failures_<cache-name> - The
  number of errors caught when restore cache index from file operation
  was performed
* cache/chroniclemap/keys_index_persist_failures_<cache-name> - The
  number of errors caught when persist cache index to file operation was
  performed

Bug: Issue 15121
Change-Id: I7a953562e2b3fae15282a9130bfb4218c44ff8b3
diff --git a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheKeysIndex.java b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheKeysIndex.java
index 430a95e..9e56321 100644
--- a/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheKeysIndex.java
+++ b/src/main/java/com/googlesource/gerrit/modules/cache/chroniclemap/CacheKeysIndex.java
@@ -19,6 +19,7 @@
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.flogger.FluentLogger;
 import com.google.errorprone.annotations.CompatibleWith;
+import com.google.gerrit.metrics.Counter0;
 import com.google.gerrit.metrics.Description;
 import com.google.gerrit.metrics.Description.Units;
 import com.google.gerrit.metrics.MetricMaker;
@@ -84,6 +85,10 @@
     private final Timer0 addLatency;
     private final Timer0 removeAndConsumeOlderThanLatency;
     private final Timer0 removeAndConsumeLruKeyLatency;
+    private final Timer0 restoreLatency;
+    private final Timer0 persistLatency;
+    private final Counter0 restoreFailures;
+    private final Counter0 persistFailures;
 
     private Metrics(MetricMaker metricMaker, String name) {
       String sanitizedName = metricMaker.sanitizeMetricName(name);
@@ -127,6 +132,42 @@
                           name))
                   .setCumulative()
                   .setUnit(Units.NANOSECONDS));
+
+      restoreLatency =
+          metricMaker.newTimer(
+              "cache/chroniclemap/keys_index_restore_latency_" + sanitizedName,
+              new Description(
+                      String.format("The latency of restoring %s cache's index from file", name))
+                  .setCumulative()
+                  .setUnit(Units.MICROSECONDS));
+
+      persistLatency =
+          metricMaker.newTimer(
+              "cache/chroniclemap/keys_index_persist_latency_" + sanitizedName,
+              new Description(
+                      String.format("The latency of perststing %s cache's index to file", name))
+                  .setCumulative()
+                  .setUnit(Units.MICROSECONDS));
+
+      restoreFailures =
+          metricMaker.newCounter(
+              "cache/chroniclemap/keys_index_restore_failures_" + sanitizedName,
+              new Description(
+                      String.format(
+                          "The number of errors caught when restore %s cache index from file operation was performed: ",
+                          name))
+                  .setCumulative()
+                  .setUnit("errors"));
+
+      persistFailures =
+          metricMaker.newCounter(
+              "cache/chroniclemap/keys_index_persist_failures_" + sanitizedName,
+              new Description(
+                      String.format(
+                          "The number of errors caught when persist %s cache index to file operation was performed: ",
+                          name))
+                  .setCumulative()
+                  .setUnit("errors"));
     }
   }
 
@@ -211,27 +252,31 @@
 
   void persist() {
     logger.atInfo().log("Persisting cache keys index %s to %s file", name, indexFile);
-    ArrayList<TimedKey> toPersist;
-    synchronized (keys) {
-      toPersist = new ArrayList<>(keys);
-    }
-    CacheSerializer<T> serializer = CacheSerializers.getKeySerializer(name);
-    try (DataOutputStream dos =
-        new DataOutputStream(new BufferedOutputStream(new FileOutputStream(tempIndexFile)))) {
-      for (TimedKey key : toPersist) {
-        writeKey(serializer, dos, key);
+    try (Timer0.Context timer = metrics.persistLatency.start()) {
+      ArrayList<TimedKey> toPersist;
+      synchronized (keys) {
+        toPersist = new ArrayList<>(keys);
       }
-      dos.flush();
-      indexFile.delete();
-      if (!tempIndexFile.renameTo(indexFile)) {
-        logger.atSevere().log(
-            "Renaming temporary index file %s to %s was not successful. Persisting cache key index failed.",
-            tempIndexFile, indexFile);
-        return;
+      CacheSerializer<T> serializer = CacheSerializers.getKeySerializer(name);
+      try (DataOutputStream dos =
+          new DataOutputStream(new BufferedOutputStream(new FileOutputStream(tempIndexFile)))) {
+        for (TimedKey key : toPersist) {
+          writeKey(serializer, dos, key);
+        }
+        dos.flush();
+        indexFile.delete();
+        if (!tempIndexFile.renameTo(indexFile)) {
+          logger.atWarning().log(
+              "Renaming temporary index file %s to %s was not successful",
+              tempIndexFile, indexFile);
+          metrics.persistFailures.increment();
+          return;
+        }
+        logger.atInfo().log("Cache keys index %s was persisted to %s file", name, indexFile);
+      } catch (Exception e) {
+        logger.atSevere().withCause(e).log("Persisting cache keys index %s failed", name);
+        metrics.persistFailures.increment();
       }
-      logger.atInfo().log("Cache keys index %s was persisted to %s file", name, indexFile);
-    } catch (Exception e) {
-      logger.atSevere().withCause(e).log("Persisting cache keys index %s failed", name);
     }
   }
 
@@ -242,6 +287,7 @@
             "Gerrit was not closed properly as index persist operation was not finished: temporary"
                 + " index storage file %s exists for %s cache.",
             tempIndexFile, name);
+        metrics.restoreFailures.increment();
         if (!tempIndexFile.delete()) {
           logger.atSevere().log(
               "Cannot delete the temporary index storage file %s.", tempIndexFile);
@@ -256,21 +302,25 @@
                   + " Existing persisted entries will be pruned only when they are accessed after"
                   + " Gerrit start.",
               name, indexFile.getPath());
+          metrics.restoreFailures.increment();
         }
         return;
       }
 
       logger.atInfo().log("Restoring cache keys index %s from %s file", name, indexFile);
-      CacheSerializer<T> serializer = CacheSerializers.getKeySerializer(name);
-      try (DataInputStream dis =
-          new DataInputStream(new BufferedInputStream(new FileInputStream(indexFile)))) {
-        while (dis.available() > 0) {
-          keys.add(readKey(serializer, dis));
+      try (Timer0.Context timer = metrics.restoreLatency.start()) {
+        CacheSerializer<T> serializer = CacheSerializers.getKeySerializer(name);
+        try (DataInputStream dis =
+            new DataInputStream(new BufferedInputStream(new FileInputStream(indexFile)))) {
+          while (dis.available() > 0) {
+            keys.add(readKey(serializer, dis));
+          }
+          logger.atInfo().log("Cache keys index %s was restored from %s file", name, indexFile);
         }
-        logger.atInfo().log("Cache keys index %s was restored from %s file", name, indexFile);
       }
     } catch (Exception e) {
       logger.atSevere().withCause(e).log("Restoring cache keys index %s failed", name);
+      metrics.restoreFailures.increment();
     }
   }
 
diff --git a/src/main/resources/Documentation/metrics.md b/src/main/resources/Documentation/metrics.md
index b0bc902..03b8918 100644
--- a/src/main/resources/Documentation/metrics.md
+++ b/src/main/resources/Documentation/metrics.md
@@ -32,5 +32,17 @@
 * cache/chroniclemap/keys_index_remove_lru_key_latency_<cache-name>
   : The latency of removing and consuming LRU key from an index.
 
-* "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
+* cache/chroniclemap/keys_index_restore_latency_<cache-name>
+  : The latency of restoring an index from a file (performed once during the plugin start).
+
+* cache/chroniclemap/keys_index_persist_latency_<cache-name>
+  : The latency of persisting an index to a file.
+
+* cache/chroniclemap/store_put_failures_<cache-name>
+  : The number of errors caught when inserting entries in chronicle-map store
+
+* cache/chroniclemap/keys_index_restore_failures_<cache-name>
+  : The number of errors caught when restore cache index from file operation was performed
+
+* cache/chroniclemap/keys_index_persist_failures_<cache-name>
+  : The number of errors caught when persist cache index to file operation was performed
\ 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 9c839f0..d90eaff 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
@@ -534,6 +534,12 @@
         "cache/chroniclemap/keys_index_remove_and_consume_older_than_latency_" + sanitized;
     String keysIndexRemoveLruLatencyMetricName =
         "cache/chroniclemap/keys_index_remove_lru_key_latency_" + sanitized;
+    String keysIndexRestoreName = "cache/chroniclemap/keys_index_restore_latency_" + sanitized;
+    String keysIndexPersistName = "cache/chroniclemap/keys_index_persist_latency_" + sanitized;
+    String keysIndexRestoreFailuresName =
+        "cache/chroniclemap/keys_index_restore_failures_" + sanitized;
+    String keysIndexPersistFailuresName =
+        "cache/chroniclemap/keys_index_persist_failures_" + sanitized;
 
     newCacheWithMetrics(cacheName, null);
 
@@ -544,6 +550,10 @@
     getTimer(keysIndexAddLatencyMetricName);
     getTimer(keysIndexRemoveOlderThanLatencyMetricName);
     getTimer(keysIndexRemoveLruLatencyMetricName);
+    getTimer(keysIndexRestoreName);
+    getTimer(keysIndexPersistName);
+    getCounter(keysIndexRestoreFailuresName);
+    getCounter(keysIndexPersistFailuresName);
   }
 
   private int valueSize(String value) {