You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by tk...@apache.org on 2026/09/16 23:30:25 UTC

(phoenix) 01/02: PHOENIX-7874 Addendum Trim redundant per-batch metric work on the indexed write path

This is an automated email from the ASF dual-hosted git repository.

tkhurana pushed a commit to branch PHOENIX-7562-feature-new
in repository https://gitbox.apache.org/repos/asf/phoenix.git

commit 10d06ad2f605f2ab8348308a44ad346ec23d8d9a
Author: tkhurana <kh...@gmail.com>
AuthorDate: Tue Sep 15 11:40:58 2026 -0700

    PHOENIX-7874 Addendum Trim redundant per-batch metric work on the indexed write path
    
    Two allocation/CPU cleanups that fire on every indexed batch:
    
    - MetricsIndexerSourceImpl: memoize the resolved per-(table, base-metric)
      MetricHistogram/MutableFastCounter handles instead of rebuilding the
      "base.table" name string and re-resolving the registry handle on every
      update. The (base, table) set is small and bounded and the handles are
      stable for the life of the source, so steady-state per-batch allocation at
      getCounterName drops to zero. Metric names, values, and JMX output are
      unchanged.
    
    - IndexRegionObserver: doPre/doPost emitted updatePreIndexUpdateTime /
      updatePostIndexUpdateTime unconditionally, even when doIndexWritesWithExceptions
      short-circuited with no index updates to write -- recording a near-zero sample
      every batch on tables with no pre/post index work. doIndexWritesWithExceptions
      now returns whether it actually wrote, and the callers only record the timing
      metric when it did. Failure metrics are unchanged.
---
 .../index/metrics/MetricsIndexerSourceImpl.java    | 21 +++++++++++++------
 .../phoenix/hbase/index/IndexRegionObserver.java   | 24 ++++++++++++++--------
 2 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/phoenix-core-client/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceImpl.java b/phoenix-core-client/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceImpl.java
index d6fa330310..e3aea4ae6a 100644
--- a/phoenix-core-client/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceImpl.java
+++ b/phoenix-core-client/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceImpl.java
@@ -17,6 +17,7 @@
  */
 package org.apache.phoenix.hbase.index.metrics;
 
+import java.util.concurrent.ConcurrentHashMap;
 import org.apache.hadoop.hbase.metrics.BaseSourceImpl;
 import org.apache.hadoop.metrics2.MetricHistogram;
 import org.apache.hadoop.metrics2.lib.MutableFastCounter;
@@ -49,6 +50,14 @@ public class MetricsIndexerSourceImpl extends BaseSourceImpl implements MetricsI
   private final MutableFastCounter preIndexUpdateFailures;
   private final MutableFastCounter postIndexUpdateFailures;
 
+  // Resolved per-(table, base-metric) handles, memoized so the name concat and registry lookup
+  // run once per pair instead of on every per-batch metric update. Keyed tableName -> baseName ->
+  // handle; the (base, table) set is small and bounded, matching the registry's own retention.
+  private final ConcurrentHashMap<String,
+    ConcurrentHashMap<String, MetricHistogram>> tableHistograms = new ConcurrentHashMap<>();
+  private final ConcurrentHashMap<String,
+    ConcurrentHashMap<String, MutableFastCounter>> tableCounters = new ConcurrentHashMap<>();
+
   public MetricsIndexerSourceImpl() {
     this(METRICS_NAME, METRICS_DESCRIPTION, METRICS_CONTEXT, METRICS_JMX_CONTEXT);
   }
