CacheMetrics: expose request count for cache sizing decisions

Cache hit ratio alone is insufficient for deciding which caches should
be grown. A rarely accessed cache may have a poor hit ratio because most
lookups are first-time accesses, so increasing its size provides little
benefit.

Expose the total request count alongside existing cache metrics so cache
growth decisions can be prioritized based on both hit ratio and access
volume. This is especially useful when storage constraints prevent
growing every cache, allowing heavily used caches with lower hit ratios
to be prioritized over infrequently accessed caches.

Release-Notes: Emit caches/memory_request_count and caches/disk_request_count metrics
Change-Id: I4714615892e4b8c89a040851760539d3fab6bb28
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index 7da1c18..5c13aef 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -208,12 +208,20 @@
 * `caches/memory_eviction_count`: Memory eviction count.
 ** `cache_name`:
    The name of the cache.
+* `caches/memory_request_count`: Total number of lookups (hits + misses) against
+  the in-memory cache.
+** `cache_name`:
+   The name of the cache.
 * `caches/disk_cached`: Disk entries used by persistent cache.
 ** `cache_name`:
    The name of the cache.
 * `caches/disk_hit_ratio`: Disk hit ratio for persistent cache.
 ** `cache_name`:
    The name of the cache.
+* `caches/disk_request_count`: Total number of lookups (hits + misses) against
+  the persistent disk cache.
+** `cache_name`:
+   The name of the cache.
 * `caches/refresh_count`: The number of refreshes per cache with an indicator if
   a reload was necessary.
 ** `cache`:
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 7053df0..e6dd0a7 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -59,6 +59,12 @@
             Long.class,
             new Description("Memory eviction count").setGauge().setUnit("evicted entries"),
             F_NAME);
+    CallbackMetric1<String, Long> memReq =
+        metrics.newCallbackMetric(
+            "caches/memory_request_count",
+            Long.class,
+            new Description("Memory request count").setGauge().setUnit("requests"),
+            F_NAME);
     CallbackMetric1<String, Long> perDiskEnt =
         metrics.newCallbackMetric(
             "caches/disk_cached",
@@ -79,9 +85,18 @@
                 .setGauge()
                 .setUnit("invalidated entries"),
             F_NAME);
+    CallbackMetric1<String, Long> perDiskReq =
+        metrics.newCallbackMetric(
+            "caches/disk_request_count",
+            Long.class,
+            new Description("Disk request count for persistent cache")
+                .setGauge()
+                .setUnit("requests"),
+            F_NAME);
 
     ImmutableSet<CallbackMetric<?>> cacheMetrics =
-        ImmutableSet.of(memEnt, memHit, memEvict, perDiskEnt, perDiskHit, perDiskInvalid);
+        ImmutableSet.of(
+            memEnt, memHit, memEvict, memReq, perDiskEnt, perDiskHit, perDiskInvalid, perDiskReq);
 
     metrics.newTrigger(
         cacheMetrics,
@@ -93,12 +108,14 @@
             memEnt.set(name, c.size());
             memHit.set(name, cstats.hitRate() * 100);
             memEvict.set(name, cstats.evictionCount());
+            memReq.set(name, cstats.requestCount());
             if (c instanceof PersistentCache
                 && config.getBoolean("cache", "enableDiskStatMetrics", false)) {
               PersistentCache.DiskStats d = ((PersistentCache) c).diskStats();
               perDiskEnt.set(name, d.size());
               perDiskHit.set(name, hitRatio(d));
               perDiskInvalid.set(name, d.invalidatedCount());
+              perDiskReq.set(name, d.requestCount());
             }
           }
           cacheMetrics.forEach(CallbackMetric::prune);