Merge "Change checks filter to also match tag names"
diff --git a/Documentation/metrics.txt b/Documentation/metrics.txt
index feafe59..0bbf4fb 100644
--- a/Documentation/metrics.txt
+++ b/Documentation/metrics.txt
@@ -12,41 +12,112 @@
 
 * `build/label`: Version of Gerrit server software.
 * `events`: Triggered events.
+** `type`:
+   The type of the event.
 
 === Actions
 
 * `action/retry_attempt_count`: Number of retry attempts made
-by RetryHelper to execute an action (0 == single attempt, no retry)
+  by RetryHelper to execute an action (0 == single attempt, no retry)
+** `action_type`:
+   The type of the action that was retried.
+** `operation_name`:
+   The name of the operation that was retried.
+** `cause`:
+   The original cause that triggered the retry.
 * `action/retry_timeout_count`: Number of action executions of RetryHelper
-that ultimately timed out
+  that ultimately timed out
+** `action_type`:
+   The type of the action that was retried.
+** `operation_name`:
+   The name of the operation that was retried.
+** `cause`:
+   The original cause that triggered the retry.
 * `action/auto_retry_count`: Number of automatic retries with tracing
+** `action_type`:
+   The type of the action that was retried.
+** `operation_name`:
+   The name of the operation that was retried.
+** `cause`:
+   The cause for the retry.
 * `action/failures_on_auto_retry_count`: Number of failures on auto retry
+** `action_type`:
+   The type of the action that was retried.
+** `operation_name`:
+   The name of the operation that was retried.
+** `cause`:
+   The cause for the retry.
 
 [[cancellations]]
 === Cancellations
 
 * `cancellation/advisory_deadline_count`: Exceeded advisory deadlines by request
+** `request_type`:
+   The type of the request to which the advisory deadline applied.
+** `request_uri`:
+   The redacted URI of the request to which the advisory deadline applied (only
+   set for request_type = REST).
+** `deadline_id`:
+   The ID of the advisory deadline.
 * `cancellation/cancelled_requests_count`: Number of request cancellations by
   request
+** `request_type`:
+   The type of the request that was cancelled.
+** `request_uri`:
+   The redacted URI of the request that was cancelled (only set for
+   request_type = REST).
+** `cancellation_reason`:
+   The reason why the request was cancelled.
 * `cancellation/receive_timeout_count`: Number of requests that are cancelled
   because link:config.html#receive.timeout[receive.timout] is exceeded
+** `cancellation_type`:
+   The cancellation type (graceful or forceful).
 
 [[performance]]
 === Performance
 
 * `performance/operations`: Latency of performing operations
+** `operation_name`:
+   The operation that was performed.
+** `change_identifier`:
+   The ID of the change for which the operation was performed (format =
+   '<project>~<numeric-change-id>').
+** `trace_id`:
+   The ID of the trace if tracing was done.
 * `performance/operations_count`: Number of performed operations
