Add load durations to HitHashMap uses

The PredicateCache and changesByNamesFactoryQuery now make explicit use
of getOrStartLoad for accurate load duration accounting. The other users
do this implicitly with computeIfAbsent.

Change-Id: Ib505994e1594a3f67e9054e83c42d92057127df9
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
index a7f0f72..453c118 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/HitHashMap.java
@@ -26,7 +26,10 @@
   public static class Statistics {
     public long hits;
     public int size;
+    public long sumNanosecondsLoading;
     public List<Object> elements;
+
+    protected transient StopWatch loadingStopWatch;
   }
 
   public static final long serialVersionUID = 1;
@@ -50,6 +53,14 @@
     return v;
   }
 
+  public V getOrStartLoad(K key) {
+    V v = get(key);
+    if (v == null) {
+      startLoad();
+    }
+    return v;
+  }
+
   @Override
   public V getOrDefault(Object key, V dv) {
     V v = get(key);
@@ -61,11 +72,13 @@
 
   @Override
   public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
-    V v = get(key);
+    V v = getOrStartLoad(key);
     if (v == null) {
       v = mappingFunction.apply(key);
       if (v != null) {
         put(key, v);
+      } else {
+        stopLoad(key);
       }
     }
     return v;
@@ -73,6 +86,7 @@
 
   @Override
   public V put(K key, V value) {
+    stopLoad(key);
     if (statistics != null && value instanceof TracksStatistics) {
       ((TracksStatistics) value).ensureStatistics();
     }
@@ -124,9 +138,23 @@
     throw new UnsupportedOperationException(); // Todo if needed
   }
 
+  public void startLoad() {
+    if (statistics != null && statistics.loadingStopWatch != null) {
+      statistics.loadingStopWatch.start();
+    }
+  }
+
+  public void stopLoad(K key) {
+    if (statistics != null && statistics.loadingStopWatch != null) {
+      statistics.loadingStopWatch.stop();
+    }
+  }
+
   @Override
   public void initStatistics() {
     statistics = new Statistics();
+    statistics.loadingStopWatch =
+        new StopWatch().enable().setConsumer(ns -> statistics.sumNanosecondsLoading += ns);
   }
 
   @Override
@@ -147,6 +175,7 @@
     if (!elementStatistics.isEmpty()) {
       statistics.elements = elementStatistics;
     }
+    statistics.loadingStopWatch = null;
     return statistics;
   }
 }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java b/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
index 8655e6b..4c3748d 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/PredicateCache.java
@@ -45,8 +45,7 @@
   protected final ChangeQueryBuilder cqb;
   protected final Set<String> cacheableByBranchPredicateClassNames;
   protected final CurrentUser user;
-  protected final StatisticsMap<
-          String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
+  protected final HitHashMap<String, ThrowingProvider<Predicate<ChangeData>, QueryParseException>>
       predicatesByQuery = new HitHashMap<>();
 
   protected Statistics statistics;
@@ -90,7 +89,7 @@
 
   protected Predicate<ChangeData> getPredicate(String query) throws QueryParseException {
     ThrowingProvider<Predicate<ChangeData>, QueryParseException> predProvider =
-        predicatesByQuery.get(query);
+        predicatesByQuery.getOrStartLoad(query);
     if (predProvider != null) {
       return predProvider.get();
     }
diff --git a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
index 17b1a1b..172208a 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/task/TaskTree.java
@@ -86,7 +86,7 @@
   protected final NodeList root = new NodeList();
   protected final Provider<ChangeQueryBuilder> changeQueryBuilderProvider;
   protected final Provider<ChangeQueryProcessor> changeQueryProcessorProvider;
-  protected final StatisticsMap<String, List<ChangeData>> changesByNamesFactoryQuery =
+  protected final HitHashMap<String, List<ChangeData>> changesByNamesFactoryQuery =
       new HitHashMap<>();
   protected final StatisticsMap<SubSectionKey, List<Task>> definitionsBySubSection =
       new HitHashMapOfCollection<>();
@@ -593,7 +593,7 @@
   }
 
   public List<ChangeData> query(String query) throws StorageException, QueryParseException {
-    List<ChangeData> changeDataList = changesByNamesFactoryQuery.get(query);
+    List<ChangeData> changeDataList = changesByNamesFactoryQuery.getOrStartLoad(query);
     if (changeDataList == null) {
       changeDataList =
           changeQueryProcessorProvider