@@ -225,15 +234,15 @@ public class MetricsIndexerSourceImpl extends BaseSourceImpl implements MetricsI
   }
 
   private void incrementTableSpecificCounter(String baseCounterName, String tableName) {
-    MutableFastCounter indexSpecificCounter =
-      getMetricsRegistry().getCounter(getCounterName(baseCounterName, tableName), 0);
-    indexSpecificCounter.incr();
+    tableCounters.computeIfAbsent(tableName, k -> new ConcurrentHashMap<>())
+      .computeIfAbsent(baseCounterName,
+        b -> getMetricsRegistry().getCounter(getCounterName(b, tableName), 0))
+      .incr();
   }
 
   private void incrementTableSpecificHistogram(String baseCounterName, String tableName, long t) {
-    MetricHistogram tableSpecificHistogram =
-      getMetricsRegistry().getHistogram(getCounterName(baseCounterName, tableName));
-    tableSpecificHistogram.add(t);
+    tableHistograms.computeIfAbsent(tableName, k -> new ConcurrentHashMap<>()).computeIfAbsent(
+      baseCounterName, b -> getMetricsRegistry().getHistogram(getCounterName(b, tableName))).add(t);
   }
 
   private String getCounterName(String baseCounterName, String tableName) {
diff --git a/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java b/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
index a762a77467..dd887cc73f 100644
--- a/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
+++ b/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
@@ -3052,9 +3052,12 @@ public class IndexRegionObserver implements RegionCoprocessor, RegionObserver {
         throw new DoNotRetryIOException(
           "Simulating the last (i.e., post) index table write failure");
       }
-      doIndexWritesWithExceptions(context, true);
-      metricSource.updatePostIndexUpdateTime(dataTableName,
-        EnvironmentEdgeManager.currentTimeMillis() - start);
+      // Only record the post-index-update time when there were actually index updates to write;
+      // otherwise every batch with no post index updates emits a near-zero sample.
+      if (doIndexWritesWithExceptions(context, true)) {
+        metricSource.updatePostIndexUpdateTime(dataTableName,
+          EnvironmentEdgeManager.currentTimeMillis() - start);
+      }
     } catch (Throwable e) {
       metricSource.updatePostIndexUpdateFailureTime(dataTableName,
         EnvironmentEdgeManager.currentTimeMillis() - start);
@@ -3063,14 +3066,15 @@ public class IndexRegionObserver implements RegionCoprocessor, RegionObserver {
     }
   }
 
-  private void doIndexWritesWithExceptions(BatchMutateContext context, boolean post)
+  /** Returns true if index updates were actually written, false if there was no work to do. */
+  private boolean doIndexWritesWithExceptions(BatchMutateContext context, boolean post)
     throws IOException {
     ListMultimap<HTableInterfaceReference, Mutation> indexUpdates =
       post ? context.postIndexUpdates : context.preIndexUpdates;
     // short circuit, if we don't need to do any work
 
     if (context == null || indexUpdates == null || indexUpdates.isEmpty()) {
-      return;
+      return false;
     }
 
     // get the current span, or just use a null-span to avoid a bunch of if statements
@@ -3088,6 +3092,7 @@ public class IndexRegionObserver implements RegionCoprocessor, RegionObserver {
         preWriter.write(indexUpdates, false, context.clientVersion);
       }
     }
+    return true;
   }
 
   private void removePendingRows(BatchMutateContext context) {
@@ -3107,9 +3112,12 @@ public class IndexRegionObserver implements RegionCoprocessor, RegionObserver {
         throw new DoNotRetryIOException(
           "Simulating the first (i.e., pre) index table write failure");
       }
-      doIndexWritesWithExceptions(context, false);
-      metricSource.updatePreIndexUpdateTime(dataTableName,
-        EnvironmentEdgeManager.currentTimeMillis() - start);
+      // Only record the pre-index-update time when there were actually index updates to write;
+      // otherwise every batch with no pre index updates emits a near-zero sample.
+      if (doIndexWritesWithExceptions(context, false)) {
+        metricSource.updatePreIndexUpdateTime(dataTableName,
+          EnvironmentEdgeManager.currentTimeMillis() - start);
+      }
     } catch (Throwable e) {
       metricSource.updatePreIndexUpdateFailureTime(dataTableName,
         EnvironmentEdgeManager.currentTimeMillis() - start);