+** `operation_name`:
+   The operation that was performed.
+** `trace_id`:
+   The ID of the trace if tracing was done.
+** `request`:
+   The request for which the operation was performed (format = '<request-type>
+   <redacted-request-uri>').
+* `performance/plugin_operations_count`: Number of performed operations by
+  plugin
+** `operation_name`:
+   The operation that was performed.
+** `plugin`:
+   The name of the plugin that performed the operation.
+** `trace_id`:
+   The ID of the trace if tracing was done.
+
 
 === Pushes
 
-* `receivecommits/changes`: histogram of number of changes processed
-in a single upload, split up by update type (change created/updated,
-change autoclosed).
-* `receivecommits/latency`: latency per change for processing a push,
-split up by update type (create+replace, and autoclose)
-* `receivecommits/push_latency`: total latency for processing a push,
-split up by update type (create+replace, autoclose, normal)
-* `receivecommits/timeout`: number of timeouts during push processing.
+* `receivecommits/changes`: histogram of number of changes processed in a single
+   upload
+** `type`:
+   type of push (create/replace, autoclose)
+* `receivecommits/latency_per_push`: processing delay for a processing single
+  push
+** `type`:
+   type of push (create/replace, autoclose, normal)
+* `receivecommits/latency_per_push_per_change`: Processing delay per push
+  divided by the number of changes in said push. (Only includes pushes which
+  contain changes.)
+** `type`:
+   type of push (create/replace, autoclose, normal)
+* `receivecommits/timeout`: rate of push timeouts
 
 === Process
 
@@ -64,27 +135,58 @@
 * `proc/jvm/memory/object_pending_finalization_count`: Approximate number of
 objects needing finalization.
 * `proc/jvm/gc/count`: Number of GCs.
+** `gc_name`:
+   The name of the garbage collector.
 * `proc/jvm/gc/time`: Approximate accumulated GC elapsed time.
-* `proc/jvm/memory/pool/committed/<pool name>`: Committed amount of memory for pool.
-* `proc/jvm/memory/pool/max/<pool name>`: Maximum amount of memory for pool.
-* `proc/jvm/memory/pool/used/<pool name>`: Used amount of memory for pool.
+** `gc_name`:
+   The name of the garbage collector.
+* `proc/jvm/memory/pool/committed`: Committed amount of memory for pool.
+** `pool_name`:
+   The name of the memory pool.
+* `proc/jvm/memory/pool/max`: Maximum amount of memory for pool.
+** `pool_name`:
+   The name of the memory pool.
+* `proc/jvm/memory/pool/used`: Used amount of memory for pool.
+** `pool_name`:
+   The name of the memory pool.
 * `proc/jvm/thread/num_live`: Current live thread count.
 * `proc/jvm/thread/num_daemon_live`: Current live daemon threads count.
-* `proc/jvm/thread/num_peak_live`: Peak live thread count since the Java virtual machine started or peak was reset.
-* `proc/jvm/thread/num_total_started`: Total number of threads created and also started since the Java virtual machine started.
-* `proc/jvm/thread/num_deadlocked_threads`: Number of threads that are deadlocked waiting for object monitors or ownable synchronizers.
-   If deadlocks waiting for ownable synchronizers can be monitored depends on the capabilities of the used JVM.
+* `proc/jvm/thread/num_peak_live`: Peak live thread count since the Java virtual
+  machine started or peak was reset.
+* `proc/jvm/thread/num_total_started`: Total number of threads created and also
+  started since the Java virtual machine started.
+* `proc/jvm/thread/num_deadlocked_threads`: Number of threads that are
+  deadlocked waiting for object monitors or ownable synchronizers.
+  If deadlocks waiting for ownable synchronizers can be monitored depends on the
+  capabilities of the used JVM.
 
 === Caches
 
 * `caches/memory_cached`: Memory entries.
+** `cache_name`:
+   The name of the cache.
 * `caches/memory_hit_ratio`: Memory hit ratio.
+** `cache_name`:
+   The name of the cache.
 * `caches/memory_eviction_count`: Memory eviction count.
+** `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.
-* `caches/refresh_count`: The number of refreshes per cache with an indicator if a reload was necessary.
-* `caches/diff/timeouts`: The number of git file diff computations that resulted in timeouts.
-* `caches/diff/legacy/timeouts`: The number of git file diff computations (using the legacy cache) that resulted in timeouts.
+** `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`:
+   The name of the cache.
+** `outdated`:
+   Whether the cache entry was outdated on reload.
+* `caches/diff/timeouts`: The number of git file diff computations that resulted
+  in timeouts.
+* `caches/diff/legacy/timeouts`: The number of git file diff computations (using
+  the legacy cache) that resulted in timeouts.
 
 Cache disk metrics are expensive to compute on larger installations and are not
 computed by default. They can be enabled via the
@@ -93,66 +195,110 @@
 
 === Change
 
-* `change/submit_rule_evaluation`: Latency for evaluating submit rules on a change.
-* `change/submit_type_evaluation`: Latency for evaluating the submit type on a change.
-* `change/post_review/draft_handling`: Total number of draft handling option (KEEP, PUBLISH, PUBLISH_ALL_REVISIONS) selected by users while posting a review.
+* `change/submit_rule_evaluation`: Latency for evaluating submit rules on a
+  change.
+* `change/submit_type_evaluation`: Latency for evaluating the submit type on a
+  change.
+* `change/post_review/draft_handling`: Total number of draft handling option
+  (KEEP, PUBLISH, PUBLISH_ALL_REVISIONS) selected by users while posting a
+  review.
+** `type`:
+  The type of the draft handling option (KEEP, PUBLISH, PUBLISH_ALL_REVISIONS).
 
 === Comments
 
-* `ported_comments/as_patchset_level`: Total number of comments ported as patchset-level comments.
-* `ported_comments/as_file_level`: Total number of comments ported as file-level comments.
-* `ported_comments/as_range_comments`: Total number of comments having line/range values in the ported patchset.
+* `ported_comments/as_patchset_level`: Total number of comments ported as
+  patchset-level comments.
+* `ported_comments/as_file_level`: Total number of comments ported as file-level
+  comments.
+* `ported_comments/as_range_comments`: Total number of comments having
+  line/range values in the ported patchset.
 
 === HTTP
 
 ==== Jetty
 
-* `http/server/jetty/connections/connections`: The current number of open connections
-* `http/server/jetty/connections/connections_total`: The total number of connections opened
-* `http/server/jetty/connections/connections_duration_max`: The max duration of a connection in ms
-* `http/server/jetty/connections/connections_duration_mean`: The mean duration of a connection in ms
-* `http/server/jetty/connections/connections_duration_stdev`: The standard deviation of the duration of a connection in ms
-* `http/server/jetty/connections/received_messages`: The total number of messages received
-* `http/server/jetty/connections/sent_messages`: The total number of messages sent
-* `http/server/jetty/connections/received_bytes`: Total number of bytes received by tracked connections
-* `http/server/jetty/connections/sent_bytes`: Total number of bytes sent by tracked connections"
+* `http/server/jetty/connections/connections`: The current number of open
+  connections
+* `http/server/jetty/connections/connections_total`: The total number of
+  connections opened
+* `http/server/jetty/connections/connections_duration_max`: The max duration of
+  a connection in ms
+* `http/server/jetty/connections/connections_duration_mean`: The mean duration
+  of a connection in ms
+* `http/server/jetty/connections/connections_duration_stdev`: The standard
+  deviation of the duration of a connection in ms
+* `http/server/jetty/connections/received_messages`: The total number of
+  messages received
+* `http/server/jetty/connections/sent_messages`: The total number of messages
+  sent
+* `http/server/jetty/connections/received_bytes`: Total number of bytes received
+  by tracked connections
+* `http/server/jetty/connections/sent_bytes`: Total number of bytes sent by
+  tracked connections
 * `http/server/jetty/threadpool/active_threads`: Active threads
 * `http/server/jetty/threadpool/idle_threads`: Idle threads
 * `http/server/jetty/threadpool/reserved_threads`: Reserved threads
 * `http/server/jetty/threadpool/max_pool_size`: Maximum thread pool size
 * `http/server/jetty/threadpool/min_pool_size`: Minimum thread pool size
 * `http/server/jetty/threadpool/pool_size`: Current thread pool size
-* `http/server/jetty/threadpool/queue_size`: Queued requests waiting for a thread
+* `http/server/jetty/threadpool/queue_size`: Queued requests waiting for a
+  thread
+* `http/server/jetty/threadpool/is_low_on_threads`: Whether thread pool is low
+  on threads
 
 ==== LDAP
 
 * `ldap/login_latency`: Latency of logins.
 * `ldap/user_search_latency`: Latency for searching the user account.
-* `ldap/group_search_latency`: Latency for querying the group memberships of an account.
+* `ldap/group_search_latency`: Latency for querying the group memberships of an
+  account.
 * `ldap/group_expansion_latency`: Latency for expanding nested groups.
 
 ==== REST API
 
 * `http/server/error_count`: Rate of REST API error responses.
+** `status`:
+   HTTP status code
 * `http/server/success_count`: Rate of REST API success responses.
+** `status`:
+   HTTP status code
 * `http/server/rest_api/count`: Rate of REST API calls by view.
+** `view`:
+   view implementation class
 * `http/server/rest_api/change_id_type`: Rate of REST API calls by change ID type.
+** `change_id_type`:
+   The type of the change identifier.
 * `http/server/rest_api/error_count`: Rate of REST API calls by view.
+** `view`:
+   view implementation class
+** `error_code`:
+   HTTP status code
+** `cause`:
+   The cause of the error.
 * `http/server/rest_api/server_latency`: REST API call latency by view.
+** `view`:
+   view implementation class
 * `http/server/rest_api/response_bytes`: Size of REST API response on network
-(may be gzip compressed) by view.
+  (may be gzip compressed) by view.
+** `view`:
+   view implementation class
 * `http/server/rest_api/change_json/to_change_info_latency`: Latency for
-toChangeInfo invocations in ChangeJson.
+  toChangeInfo invocations in ChangeJson.
 * `http/server/rest_api/change_json/to_change_infos_latency`: Latency for
-toChangeInfos invocations in ChangeJson.
+  toChangeInfos invocations in ChangeJson.
 * `http/server/rest_api/change_json/format_query_results_latency`: Latency for
-formatQueryResults invocations in ChangeJson.
-* `http/server/rest_api/ui_actions/latency`: Latency for RestView#getDescription calls.
+  formatQueryResults invocations in ChangeJson.
+* `http/server/rest_api/ui_actions/latency`: Latency for RestView#getDescription
+  calls.
+** `view`:
+   view implementation class
 
 === Query
 
 * `query/query_latency`: Successful query latency, accumulated over the life
-of the process.
+  of the process.
+** `index`: index name
 
 === Core Queues
 
@@ -171,11 +317,15 @@
 Each queue provides the following metrics:
 
 * `queue/<queue_name>/pool_size`: Current number of threads in the pool
-* `queue/<queue_name>/max_pool_size`: Maximum allowed number of threads in the pool
-* `queue/<queue_name>/active_threads`: Number of threads that are actively executing tasks
+* `queue/<queue_name>/max_pool_size`: Maximum allowed number of threads in the
+  pool
+* `queue/<queue_name>/active_threads`: Number of threads that are actively
+  executing tasks
 * `queue/<queue_name>/scheduled_tasks`: Number of scheduled tasks in the queue
-* `queue/<queue_name>/total_scheduled_tasks_count`: Total number of tasks that have been scheduled
-* `queue/<queue_name>/total_completed_tasks_count`: Total number of tasks that have completed execution
+* `queue/<queue_name>/total_scheduled_tasks_count`: Total number of tasks that
+  have been scheduled
+* `queue/<queue_name>/total_completed_tasks_count`: Total number of tasks that
+  have completed execution
 
 === SSH sessions
 
@@ -187,7 +337,7 @@
 
 * `topic/cross_project_submit`: number of cross-project topic submissions.
 * `topic/cross_project_submit_completed`: number of cross-project
-topic submissions that concluded successfully.
+  topic submissions that concluded successfully.
 
 === JGit
 
@@ -204,23 +354,34 @@
 * `load_success_count` : Successful cache loads for JGit block cache.
 * `miss_count` : Cache misses for JGit block cache.
 * `miss_ratio` : Cache miss ratio for JGit block cache.
-* `cache_used_per_repository` : Bytes of memory retained per repository for the top N repositories
-having most data in the cache. The number N of reported repositories is limited to 1000.
+* `cache_used_per_repository` : Bytes of memory retained per repository for the
+  top N repositories having most data in the cache. The number N of reported
+  repositories is limited to 1000.
+** `repository_name`: The name of the repository.
 
 === Git
 
 * `git/upload-pack/request_count`: Total number of git-upload-pack requests.
+** `operation`:
+   The name of the operation (CLONE, FETCH).
 * `git/upload-pack/phase_counting`: Time spent in the 'Counting...' phase.
+** `operation`:
+   The name of the operation (CLONE, FETCH).
 * `git/upload-pack/phase_compressing`: Time spent in the 'Compressing...' phase.
+** `operation`:
+   The name of the operation (CLONE, FETCH).
 * `git/upload-pack/phase_writing`: Time spent transferring bytes to client.
+** `operation`:
+   The name of the operation (CLONE, FETCH).
 * `git/upload-pack/pack_bytes`: Distribution of sizes of packs sent to clients.
+** `operation`:
+   The name of the operation (CLONE, FETCH).
 * `git/auto-merge/num_operations`: Number of auto merge operations and context.
+** `operation`:
+   The type of the operation (CACHE_LOAD, IN_MEMORY_WRITE, ON_DISK_WRITE).
 * `git/auto-merge/latency`: Latency of auto merge operations and context.
-
-=== BatchUpdate
-
-* `batch_update/execute_change_ops`: BatchUpdate change update latency,
-excluding reindexing
+** `operation`:
+   The type of the operation (CACHE_LOAD, IN_MEMORY_WRITE, ON_DISK_WRITE).
 
 === NoteDb
 
@@ -230,43 +391,63 @@
 * `notedb/parse_latency`: NoteDb parse latency for changes.
 * `notedb/external_id_cache_load_count`: Total number of times the external ID
   cache loader was called.
-* `notedb/external_id_partial_read_latency`: Latency for generating a new external ID
-  cache state from a prior state.
+** `partial`:
+   Whether the reload was partial.
+* `notedb/external_id_partial_read_latency`: Latency for generating a new
+  external ID cache state from a prior state.
 * `notedb/external_id_update_count`: Total number of external ID updates.
 * `notedb/read_all_external_ids_latency`: Latency for reading all
-external ID's from NoteDb.
+  external ID's from NoteDb.
 * `notedb/read_single_account_config_latency`: Latency for reading a single
-account config from NoteDb.
+  account config from NoteDb.
 * `notedb/read_single_external_id_latency`: Latency for reading a single
-external ID from NoteDb.
+  external ID from NoteDb.
 
 === Permissions
 
-* `permissions/permission_collection/filter_latency`: Latency to filter access sections
-by user and ref.
+* `permissions/permission_collection/filter_latency`: Latency for access filter
+  computations in PermissionCollection
 * `permissions/ref_filter/full_filter_count`: Rate of full ref filter operations
-* `permissions/ref_filter/skip_filter_count`: Rate of ref filter operations where
-we skip full evaluation because the user can read all refs
+* `permissions/ref_filter/skip_filter_count`: Rate of ref filter operations
+  where we skip full evaluation because the user can read all refs
 
 === Reviewer Suggestion
 
 * `reviewer_suggestion/query_accounts`: Latency for querying accounts for
-reviewer suggestion.
+  reviewer suggestion.
 * `reviewer_suggestion/recommend_accounts`: Latency for recommending accounts
-for reviewer suggestion.
+  for reviewer suggestion.
 * `reviewer_suggestion/load_accounts`: Latency for loading accounts for
-reviewer suggestion.
+  reviewer suggestion.
 * `reviewer_suggestion/query_groups`: Latency for querying groups for reviewer
-suggestion.
+  suggestion.
+* `reviewer_suggestion/filter_visibility`: Latency for removing users that can't
+  see the change
 
 === Repo Sequences
 
 * `sequence/next_id_latency`: Latency of requesting IDs from repo sequences.
+** `sequence`:
+   The sequence from which IDs were retrieved.
+** `multiple`:
+   Whether more than one ID was retrieved.
 
 === Plugin
 
 * `plugin/latency`: Latency for plugin invocation.
+** `plugin_name`"
+   The name of the plugin.
+** `class`:
+   The class of the plugin that was invoked.
+** `export_value`:
+   The export name under which the invoked class is registered.
 * `plugin/error_count`: Number of plugin errors.
+** `plugin_name`"
+   The name of the plugin.
+** `class`:
+   The class of the plugin that was invoked.
+** `export_value`:
+   The export name under which the invoked class is registered.
 
 === Group
 
@@ -275,11 +456,19 @@
 === Replication Plugin
 
 * `plugins/replication/replication_latency`: Time spent pushing to remote
-destination.
+  destination.
+** `destination`: The destination of the replication.
 * `plugins/replication/replication_delay`: Time spent waiting before pushing to
-remote destination.
+  remote destination.
+** `destination`: The destination of the replication.
 * `plugins/replication/replication_retries`: Number of retries when pushing to
-remote destination.
+  remote destination.
+** `destination`: The destination of the replication.
+* `plugins/replication/latency_slower_than_threshold`: latency for project to
+  destination, where latency was slower than threshold
+** `slow_threshold`: The threshold.
+** `project`: The name of the project.
+** `destination`: The destination of the replication.
 
 === License
 
diff --git a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
index e8611b3..d64bd19 100644
--- a/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
+++ b/java/com/google/gerrit/metrics/proc/JGitMetricModule.java
@@ -184,7 +184,9 @@
                         + "having most data in the cache.")
                 .setGauge()
                 .setUnit("byte"),
-            Field.ofString("repository_name", Metadata.Builder::projectName).build());
+            Field.ofString("repository_name", Metadata.Builder::projectName)
+                .description("The name of the repository.")
+                .build());
     metrics.newTrigger(
         repoEnt,
         () -> {
diff --git a/java/com/google/gerrit/pgm/init/InitAuth.java b/java/com/google/gerrit/pgm/init/InitAuth.java
index c15cff3..948ec49 100644
--- a/java/com/google/gerrit/pgm/init/InitAuth.java
+++ b/java/com/google/gerrit/pgm/init/InitAuth.java
@@ -26,6 +26,7 @@
 import com.google.gerrit.pgm.init.api.InitFlags;
 import com.google.gerrit.pgm.init.api.InitStep;
 import com.google.gerrit.pgm.init.api.Section;
+import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.mail.SignedToken;
 import com.google.inject.Inject;
 import com.google.inject.Singleton;
@@ -42,11 +43,13 @@
   private final Section ldap;
   private final Section receive;
   private final InitFlags flags;
+  private final SitePaths site;
 
   @Inject
-  InitAuth(InitFlags flags, ConsoleUI ui, Section.Factory sections) {
+  InitAuth(InitFlags flags, ConsoleUI ui, final SitePaths site, Section.Factory sections) {
     this.flags = flags;
     this.ui = ui;
+    this.site = site;
     this.auth = sections.get("auth", null);
     this.ldap = sections.get("ldap", null);
     this.receive = sections.get(RECEIVE, null);
@@ -62,6 +65,10 @@
     }
 
     initSignedPush();
+
+    if (site.isNew) {
+      initUserNameCaseSensitivity();
+    }
   }
 
   private void initAuthType() {
@@ -156,4 +163,9 @@
     boolean enable = ui.yesno(def, "Enable signed push support");
     receive.set("enableSignedPush", Boolean.toString(enable));
   }
+
+  private void initUserNameCaseSensitivity() {
+    boolean enableCaseInsensitivity = ui.yesno(true, "Use case insensitive usernames");
+    auth.set("userNameCaseInsensitive", Boolean.toString(enableCaseInsensitivity));
+  }
 }
diff --git a/java/com/google/gerrit/server/CancellationMetrics.java b/java/com/google/gerrit/server/CancellationMetrics.java
index 487a748..f534ccb 100644
--- a/java/com/google/gerrit/server/CancellationMetrics.java
+++ b/java/com/google/gerrit/server/CancellationMetrics.java
@@ -43,7 +43,7 @@
                 .build(),
             Field.ofString("request_uri", Metadata.Builder::restViewName)
                 .description(
-                    "The URI of the request to which the advisory deadline applied"
+                    "The redacted URI of the request to which the advisory deadline applied"
                         + " (only set for request_type = REST).")
                 .build(),
             Field.ofString("deadline_id", (metadataBuilder, resolveAllUsers) -> {})
@@ -59,7 +59,7 @@
                 .build(),
             Field.ofString("request_uri", Metadata.Builder::restViewName)
                 .description(
-                    "The URI of the request that was cancelled"
+                    "The redacted URI of the request that was cancelled"
                         + " (only set for request_type = REST).")
                 .build(),
             Field.ofEnum(
diff --git a/java/com/google/gerrit/server/PerformanceMetrics.java b/java/com/google/gerrit/server/PerformanceMetrics.java
index fa6e22c..85452ce 100644
--- a/java/com/google/gerrit/server/PerformanceMetrics.java
+++ b/java/com/google/gerrit/server/PerformanceMetrics.java
@@ -32,9 +32,12 @@
 public class PerformanceMetrics implements PerformanceLogger {
   private static final String OPERATION_LATENCY_METRIC_NAME = "performance/operations";
   private static final String OPERATION_COUNT_METRIC_NAME = "performance/operations_count";
+  private static final String PLUGIN_OPERATION_COUNT_METRIC_NAME =
+      "performance/plugin_operations_count";
 
   public final Timer3<String, String, String> operationsLatency;
   public final Counter3<String, String, String> operationsCounter;
+  public final Counter3<String, String, String> pluginOperationsCounter;
 
   @Inject
   PerformanceMetrics(MetricMaker metricMaker) {
@@ -42,13 +45,29 @@
         Field.ofString(
                 "operation_name",
                 (metadataBuilder, fieldValue) -> metadataBuilder.operationName(fieldValue))
+            .description("The operation that was performed.")
             .build();
     Field<String> changeIdentifierField =
-        Field.ofString("change_identifier", (metadataBuilder, fieldValue) -> {}).build();
+        Field.ofString("change_identifier", (metadataBuilder, fieldValue) -> {})
+            .description(
+                "The ID of the change for which the operation was performed"
+                    + " (format = '<project>~<numeric-change-id>').")
+            .build();
     Field<String> traceIdField =
-        Field.ofString("trace_id", (metadataBuilder, fieldValue) -> {}).build();
+        Field.ofString("trace_id", (metadataBuilder, fieldValue) -> {})
+            .description("The ID of the trace if tracing was done.")
+            .build();
     Field<String> requestField =
-        Field.ofString("request", (metadataBuilder, fieldValue) -> {}).build();
+        Field.ofString("request", (metadataBuilder, fieldValue) -> {})
+            .description(
+                "The request for which the operation was performed"
+                    + " (format = '<request-type> <redacted-request-uri>').")
+            .build();
+    Field<String> pluginField =
+        Field.ofString(
+                "plugin", (metadataBuilder, fieldValue) -> metadataBuilder.pluginName(fieldValue))
+            .description("The name of the plugin that performed the operation.")
+            .build();
 
     this.operationsLatency =
         metricMaker.newTimer(
@@ -66,6 +85,13 @@
             operationNameField,
             traceIdField,
             requestField);
+    this.pluginOperationsCounter =
+        metricMaker.newCounter(
+            PLUGIN_OPERATION_COUNT_METRIC_NAME,
+            new Description("Number of performed operations by plugin").setRate(),
+            operationNameField,
+            pluginField,
+            traceIdField);
   }
 
   @Override
@@ -91,6 +117,9 @@
 
     String requestTag = TraceContext.getTag(TraceRequestListener.TAG_REQUEST).orElse("");
     operationsCounter.increment(operation, traceId, requestTag);
+
+    TraceContext.getPluginTag()
+        .ifPresent(pluginName -> pluginOperationsCounter.increment(operation, pluginName, traceId));
   }
 
   private String formatChangeIdentifier(@Nullable Metadata metadata) {
diff --git a/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java b/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
index 27ac9f4..db030f9 100644
--- a/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
+++ b/java/com/google/gerrit/server/account/ServiceUserClassifierImpl.java
@@ -76,7 +76,7 @@
               .get(toTraverse.remove(0))
               .orElseThrow(() -> new IllegalStateException("invalid subgroup"));
       if (seen.contains(currentGroup.getGroupUUID())) {
-        logger.atWarning().log(
+        logger.atFine().log(
             "Skipping %s because it's a cyclic subgroup", currentGroup.getGroupUUID());
         continue;
       }
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalId.java b/java/com/google/gerrit/server/account/externalids/ExternalId.java
index 8bf095c..30f4094 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalId.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalId.java
@@ -191,6 +191,7 @@
      * notes branch.
      */
     @SuppressWarnings("deprecation") // Use Hashing.sha1 for compatibility.
+    @Memoized
     public ObjectId sha1() {
       String keyString = isCaseInsensitive() ? get().toLowerCase(Locale.US) : get();
       return ObjectId.fromRaw(Hashing.sha1().hashString(keyString, UTF_8).asBytes());
@@ -226,7 +227,8 @@
     }
 
     @Override
-    public final int hashCode() {
+    @Memoized
+    public int hashCode() {
       return Objects.hash(sha1());
     }
 
@@ -322,7 +324,8 @@
    * </pre>
    */
   @Override
-  public final String toString() {
+  @Memoized
+  public String toString() {
     Config c = new Config();
     writeToConfig(c);
     return c.toText();
diff --git a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
index 5d81a25..72d703b 100644
--- a/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
+++ b/java/com/google/gerrit/server/account/externalids/ExternalIdCacheLoader.java
@@ -95,7 +95,9 @@
             new Description("Total number of external ID cache reloads from Git.")
                 .setRate()
                 .setUnit("updates"),
-            Field.ofBoolean("partial", Metadata.Builder::partial).build());
+            Field.ofBoolean("partial", Metadata.Builder::partial)
+                .description("Whether the reload was partial.")
+                .build());
     this.reloadDifferential =
         metricMaker.newTimer(
             "notedb/external_id_partial_read_latency",
diff --git a/java/com/google/gerrit/server/cache/CacheMetrics.java b/java/com/google/gerrit/server/cache/CacheMetrics.java
index 12194e7..f1fd4a8 100644
--- a/java/com/google/gerrit/server/cache/CacheMetrics.java
+++ b/java/com/google/gerrit/server/cache/CacheMetrics.java
@@ -35,7 +35,9 @@
 @Singleton
 public class CacheMetrics {
   private static final Field<String> F_NAME =
-      Field.ofString("cache_name", Metadata.Builder::cacheName).build();
+      Field.ofString("cache_name", Metadata.Builder::cacheName)
+          .description("The name of the cache.")
+          .build();
 
   @Inject
   public CacheMetrics(
diff --git a/java/com/google/gerrit/server/change/ChangeFinder.java b/java/com/google/gerrit/server/change/ChangeFinder.java
index ba104d8..9f253de 100644
--- a/java/com/google/gerrit/server/change/ChangeFinder.java
+++ b/java/com/google/gerrit/server/change/ChangeFinder.java
@@ -96,6 +96,7 @@
                 .setRate()
                 .setUnit("requests"),
             Field.ofEnum(ChangeIdType.class, "change_id_type", Metadata.Builder::changeIdType)
+                .description("The type of the change identifier.")
                 .build());
   }
 
diff --git a/java/com/google/gerrit/server/change/FileInfoJsonComparingImpl.java b/java/com/google/gerrit/server/change/FileInfoJsonComparingImpl.java
deleted file mode 100644
index a926147..0000000
--- a/java/com/google/gerrit/server/change/FileInfoJsonComparingImpl.java
+++ /dev/null
@@ -1,167 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.change;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.extensions.common.FileInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.metrics.Counter1;
-import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.MetricMaker;
-import com.google.gerrit.server.logging.Metadata;
-import com.google.gerrit.server.patch.DiffExecutor;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.Map;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * Implementation of FileInfoJson which uses {@link FileInfoJsonOldImpl}, but also runs {@link
- * FileInfoJsonNewImpl} asynchronously and compares the results. This implementation is temporary
- * and will be used to verify that the results are the same.
- */
-public class FileInfoJsonComparingImpl implements FileInfoJson {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
-  private final FileInfoJsonOldImpl oldImpl;
-  private final FileInfoJsonNewImpl newImpl;
-  private final ExecutorService executor;
-  private final Metrics metrics;
-
-  /**
-   * TODO(ghareeb): These metrics are temporary for launching the new diff cache redesign and are
-   * not documented. These will be removed soon.
-   */
-  @VisibleForTesting
-  @Singleton
-  static class Metrics {
-    private enum Status {
-      MATCH,
-      MISMATCH,
-      ERROR
-    }
-
-    final Counter1<Status> diffs;
-
-    @Inject
-    Metrics(MetricMaker metricMaker) {
-      diffs =
-          metricMaker.newCounter(
-              "diff/list_files/dark_launch",
-              new Description(
-                      "Total number of matching, non-matching, or error in list-files diffs in the old and new diff cache implementations.")
-                  .setRate()
-                  .setUnit("count"),
-              Field.ofEnum(Status.class, "type", Metadata.Builder::eventType).build());
-    }
-  }
-
-  @Inject
-  public FileInfoJsonComparingImpl(
-      FileInfoJsonOldImpl oldImpl,
-      FileInfoJsonNewImpl newImpl,
-      @DiffExecutor ExecutorService executor,
-      Metrics metrics) {
-    this.oldImpl = oldImpl;
-    this.newImpl = newImpl;
-    this.executor = executor;
-    this.metrics = metrics;
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Change change, ObjectId objectId, @Nullable PatchSet base)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    Map<String, FileInfo> result = oldImpl.getFileInfoMap(change, objectId, base);
-    @SuppressWarnings("unused")
-    Future<?> ignored =
-        executor.submit(
-            () -> {
-              try {
-                Map<String, FileInfo> fileInfoNew = newImpl.getFileInfoMap(change, objectId, base);
-                compareAndLogMetrics(
-                    result,
-                    fileInfoNew,
-                    String.format(
-                        "Mismatch comparing old and new diff implementations for change: %s, objectId: %s and base: %s",
-                        change, objectId, base == null ? "none" : base.id()));
-              } catch (ResourceConflictException | PatchListNotAvailableException e) {
-                // If an exception happens while evaluating the new diff, increment the non-matching
-                // counter
-                metrics.diffs.increment(Metrics.Status.ERROR);
-                logger.atWarning().withCause(e).log(
-                    "Error comparing old and new diff implementations.");
-              }
-            });
-    return result;
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Project.NameKey project, ObjectId objectId, int parentNum)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    Map<String, FileInfo> result = oldImpl.getFileInfoMap(project, objectId, parentNum);
-    @SuppressWarnings("unused")
-    Future<?> ignored =
-        executor.submit(
-            () -> {
-              try {
-                Map<String, FileInfo> resultNew =
-                    newImpl.getFileInfoMap(project, objectId, parentNum);
-                compareAndLogMetrics(
-                    result,
-                    resultNew,
-                    String.format(
-                        "Mismatch comparing old and new diff implementations for project: %s, objectId: %s and parentNum: %d",
-                        project, objectId, parentNum));
-              } catch (ResourceConflictException | PatchListNotAvailableException e) {
-                // If an exception happens while evaluating the new diff, increment the non-matching
-                // ctr
-                metrics.diffs.increment(Metrics.Status.ERROR);
-                logger.atWarning().withCause(e).log(
-                    "Error comparing old and new diff implementations.");
-              }
-            });
-    return result;
-  }
-
-  private void compareAndLogMetrics(
-      Map<String, FileInfo> fileInfoMapOld,
-      Map<String, FileInfo> fileInfoMapNew,
-      String warningMessage) {
-    if (fileInfoMapOld.equals(fileInfoMapNew)) {
-      metrics.diffs.increment(Metrics.Status.MATCH);
-      return;
-    }
-    metrics.diffs.increment(Metrics.Status.MISMATCH);
-    logger.atWarning().log(
-        warningMessage
-            + "\n"
-            + "Result using old impl: "
-            + fileInfoMapOld
-            + "\n"
-            + "Result using new impl: "
-            + fileInfoMapNew);
-  }
-}
diff --git a/java/com/google/gerrit/server/change/FileInfoJsonExperimentImpl.java b/java/com/google/gerrit/server/change/FileInfoJsonExperimentImpl.java
deleted file mode 100644
index 81f014d..0000000
--- a/java/com/google/gerrit/server/change/FileInfoJsonExperimentImpl.java
+++ /dev/null
@@ -1,72 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.change;
-
-import com.google.common.annotations.VisibleForTesting;
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.extensions.common.FileInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.server.experiments.ExperimentFeatures;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import java.util.Map;
-import javax.inject.Inject;
-import org.eclipse.jgit.lib.ObjectId;
-
-/**
- * An experimental implementation of FileInfoJson that uses {@link FileInfoJsonNewImpl} if the
- * experiment flag "GerritBackendRequestFeature__use_new_diff_cache" is enabled, or {@link
- * FileInfoJsonOldImpl} otherwise. This would enable a gradual rollout of {@link
- * FileInfoJsonNewImpl}.
- */
-public class FileInfoJsonExperimentImpl implements FileInfoJson {
-  @VisibleForTesting
-  public static final String NEW_DIFF_CACHE_FEATURE =
-      "GerritBackendRequestFeature__use_new_diff_cache";
-
-  private final FileInfoJsonOldImpl oldImpl;
-  private final FileInfoJsonNewImpl newImpl;
-  private final ExperimentFeatures experimentFeatures;
-
-  @Inject
-  public FileInfoJsonExperimentImpl(
-      FileInfoJsonOldImpl oldImpl,
-      FileInfoJsonNewImpl newImpl,
-      ExperimentFeatures experimentFeatures) {
-    this.oldImpl = oldImpl;
-    this.newImpl = newImpl;
-    this.experimentFeatures = experimentFeatures;
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Change change, ObjectId objectId, @Nullable PatchSet base)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    return experimentFeatures.isFeatureEnabled(NEW_DIFF_CACHE_FEATURE)
-        ? newImpl.getFileInfoMap(change, objectId, base)
-        : oldImpl.getFileInfoMap(change, objectId, base);
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Project.NameKey project, ObjectId objectId, int parentNum)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    return experimentFeatures.isFeatureEnabled(NEW_DIFF_CACHE_FEATURE)
-        ? newImpl.getFileInfoMap(project, objectId, parentNum)
-        : oldImpl.getFileInfoMap(project, objectId, parentNum);
-  }
-}
diff --git a/java/com/google/gerrit/server/change/FileInfoJsonNewImpl.java b/java/com/google/gerrit/server/change/FileInfoJsonImpl.java
similarity index 95%
rename from java/com/google/gerrit/server/change/FileInfoJsonNewImpl.java
rename to java/com/google/gerrit/server/change/FileInfoJsonImpl.java
index 7277404..b729c11 100644
--- a/java/com/google/gerrit/server/change/FileInfoJsonNewImpl.java
+++ b/java/com/google/gerrit/server/change/FileInfoJsonImpl.java
@@ -32,12 +32,12 @@
 import org.eclipse.jgit.errors.NoMergeBaseException;
 import org.eclipse.jgit.lib.ObjectId;
 
-/** Implementation of {@link FileInfoJson} using the new diff cache {@link DiffOperations}. */
-public class FileInfoJsonNewImpl implements FileInfoJson {
+/** Implementation of {@link FileInfoJson} using {@link DiffOperations}. */
+public class FileInfoJsonImpl implements FileInfoJson {
   private final DiffOperations diffs;
 
   @Inject
-  FileInfoJsonNewImpl(DiffOperations diffOperations) {
+  FileInfoJsonImpl(DiffOperations diffOperations) {
     this.diffs = diffOperations;
   }
 
diff --git a/java/com/google/gerrit/server/change/FileInfoJsonModule.java b/java/com/google/gerrit/server/change/FileInfoJsonModule.java
index 952b503..b8e05f0 100644
--- a/java/com/google/gerrit/server/change/FileInfoJsonModule.java
+++ b/java/com/google/gerrit/server/change/FileInfoJsonModule.java
@@ -20,7 +20,6 @@
 
   @Override
   public void configure() {
-    // Binding to the experimental implementation to enable gradual rollout of the new diff cache.
-    bind(FileInfoJson.class).to(FileInfoJsonExperimentImpl.class);
+    bind(FileInfoJson.class).to(FileInfoJsonImpl.class);
   }
 }
diff --git a/java/com/google/gerrit/server/change/FileInfoJsonOldImpl.java b/java/com/google/gerrit/server/change/FileInfoJsonOldImpl.java
deleted file mode 100644
index 0570296..0000000
--- a/java/com/google/gerrit/server/change/FileInfoJsonOldImpl.java
+++ /dev/null
@@ -1,128 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.server.change;
-
-import com.google.gerrit.common.Nullable;
-import com.google.gerrit.entities.Change;
-import com.google.gerrit.entities.Patch;
-import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.entities.Project;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.extensions.common.FileInfo;
-import com.google.gerrit.extensions.restapi.ResourceConflictException;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListEntry;
-import com.google.gerrit.server.patch.PatchListKey;
-import com.google.gerrit.server.patch.PatchListNotAvailableException;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-import java.util.Map;
-import java.util.TreeMap;
-import java.util.concurrent.ExecutionException;
-import org.eclipse.jgit.errors.NoMergeBaseException;
-import org.eclipse.jgit.lib.ObjectId;
-
-/** Implementation of {@link FileInfoJson} using the old diff cache {@link PatchListCache}. */
-@Deprecated
-@Singleton
-class FileInfoJsonOldImpl implements FileInfoJson {
-  private final PatchListCache patchListCache;
-
-  @Inject
-  FileInfoJsonOldImpl(PatchListCache patchListCache) {
-    this.patchListCache = patchListCache;
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Change change, ObjectId objectId, @Nullable PatchSet base)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    ObjectId a = base != null ? base.commitId() : null;
-    return toFileInfoMap(change, PatchListKey.againstCommit(a, objectId, Whitespace.IGNORE_NONE));
-  }
-
-  @Override
-  public Map<String, FileInfo> getFileInfoMap(
-      Project.NameKey project, ObjectId objectId, int parentNum)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    PatchListKey key =
-        parentNum == 0
-            ? PatchListKey.againstDefaultBase(objectId, Whitespace.IGNORE_NONE)
-            : PatchListKey.againstParentNum(
-                parentNum, objectId, DiffPreferencesInfo.Whitespace.IGNORE_NONE);
-    return toFileInfoMap(project, key);
-  }
-
-  private Map<String, FileInfo> toFileInfoMap(Change change, PatchListKey key)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    return toFileInfoMap(change.getProject(), key);
-  }
-
-  Map<String, FileInfo> toFileInfoMap(Project.NameKey project, PatchListKey key)
-      throws ResourceConflictException, PatchListNotAvailableException {
-    PatchList list;
-    try {
-      list = patchListCache.get(key, project);
-    } catch (PatchListNotAvailableException e) {
-      Throwable cause = e.getCause();
-      if (cause instanceof ExecutionException) {
-        cause = cause.getCause();
-      }
-      if (cause instanceof NoMergeBaseException) {
-        throw new ResourceConflictException(
-            String.format("Cannot create auto merge commit: %s", e.getMessage()), e);
-      }
-      throw e;
-    }
-
-    Map<String, FileInfo> files = new TreeMap<>();
-    for (PatchListEntry e : list.getPatches()) {
-      FileInfo fileInfo = new FileInfo();
-      fileInfo.status =
-          e.getChangeType() != Patch.ChangeType.MODIFIED ? e.getChangeType().getCode() : null;
-      fileInfo.oldPath = e.getOldName();
-      fileInfo.sizeDelta = e.getSizeDelta();
-      fileInfo.size = e.getSize();
-      if (e.getPatchType() == Patch.PatchType.BINARY) {
-        fileInfo.binary = true;
-      } else {
-        fileInfo.linesInserted = e.getInsertions() > 0 ? e.getInsertions() : null;
-        fileInfo.linesDeleted = e.getDeletions() > 0 ? e.getDeletions() : null;
-      }
-
-      FileInfo o = files.put(e.getNewName(), fileInfo);
-      if (o != null) {
-        // This should only happen on a delete-add break created by JGit
-        // when the file was rewritten and too little content survived. Write
-        // a single record with data from both sides.
-        fileInfo.status = Patch.ChangeType.REWRITE.getCode();
-        fileInfo.sizeDelta = o.sizeDelta;
-        fileInfo.size = o.size;
-        if (o.binary != null && o.binary) {
-          fileInfo.binary = true;
-        }
-        if (o.linesInserted != null) {
-          fileInfo.linesInserted = o.linesInserted;
-        }
-        if (o.linesDeleted != null) {
-          fileInfo.linesDeleted = o.linesDeleted;
-        }
-      }
-    }
-    return files;
-  }
-}
diff --git a/java/com/google/gerrit/server/events/EventsMetrics.java b/java/com/google/gerrit/server/events/EventsMetrics.java
index 3c87cca..6d48c37 100644
--- a/java/com/google/gerrit/server/events/EventsMetrics.java
+++ b/java/com/google/gerrit/server/events/EventsMetrics.java
@@ -32,7 +32,9 @@
         metricMaker.newCounter(
             "events",
             new Description("Triggered events").setRate().setUnit("triggered events"),
-            Field.ofString("type", Metadata.Builder::eventType).build());
+            Field.ofString("type", Metadata.Builder::eventType)
+                .description("The type of the event.")
+                .build());
   }
 
   @Override
diff --git a/java/com/google/gerrit/server/extensions/webui/UiActions.java b/java/com/google/gerrit/server/extensions/webui/UiActions.java
index a7f6b48..34c3c20 100644
--- a/java/com/google/gerrit/server/extensions/webui/UiActions.java
+++ b/java/com/google/gerrit/server/extensions/webui/UiActions.java
@@ -72,7 +72,9 @@
             new com.google.gerrit.metrics.Description("Latency for RestView#getDescription calls")
                 .setCumulative()
                 .setUnit(Units.MILLISECONDS),
-            Field.ofString("view", Metadata.Builder::restViewName).build());
+            Field.ofString("view", Metadata.Builder::restViewName)
+                .description("view implementation class")
+                .build());
   }
 
   public <R extends RestResource> Iterable<UiAction.Description> from(
diff --git a/java/com/google/gerrit/server/git/UploadPackMetricsHook.java b/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
index 4afff2b..1619add 100644
--- a/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
+++ b/java/com/google/gerrit/server/git/UploadPackMetricsHook.java
@@ -45,7 +45,9 @@
   @Inject
   UploadPackMetricsHook(MetricMaker metricMaker) {
     Field<Operation> operationField =
-        Field.ofEnum(Operation.class, "operation", Metadata.Builder::gitOperation).build();
+        Field.ofEnum(Operation.class, "operation", Metadata.Builder::gitOperation)
+            .description("The name of the operation (CLONE, FETCH).")
+            .build();
     requestCount =
         metricMaker.newCounter(
             "git/upload-pack/request_count",
diff --git a/java/com/google/gerrit/server/logging/TraceContext.java b/java/com/google/gerrit/server/logging/TraceContext.java
index e333824..487e0af 100644
--- a/java/com/google/gerrit/server/logging/TraceContext.java
+++ b/java/com/google/gerrit/server/logging/TraceContext.java
@@ -277,6 +277,10 @@
         .findFirst();
   }
 
+  public static Optional<String> getPluginTag() {
+    return getTag(PLUGIN_TAG);
+  }
+
   public static Optional<String> getTag(String tagName) {
     return LoggingContext.getInstance().getTagsAsMap().get(tagName).stream().findFirst();
   }
diff --git a/java/com/google/gerrit/server/notedb/Sequences.java b/java/com/google/gerrit/server/notedb/Sequences.java
index 7a8e28f..7ae98778 100644
--- a/java/com/google/gerrit/server/notedb/Sequences.java
+++ b/java/com/google/gerrit/server/notedb/Sequences.java
@@ -112,8 +112,11 @@
                 .setCumulative()
                 .setUnit(Units.MILLISECONDS),
             Field.ofEnum(SequenceType.class, "sequence", Metadata.Builder::noteDbSequenceType)
+                .description("The sequence from which IDs were retrieved.")
                 .build(),
-            Field.ofBoolean("multiple", Metadata.Builder::multiple).build());
+            Field.ofBoolean("multiple", Metadata.Builder::multiple)
+                .description("Whether more than one ID was retrieved.")
+                .build());
   }
 
   public int nextAccountId() {
diff --git a/java/com/google/gerrit/server/patch/AutoMerger.java b/java/com/google/gerrit/server/patch/AutoMerger.java
index 97910400..2529c04 100644
--- a/java/com/google/gerrit/server/patch/AutoMerger.java
+++ b/java/com/google/gerrit/server/patch/AutoMerger.java
@@ -100,18 +100,22 @@
       MetricMaker metricMaker,
       @GerritServerConfig Config cfg,
       @GerritPersonIdent Provider<PersonIdent> gerritIdentProvider) {
+    Field<OperationType> operationTypeField =
+        Field.ofEnum(OperationType.class, "type", Metadata.Builder::operationName)
+            .description("The type of the operation (CACHE_LOAD, IN_MEMORY_WRITE, ON_DISK_WRITE).")
+            .build();
     this.counter =
         metricMaker.newCounter(
             "git/auto-merge/num_operations",
             new Description("AutoMerge computations").setRate().setUnit("auto merge computations"),
-            Field.ofEnum(OperationType.class, "type", Metadata.Builder::operationName).build());
+            operationTypeField);
     this.latency =
         metricMaker.newTimer(
             "git/auto-merge/latency",
             new Description("AutoMerge computation latency")
                 .setCumulative()
                 .setUnit("milliseconds"),
-            Field.ofEnum(OperationType.class, "type", Metadata.Builder::operationName).build());
+            operationTypeField);
     this.save = cacheAutomerge(cfg);
     this.gerritIdentProvider = gerritIdentProvider;
     this.configuredMergeStrategy = MergeUtil.getMergeStrategy(cfg);
diff --git a/java/com/google/gerrit/server/patch/PatchScriptFactory.java b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
index 6fa14e1..96b23c8 100644
--- a/java/com/google/gerrit/server/patch/PatchScriptFactory.java
+++ b/java/com/google/gerrit/server/patch/PatchScriptFactory.java
@@ -26,21 +26,13 @@
 import com.google.gerrit.entities.Project;
 import com.google.gerrit.exceptions.StorageException;
 import com.google.gerrit.extensions.client.DiffPreferencesInfo;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
 import com.google.gerrit.extensions.restapi.AuthException;
-import com.google.gerrit.metrics.Counter1;
-import com.google.gerrit.metrics.Description;
-import com.google.gerrit.metrics.Field;
-import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.CurrentUser;
 import com.google.gerrit.server.PatchSetUtil;
-import com.google.gerrit.server.change.FileInfoJsonExperimentImpl;
 import com.google.gerrit.server.edit.ChangeEdit;
 import com.google.gerrit.server.edit.ChangeEditUtil;
-import com.google.gerrit.server.experiments.ExperimentFeatures;
 import com.google.gerrit.server.git.GitRepositoryManager;
 import com.google.gerrit.server.git.LargeObjectException;
-import com.google.gerrit.server.logging.Metadata;
 import com.google.gerrit.server.notedb.ChangeNotes;
 import com.google.gerrit.server.patch.PatchScriptBuilder.IntraLineDiffCalculatorResult;
 import com.google.gerrit.server.patch.filediff.FileDiffOutput;
@@ -51,9 +43,7 @@
 import com.google.gerrit.server.project.NoSuchChangeException;
 import com.google.gerrit.server.project.ProjectCache;
 import com.google.gerrit.server.project.ProjectState;
-import com.google.inject.Inject;
 import com.google.inject.Provider;
-import com.google.inject.Singleton;
 import com.google.inject.assistedinject.Assisted;
 import com.google.inject.assistedinject.AssistedInject;
 import java.io.IOException;
@@ -88,27 +78,6 @@
         CurrentUser currentUser);
   }
 
-  /** These metrics are temporary for launching the new redesigned diff cache. */
-  @Singleton
-  static class Metrics {
-    final Counter1<String> diffs;
-    static final String MATCH = "match";
-    static final String MISMATCH = "mismatch";
-    static final String ERROR = "error";
-
-    @Inject
-    Metrics(MetricMaker metricMaker) {
-      diffs =
-          metricMaker.newCounter(
-              "diff/get_diff/dark_launch",
-              new Description(
-                      "Total number of matching, non-matching, or error in diffs in the old and new diff cache implementations.")
-                  .setRate()
-                  .setUnit("count"),
-              Field.ofString("type", Metadata.Builder::eventType).build());
-    }
-  }
-
   private final GitRepositoryManager repoManager;
   private final PatchSetUtil psUtil;
   private final Provider<PatchScriptBuilder> builderFactory;
@@ -130,8 +99,6 @@
 
   private ChangeNotes notes;
 
-  private final boolean runNewDiffCache;
-
   @AssistedInject
   PatchScriptFactory(
       GitRepositoryManager grm,
@@ -139,7 +106,6 @@
       Provider<PatchScriptBuilder> builderFactory,
       PatchListCache patchListCache,
       ChangeEditUtil editReader,
-      ExperimentFeatures experimentFeatures,
       PermissionBackend permissionBackend,
       ProjectCache projectCache,
       DiffOperations diffOperations,
@@ -165,10 +131,6 @@
     this.psb = patchSetB;
     this.diffPrefs = diffPrefs;
     this.currentUser = currentUser;
-
-    this.runNewDiffCache =
-        experimentFeatures.isFeatureEnabled(FileInfoJsonExperimentImpl.NEW_DIFF_CACHE_FEATURE);
-
     changeId = patchSetB.changeId();
   }
 
@@ -179,7 +141,6 @@
       Provider<PatchScriptBuilder> builderFactory,
       PatchListCache patchListCache,
       ChangeEditUtil editReader,
-      ExperimentFeatures experimentFeatures,
       PermissionBackend permissionBackend,
       ProjectCache projectCache,
       DiffOperations diffOperations,
@@ -205,10 +166,6 @@
     this.psb = patchSetB;
     this.diffPrefs = diffPrefs;
     this.currentUser = currentUser;
-
-    this.runNewDiffCache =
-        experimentFeatures.isFeatureEnabled(FileInfoJsonExperimentImpl.NEW_DIFF_CACHE_FEATURE);
-
     changeId = patchSetB.changeId();
     checkArgument(parentNum > 0, "parentNum must be > 0");
   }
@@ -246,11 +203,7 @@
           }
           bId = edit.get().getEditCommit();
         }
-        return runNewDiffCache
-            ? getPatchScriptWithNewDiffCache(git, aId, bId)
-            : getPatchScriptWithOldDiffCache(git, aId, bId);
-      } catch (PatchListNotAvailableException e) {
-        throw new NoSuchChangeException(changeId, e);
+        return getPatchScript(git, aId, bId);
       } catch (DiffNotAvailableException e) {
         throw new StorageException(e);
       } catch (IOException e) {
@@ -268,15 +221,7 @@
     }
   }
 
-  private PatchScript getPatchScriptWithOldDiffCache(Repository git, ObjectId aId, ObjectId bId)
-      throws IOException, PatchListNotAvailableException {
-    PatchScriptBuilder patchScriptBuilder = newBuilder();
-    PatchList list = listFor(keyFor(aId, bId, diffPrefs.ignoreWhitespace));
-    PatchListEntry content = list.get(fileName);
-    return patchScriptBuilder.toPatchScriptOld(git, list, content);
-  }
-
-  private PatchScript getPatchScriptWithNewDiffCache(Repository git, ObjectId aId, ObjectId bId)
+  private PatchScript getPatchScript(Repository git, ObjectId aId, ObjectId bId)
       throws IOException, DiffNotAvailableException {
     FileDiffOutput fileDiffOutput =
         aId == null
@@ -304,17 +249,6 @@
     return Optional.of(getCommitId(psb));
   }
 
-  private PatchListKey keyFor(ObjectId aId, ObjectId bId, Whitespace whitespace) {
-    if (parentNum == 0) {
-      return PatchListKey.againstCommit(aId, bId, whitespace);
-    }
-    return PatchListKey.againstParentNum(parentNum, bId, whitespace);
-  }
-
-  private PatchList listFor(PatchListKey key) throws PatchListNotAvailableException {
-    return patchListCache.get(key, notes.getProjectName());
-  }
-
   private PatchScriptBuilder newBuilder() {
     final PatchScriptBuilder b = builderFactory.get();
     b.setDiffPrefs(diffPrefs);
diff --git a/java/com/google/gerrit/server/plugincontext/PluginContext.java b/java/com/google/gerrit/server/plugincontext/PluginContext.java
index 3c30745..ef2e181 100644
--- a/java/com/google/gerrit/server/plugincontext/PluginContext.java
+++ b/java/com/google/gerrit/server/plugincontext/PluginContext.java
@@ -121,11 +121,17 @@
     @Inject
     PluginMetrics(MetricMaker metricMaker) {
       Field<String> pluginNameField =
-          Field.ofString("plugin_name", Metadata.Builder::pluginName).build();
+          Field.ofString("plugin_name", Metadata.Builder::pluginName)
+              .description("The name of the plugin.")
+              .build();
       Field<String> classNameField =
-          Field.ofString("class_name", Metadata.Builder::className).build();
+          Field.ofString("class_name", Metadata.Builder::className)
+              .description("The class of the plugin that was invoked.")
+              .build();
       Field<String> exportValueField =
-          Field.ofString("export_value", Metadata.Builder::exportValue).build();
+          Field.ofString("export_value", Metadata.Builder::exportValue)
+              .description("The export name under which the invoked class is registered.")
+              .build();
 
       this.latency =
           metricMaker.newTimer(
diff --git a/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 2874a34..de27afa 100644
--- a/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -345,9 +345,16 @@
       refreshCounter =
           metricMaker.newCounter(
               "caches/refresh_count",
-              new Description("count").setRate(),
-              Field.ofString("cache", Metadata.Builder::className).build(),
-              Field.ofBoolean("outdated", Metadata.Builder::outdated).build());
+              new Description(
+                      "The number of refreshes per cache with an indicator if a reload was"
+                          + " necessary.")
+                  .setRate(),
+              Field.ofString("cache", Metadata.Builder::className)
+                  .description("The name of the cache.")
+                  .build(),
+              Field.ofBoolean("outdated", Metadata.Builder::outdated)
+                  .description("Whether the cache entry was outdated on reload.")
+                  .build());
       this.allProjectsName = allProjectsName;
       this.allProjectsConfigProvider = allProjectsConfigProvider;
     }
diff --git a/java/com/google/gerrit/server/restapi/change/PostReview.java b/java/com/google/gerrit/server/restapi/change/PostReview.java
index 94dc21f..5002a82 100644
--- a/java/com/google/gerrit/server/restapi/change/PostReview.java
+++ b/java/com/google/gerrit/server/restapi/change/PostReview.java
@@ -170,7 +170,11 @@
                           + "selected by users while posting a review.")
                   .setRate()
                   .setUnit("count"),
-              Field.ofString("type", Metadata.Builder::eventType).build());
+              Field.ofString("type", Metadata.Builder::eventType)
+                  .description(
+                      "The type of the draft handling option"
+                          + " (KEEP, PUBLISH, PUBLISH_ALL_REVISIONS).")
+                  .build());
     }
   }
 
diff --git a/java/com/google/gerrit/server/update/RetryHelper.java b/java/com/google/gerrit/server/update/RetryHelper.java
index 94becc7..7e6974c 100644
--- a/java/com/google/gerrit/server/update/RetryHelper.java
+++ b/java/com/google/gerrit/server/update/RetryHelper.java
@@ -121,7 +121,9 @@
     @Inject
     Metrics(MetricMaker metricMaker) {
       Field<String> actionTypeField =
-          Field.ofString("action_type", Metadata.Builder::actionType).build();
+          Field.ofString("action_type", Metadata.Builder::actionType)
+              .description("The type of the action that was retried.")
+              .build();
       Field<String> operationNameField =
           Field.ofString("operation_name", Metadata.Builder::operationName)
               .description("The name of the operation that was retried.")
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
index eadd259..58dc0b0 100644
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
+++ b/javatests/com/google/gerrit/acceptance/api/revision/RevisionDiffIT.java
@@ -50,7 +50,6 @@
 import com.google.gerrit.extensions.restapi.BadRequestException;
 import com.google.gerrit.extensions.restapi.BinaryResult;
 import com.google.gerrit.extensions.webui.EditWebLink;
-import com.google.gerrit.server.change.FileInfoJsonExperimentImpl;
 import com.google.gerrit.server.patch.DiffOperations;
 import com.google.gerrit.server.patch.filediff.FileDiffOutput;
 import com.google.inject.Inject;
@@ -93,7 +92,6 @@
   @Inject private ProjectOperations projectOperations;
 
   private boolean intraline;
-  private boolean useNewDiffCache;
 
   private ObjectId initialCommit;
   private ObjectId commit1;
@@ -108,9 +106,6 @@
     baseConfig.setString("cache", "diff_intraline", "timeout", "1 minute");
 
     intraline = baseConfig.getBoolean(TEST_PARAMETER_MARKER, "intraline", false);
-    useNewDiffCache =
-        Arrays.asList(baseConfig.getStringList("experiments", null, "enabled"))
-            .contains(FileInfoJsonExperimentImpl.NEW_DIFF_CACHE_FEATURE);
 
     ObjectId headCommit = testRepo.getRepository().resolve("HEAD");
     initialCommit = headCommit;
@@ -1358,10 +1353,10 @@
   }
 
   @Test
+  @Ignore
   public void renamedUnrelatedFileIsIgnored_forPatchSetDiffWithRebase_whenEquallyModifiedInBoth()
       throws Exception {
     // TODO(ghareeb): fix this test for the new diff cache implementation
-    assume().that(useNewDiffCache).isFalse();
 
     Function<String, String> contentModification =
         fileContent -> fileContent.replace("1st line\n", "First line\n");
@@ -1452,9 +1447,9 @@
   }
 
   @Test
+  @Ignore
   public void filesTouchedByPatchSetsAndContainingOnlyRebaseHunksAreIgnored() throws Exception {
     // TODO(ghareeb): fix this test for the new diff cache implementation
-    assume().that(useNewDiffCache).isFalse();
 
     addModifiedPatchSet(
         changeId, FILE_NAME, fileContent -> fileContent.replace("Line 50\n", "Line fifty\n"));
@@ -2865,13 +2860,11 @@
     DiffInfo diffInfo = gApi.changes().id(cId).current().file(symlink).diff(initialRev);
     assertThat(diffInfo.content).hasSize(1);
     assertThat(diffInfo).content().element(0).linesOfB().containsExactly("new content");
-    // TODO(ghareeb): remove conditional assertion when new diff cache is fully rolled out.
-    assertThat(diffInfo.changeType)
-        .isEqualTo(useNewDiffCache ? ChangeType.REWRITE : ChangeType.ADDED);
+    assertThat(diffInfo.changeType).isEqualTo(ChangeType.REWRITE);
   }
 
   @Test
-  public void renameDeleteByJgit_isIdentifiedAsRewritten6() throws Exception {
+  public void renameDeleteByJgit_isIdentifiedAsRewritten() throws Exception {
     String target = "file.txt";
     String symlink = "link.lnk";
     PushOneCommit push =
@@ -2908,9 +2901,7 @@
             "rename to link.lnk");
     assertThat(diffInfo.content).hasSize(1);
     assertThat(diffInfo).content().element(0).commonLines().containsExactly("content");
-    // TODO(ghareeb): remove conditional assertion when new diff cache is fully rolled out.
-    assertThat(diffInfo.changeType)
-        .isEqualTo(useNewDiffCache ? ChangeType.REWRITE : ChangeType.RENAMED);
+    assertThat(diffInfo.changeType).isEqualTo(ChangeType.REWRITE);
   }
 
   @Test
diff --git a/javatests/com/google/gerrit/acceptance/api/revision/RevisionNewDiffCacheIT.java b/javatests/com/google/gerrit/acceptance/api/revision/RevisionNewDiffCacheIT.java
deleted file mode 100644
index 714bd78..0000000
--- a/javatests/com/google/gerrit/acceptance/api/revision/RevisionNewDiffCacheIT.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (C) 2021 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.api.revision;
-
-import com.google.gerrit.server.change.FileInfoJsonExperimentImpl;
-import com.google.gerrit.testing.ConfigSuite;
-import org.eclipse.jgit.lib.Config;
-
-/**
- * Runs the {@link RevisionDiffIT} with the list files endpoint using the new diff cache. This is
- * temporary until the new diff cache is fully deployed. The new diff cache will become the default
- * in the future.
- */
-public class RevisionNewDiffCacheIT extends RevisionDiffIT {
-  @ConfigSuite.Default
-  public static Config newDiffCacheConfig() {
-    Config config = new Config();
-    config.setString(
-        "experiments", null, "enabled", FileInfoJsonExperimentImpl.NEW_DIFF_CACHE_FEATURE);
-    return config;
-  }
-}
diff --git a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java b/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
deleted file mode 100644
index b23f9a3..0000000
--- a/javatests/com/google/gerrit/acceptance/server/change/PatchListCacheIT.java
+++ /dev/null
@@ -1,326 +0,0 @@
-// Copyright (C) 2014 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package com.google.gerrit.acceptance.server.change;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.gerrit.acceptance.GitUtil.getChangeId;
-import static com.google.gerrit.acceptance.GitUtil.pushHead;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import com.google.common.cache.Cache;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
-import com.google.gerrit.acceptance.AbstractDaemonTest;
-import com.google.gerrit.acceptance.NoHttpd;
-import com.google.gerrit.entities.Patch;
-import com.google.gerrit.entities.Patch.ChangeType;
-import com.google.gerrit.extensions.client.DiffPreferencesInfo.Whitespace;
-import com.google.gerrit.server.patch.IntraLineDiff;
-import com.google.gerrit.server.patch.IntraLineDiffArgs;
-import com.google.gerrit.server.patch.IntraLineDiffKey;
-import com.google.gerrit.server.patch.PatchList;
-import com.google.gerrit.server.patch.PatchListCache;
-import com.google.gerrit.server.patch.PatchListCacheImpl;
-import com.google.gerrit.server.patch.PatchListEntry;
-import com.google.gerrit.server.patch.PatchListKey;
-import com.google.gerrit.server.patch.Text;
-import com.google.inject.Inject;
-import com.google.inject.name.Named;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import org.eclipse.jgit.diff.Edit;
-import org.eclipse.jgit.lib.ObjectId;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.junit.Test;
-
-@NoHttpd
-public class PatchListCacheIT extends AbstractDaemonTest {
-  private static String SUBJECT_1 = "subject 1";
-  private static String SUBJECT_2 = "subject 2";
-  private static String SUBJECT_3 = "subject 3";
-  private static String FILE_A = "a.txt";
-  private static String FILE_B = "b.txt";
-  private static String FILE_C = "c.txt";
-  private static String FILE_D = "d.txt";
-
-  @Inject private PatchListCache patchListCache;
-
-  @Inject
-  @Named("diff")
-  private Cache<PatchListKey, PatchList> abstractPatchListCache;
-
-  @Test
-  public void ensureLegacyBackendIsUsedForFileCacheBackend() throws Exception {
-    Field fileCacheField = patchListCache.getClass().getDeclaredField("fileCache");
-    fileCacheField.setAccessible(true);
-    // Use the reflection to access "localCache" field that is only present in Guava backend.
-    assertThat(
-            Arrays.stream(fileCacheField.get(patchListCache).getClass().getDeclaredFields())
-                .anyMatch(f -> f.getName().equals("localCache")))
-        .isTrue();
-
-    // intraCache (and all other cache backends) should use Caffeine backend.
-    Field intraCacheField = patchListCache.getClass().getDeclaredField("intraCache");
-    intraCacheField.setAccessible(true);
-    assertThat(
-            Arrays.stream(intraCacheField.get(patchListCache).getClass().getDeclaredFields())
-                .noneMatch(f -> f.getName().equals("localCache")))
-        .isTrue();
-  }
-
-  @Test
-  public void listPatchesAgainstBase() throws Exception {
-    commitBuilder().add(FILE_D, "4").message(SUBJECT_1).create();
-    pushHead(testRepo, "refs/heads/master", false);
-
-    // Change 1, 1 (+FILE_A, -FILE_D)
-    RevCommit c =
-        commitBuilder().add(FILE_A, "1").rm(FILE_D).message(SUBJECT_2).insertChangeId().create();
-    String id = getChangeId(testRepo, c).get();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Compare Change 1,1 with Base (+FILE_A, -FILE_D)
-    List<PatchListEntry> entries = getCurrentPatches(id);
-    assertThat(entries).hasSize(3);
-    assertAdded(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_A, entries.get(1));
-    assertDeleted(FILE_D, entries.get(2));
-
-    // Change 1,2 (+FILE_A, +FILE_B, -FILE_D)
-    amendBuilder().add(FILE_B, "2").create();
-    pushHead(testRepo, "refs/for/master", false);
-    entries = getCurrentPatches(id);
-
-    // Compare Change 1,2 with Base (+FILE_A, +FILE_B, -FILE_D)
-    assertThat(entries).hasSize(4);
-    assertAdded(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_A, entries.get(1));
-    assertAdded(FILE_B, entries.get(2));
-    assertDeleted(FILE_D, entries.get(3));
-  }
-
-  @Test
-  public void listPatchesAgainstBaseWithRebase() throws Exception {
-    commitBuilder().add(FILE_D, "4").message(SUBJECT_1).create();
-    pushHead(testRepo, "refs/heads/master", false);
-
-    // Change 1,1 (+FILE_A, -FILE_D)
-    RevCommit c = commitBuilder().add(FILE_A, "1").rm(FILE_D).message(SUBJECT_2).create();
-    String id = getChangeId(testRepo, c).get();
-    pushHead(testRepo, "refs/for/master", false);
-    List<PatchListEntry> entries = getCurrentPatches(id);
-    assertThat(entries).hasSize(3);
-    assertAdded(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_A, entries.get(1));
-    assertDeleted(FILE_D, entries.get(2));
-
-    // Change 2,1 (+FILE_B)
-    testRepo.reset("HEAD~1");
-    commitBuilder().add(FILE_B, "2").message(SUBJECT_3).create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Change 1,2 (+FILE_A, -FILE_D))
-    testRepo.cherryPick(c);
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Compare Change 1,2 with Base (+FILE_A, -FILE_D))
-    entries = getCurrentPatches(id);
-    assertThat(entries).hasSize(3);
-    assertAdded(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_A, entries.get(1));
-    assertDeleted(FILE_D, entries.get(2));
-  }
-
-  @Test
-  public void listPatchesAgainstOtherPatchSet() throws Exception {
-    commitBuilder().add(FILE_D, "4").message(SUBJECT_1).create();
-    pushHead(testRepo, "refs/heads/master", false);
-
-    // Change 1,1 (+FILE_A, +FILE_C, -FILE_D)
-    RevCommit a =
-        commitBuilder().add(FILE_A, "1").add(FILE_C, "3").rm(FILE_D).message(SUBJECT_2).create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Change 1,2 (+FILE_A, +FILE_B, -FILE_D)
-    RevCommit b = amendBuilder().add(FILE_B, "2").rm(FILE_C).create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Compare Change 1,1 with Change 1,2 (+FILE_B, -FILE_C)
-    List<PatchListEntry> entries = getPatches(a, b);
-    assertThat(entries).hasSize(3);
-    assertModified(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_B, entries.get(1));
-    assertDeleted(FILE_C, entries.get(2));
-
-    // Compare Change 1,2 with Change 1,1 (-FILE_B, +FILE_C)
-    List<PatchListEntry> entriesReverse = getPatches(b, a);
-    assertThat(entriesReverse).hasSize(3);
-    assertModified(Patch.COMMIT_MSG, entriesReverse.get(0));
-    assertDeleted(FILE_B, entriesReverse.get(1));
-    assertAdded(FILE_C, entriesReverse.get(2));
-  }
-
-  @Test
-  public void listPatchesAgainstOtherPatchSetWithRebase() throws Exception {
-    commitBuilder().add(FILE_D, "4").message(SUBJECT_1).create();
-    pushHead(testRepo, "refs/heads/master", false);
-
-    // Change 1,1 (+FILE_A, -FILE_D)
-    RevCommit a = commitBuilder().add(FILE_A, "1").rm(FILE_D).message(SUBJECT_2).create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Change 2,1 (+FILE_B)
-    testRepo.reset("HEAD~1");
-    commitBuilder().add(FILE_B, "2").message(SUBJECT_3).create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Change 1,2 (+FILE_A, +FILE_C, -FILE_D)
-    testRepo.cherryPick(a);
-    RevCommit b = amendBuilder().add(FILE_C, "2").create();
-    pushHead(testRepo, "refs/for/master", false);
-
-    // Compare Change 1,1 with Change 1,2 (+FILE_C)
-    List<PatchListEntry> entries = getPatches(a, b);
-    assertThat(entries).hasSize(2);
-    assertModified(Patch.COMMIT_MSG, entries.get(0));
-    assertAdded(FILE_C, entries.get(1));
-
-    // Compare Change 1,2 with Change 1,1 (-FILE_C)
-    List<PatchListEntry> entriesReverse = getPatches(b, a);
-    assertThat(entriesReverse).hasSize(2);
-    assertModified(Patch.COMMIT_MSG, entriesReverse.get(0));
-    assertDeleted(FILE_C, entriesReverse.get(1));
-  }
-
-  @Test
-  public void harmfulMutationsOfEditsAreNotPossibleForPatchListEntry() throws Exception {
-    RevCommit commit =
-        commitBuilder().add("a.txt", "First line\nSecond line\n").message(SUBJECT_1).create();
-    pushHead(testRepo, "refs/heads/master", false);
-
-    PatchListKey diffKey = PatchListKey.againstDefaultBase(commit.copy(), Whitespace.IGNORE_NONE);
-    PatchList patchList = patchListCache.get(diffKey, project);
-
-    PatchListEntry patchListEntry = getEntryFor(patchList, "a.txt");
-    Edit outputEdit = Iterables.getOnlyElement(patchListEntry.getEdits());
-    Edit originalEdit =
-        new Edit(
-            outputEdit.getBeginA(),
-            outputEdit.getEndA(),
-            outputEdit.getBeginB(),
-            outputEdit.getEndB());
-
-    outputEdit.shift(5);
-
-    assertThat(patchListEntry.getEdits()).containsExactly(originalEdit);
-  }
-
-  @Test
-  public void harmfulMutationsOfEditsAreNotPossibleForIntraLineDiffArgsAndCachedValue() {
-    String a = "First line\nSecond line\n";
-    String b = "1st line\n2nd line\n";
-    Text aText = new Text(a.getBytes(UTF_8));
-    Text bText = new Text(b.getBytes(UTF_8));
-    Edit inputEdit = new Edit(0, 2, 0, 2);
-    List<Edit> inputEdits = new ArrayList<>(ImmutableList.of(inputEdit));
-    Set<Edit> inputEditsDueToRebase = new HashSet<>(ImmutableSet.of(inputEdit));
-
-    IntraLineDiffKey diffKey =
-        IntraLineDiffKey.create(ObjectId.zeroId(), ObjectId.zeroId(), Whitespace.IGNORE_NONE);
-    IntraLineDiffArgs diffArgs =
-        IntraLineDiffArgs.create(
-            aText,
-            bText,
-            inputEdits,
-            inputEditsDueToRebase,
-            project,
-            ObjectId.zeroId(),
-            "file.txt");
-    IntraLineDiff intraLineDiff = patchListCache.getIntraLineDiff(diffKey, diffArgs);
-
-    Edit outputEdit = Iterables.getOnlyElement(intraLineDiff.getEdits());
-
-    outputEdit.shift(5);
-    inputEdit.shift(7);
-    inputEdits.add(new Edit(43, 47, 50, 51));
-    inputEditsDueToRebase.add(new Edit(53, 57, 60, 61));
-
-    Edit originalEdit = new Edit(0, 2, 0, 2);
-    assertThat(diffArgs.edits()).containsExactly(originalEdit);
-    assertThat(diffArgs.editsDueToRebase()).containsExactly(originalEdit);
-    assertThat(intraLineDiff.getEdits()).containsExactly(originalEdit);
-  }
-
-  @Test
-  public void largeObjectTombstoneGetsCached() {
-    PatchListKey key = PatchListKey.againstDefaultBase(ObjectId.zeroId(), Whitespace.IGNORE_ALL);
-    PatchListCacheImpl.LargeObjectTombstone tombstone =
-        new PatchListCacheImpl.LargeObjectTombstone();
-    abstractPatchListCache.put(key, tombstone);
-    assertThat(abstractPatchListCache.getIfPresent(key)).isSameInstanceAs(tombstone);
-  }
-
-  private static void assertAdded(String expectedNewName, PatchListEntry e) {
-    assertName(expectedNewName, e);
-    assertThat(e.getChangeType()).isEqualTo(ChangeType.ADDED);
-  }
-
-  private static void assertModified(String expectedNewName, PatchListEntry e) {
-    assertName(expectedNewName, e);
-    assertThat(e.getChangeType()).isEqualTo(ChangeType.MODIFIED);
-  }
-
-  private static void assertDeleted(String expectedNewName, PatchListEntry e) {
-    assertName(expectedNewName, e);
-    assertThat(e.getChangeType()).isEqualTo(ChangeType.DELETED);
-  }
-
-  private static void assertName(String expectedNewName, PatchListEntry e) {
-    assertThat(e.getNewName()).isEqualTo(expectedNewName);
-    assertThat(e.getOldName()).isNull();
-  }
-
-  private List<PatchListEntry> getCurrentPatches(String changeId) throws Exception {
-    return patchListCache.get(getKey(null, getCurrentRevisionId(changeId)), project).getPatches();
-  }
-
-  private List<PatchListEntry> getPatches(ObjectId revisionIdA, ObjectId revisionIdB)
-      throws Exception {
-    return patchListCache.get(getKey(revisionIdA, revisionIdB), project).getPatches();
-  }
-
-  private PatchListKey getKey(ObjectId revisionIdA, ObjectId revisionIdB) {
-    return PatchListKey.againstCommit(revisionIdA, revisionIdB, Whitespace.IGNORE_NONE);
-  }
-
-  private ObjectId getCurrentRevisionId(String changeId) throws Exception {
-    return ObjectId.fromString(gApi.changes().id(changeId).get().currentRevision);
-  }
-
-  private static PatchListEntry getEntryFor(PatchList patchList, String filePath) {
-    Optional<PatchListEntry> patchListEntry =
-        patchList.getPatches().stream()
-            .filter(entry -> entry.getNewName().equals(filePath))
-            .findAny();
-    return patchListEntry.orElseThrow(
-        () -> new IllegalStateException("No PatchListEntry for " + filePath + " exists"));
-  }
-}