Cache depends on by change per thread

Caching depends on by change per thread improves the performance
of queries such as "change:<> has:a_depends-on --depends-on--all" by
3ms against ES index backend (i.e. on avg took 20ms before and 17ms
after this change over 50 samples). This improvement is due to the fact
that has:a_depends-on and --depends-on--all computes the same
depends-on information.

One can also benefit from this performance improvement when using
has:a_depends-on and in_depends-on:<> operators in Task plugin config.

Change-Id: Ia97f08bf8c0e607a4044db281f191bdd7ebe3f03
diff --git a/src/main/java/com/googlesource/gerrit/plugins/depends/on/ChangeMessageStore.java b/src/main/java/com/googlesource/gerrit/plugins/depends/on/ChangeMessageStore.java
index 0bc8122..67f5040 100644
--- a/src/main/java/com/googlesource/gerrit/plugins/depends/on/ChangeMessageStore.java
+++ b/src/main/java/com/googlesource/gerrit/plugins/depends/on/ChangeMessageStore.java
@@ -25,6 +25,7 @@
 import com.google.gerrit.extensions.restapi.RestApiException;
 import com.google.gerrit.server.ChangeMessagesUtil;
 import com.google.gerrit.server.CurrentUser;
+import com.google.gerrit.server.cache.PerThreadCache;
 import com.google.gerrit.server.change.ChangeResource;
 import com.google.gerrit.server.change.RevisionResource;
 import com.google.gerrit.server.notedb.ChangeNotes;
@@ -38,7 +39,9 @@
 import com.googlesource.gerrit.plugins.depends.on.extensions.DependencyResolver;
 import com.googlesource.gerrit.plugins.depends.on.formats.Comment;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.stream.Collectors;
@@ -48,6 +51,11 @@
 public class ChangeMessageStore implements DependencyResolver {
   private static final Logger log = LoggerFactory.getLogger(ChangeMessageStore.class);
 
+  public static class DependsOnByChangeCache extends HashMap {}
+
+  protected static final PerThreadCache.Key<DependsOnByChangeCache> DEPENDS_ON_BY_CHANGE_CACHE_KEY =
+      PerThreadCache.Key.create(DependsOnByChangeCache.class);
+
   public interface Factory {
     ChangeMessageStore create();
   }
@@ -104,6 +112,17 @@
   }
 
   public List<DependsOn> loadWithOrder(ChangeNotes changeNotes) throws StorageException {
+    PerThreadCache perThreadCache = PerThreadCache.get();
+    if (perThreadCache != null) {
+      Map<Change.Id, List<DependsOn>> dependsOnByChangeCache =
+          perThreadCache.get(DEPENDS_ON_BY_CHANGE_CACHE_KEY, DependsOnByChangeCache::new);
+      return dependsOnByChangeCache.computeIfAbsent(
+          changeNotes.getChangeId(), (id) -> loadWithOrderFromNoteDb(changeNotes));
+    }
+    return loadWithOrderFromNoteDb(changeNotes);
+  }
+
+  private List<DependsOn> loadWithOrderFromNoteDb(ChangeNotes changeNotes) throws StorageException {
     for (ChangeMessage message : Lists.reverse(cmUtil.byChange(changeNotes))) {
       Optional<List<DependsOn>> deps = Comment.from(message.getMessage());
       if (deps.isPresent()) {