Introduce option to disable caching by Change number

When importing Changes from other Gerrit servers, the change
numbers may not be unique anymore, causing the query results
to have inconsistent values due to the caching by change number.

Introduce a new setting 'index.cacheQueryResultsByChangeNum' for
turning off Change JSON caching in outputs for allowing the correct
rendering even in case of Change numbers duplicates in the output.

Release-Notes: Introduce option 'index.'cacheQueryResultsByChangeNum to disable caching by Change number
Change-Id: Ib01fa73529beaac7a9c9494b755692ded0c81f3d
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index 661af0c..1c26232 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -3259,6 +3259,15 @@
 If not set or set to a zero, defaults to the number of logical CPUs as returned
 by the JVM. If set to a negative value, defaults to a direct executor.
 
+[[index.cacheQueryResultsByChangeNum]]index.cacheQueryResultsByChangeNum::
++
+Allow to cache and reuse the change JSON elements by their Change number.
+This improves the performance of queries that are returning Changes duplicates.
+It needs to be turned off when having Changes imported from other servers
+because of the potential conflicts of change numbers.
++
+Defaults to true.
+
 [[index.onlineUpgrade]]index.onlineUpgrade::
 +
 Whether to upgrade to new index schema versions while the server is
diff --git a/java/com/google/gerrit/server/change/ChangeJson.java b/java/com/google/gerrit/server/change/ChangeJson.java
index 32e40eb..02b0a60 100644
--- a/java/com/google/gerrit/server/change/ChangeJson.java
+++ b/java/com/google/gerrit/server/change/ChangeJson.java
@@ -235,6 +235,7 @@
   private final Optional<PluginDefinedInfosFactory> pluginDefinedInfosFactory;
   private final boolean includeMergeable;
   private final boolean lazyLoad;
+  private final boolean cacheQueryResultsByChangeNum;
 
   private AccountLoader accountLoader;
   private FixInput fix;
@@ -274,6 +275,8 @@
     this.includeMergeable = MergeabilityComputationBehavior.fromConfig(cfg).includeInApi();
     this.lazyLoad = containsAnyOf(this.options, REQUIRE_LAZY_LOAD);
     this.pluginDefinedInfosFactory = pluginDefinedInfosFactory;
+    this.cacheQueryResultsByChangeNum =
+        cfg.getBoolean("index", "cacheQueryResultsByChangeNum", true);
 
     logger.atFine().log("options = %s", options);
   }
@@ -496,7 +499,7 @@
         // This problem has two sides where 'last in the list' has to be respected:
         // (1) Caching
         // (2) Reusing
-        boolean isCacheable = i != changes.size() - 1;
+        boolean isCacheable = cacheQueryResultsByChangeNum && (i != changes.size() - 1);
         ChangeData cd = changes.get(i);
         ChangeInfo info = cache.get(cd.getId());
         if (info != null && isCacheable) {