You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@phoenix.apache.org by vj...@apache.org on 2023/07/29 01:37:49 UTC

[phoenix] branch 5.1 updated: PHOENIX-6989 Expose server side metrics for DDL operations (#1649)

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

vjasani pushed a commit to branch 5.1
in repository https://gitbox.apache.org/repos/asf/phoenix.git


The following commit(s) were added to refs/heads/5.1 by this push:
     new 7657bb605f PHOENIX-6989 Expose server side metrics for DDL operations (#1649)
7657bb605f is described below

commit 7657bb605f108f09a8ff521357036585511cc7db
Author: Jing Yu <yu...@salesforce.com>
AuthorDate: Fri Jul 28 21:37:43 2023 -0400

    PHOENIX-6989 Expose server side metrics for DDL operations (#1649)
---
 .../apache/phoenix/monitoring/IndexMetricsIT.java  |   6 +-
 .../phoenix/monitoring/MetadataMetricsIT.java      | 154 +++++++++++++++++++++
 .../phoenix/coprocessor/MetaDataEndpointImpl.java  |  66 ++++++++-
 .../index/metrics/MetricsIndexerSourceFactory.java |   2 +-
 .../schema/metrics/MetricsMetadataSource.java      | 122 ++++++++++++++++
 .../metrics/MetricsMetadataSourceFactory.java      |  40 ++++++
 .../schema/metrics/MetricsMetadataSourceImpl.java  | 123 ++++++++++++++++
 7 files changed, 505 insertions(+), 8 deletions(-)

diff --git a/phoenix-core/src/it/java/org/apache/phoenix/monitoring/IndexMetricsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/monitoring/IndexMetricsIT.java
index 36c8dd934d..d6ea8145f6 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/monitoring/IndexMetricsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/monitoring/IndexMetricsIT.java
@@ -191,17 +191,17 @@ public class IndexMetricsIT extends ParallelStatsDisabledIT {
             registry, ageOfUnverifiedRow);
     }
 
-    private void verifyHistogram(String counterName, DynamicMetricsRegistry registry) {
+    public static void verifyHistogram(String counterName, DynamicMetricsRegistry registry) {
         verifyHistogram(counterName, registry, TIME_VAL);
     }
 
-    private void verifyHistogram(String counterName,
+    public static void verifyHistogram(String counterName,
             DynamicMetricsRegistry registry, long max) {
         MutableHistogram histogram = registry.getHistogram(counterName);
         assertEquals(max, histogram.getMax());
     }
 
-    private void verifyCounter(String counterName, DynamicMetricsRegistry registry) {
+    public static void verifyCounter(String counterName, DynamicMetricsRegistry registry) {
         MutableFastCounter counter = registry.getCounter(counterName, 0);
         assertEquals(1, counter.value());
     }
diff --git a/phoenix-core/src/it/java/org/apache/phoenix/monitoring/MetadataMetricsIT.java b/phoenix-core/src/it/java/org/apache/phoenix/monitoring/MetadataMetricsIT.java
new file mode 100644
index 0000000000..62263e1265
--- /dev/null
+++ b/phoenix-core/src/it/java/org/apache/phoenix/monitoring/MetadataMetricsIT.java
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you 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 org.apache.phoenix.monitoring;
+
+import org.apache.hadoop.metrics2.lib.DynamicMetricsRegistry;
+import org.apache.phoenix.end2end.ParallelStatsDisabledIT;
+import org.apache.phoenix.end2end.ParallelStatsDisabledTest;
+import org.apache.phoenix.query.QueryServices;
+import org.apache.phoenix.schema.metrics.MetricsMetadataSource;
+import org.apache.phoenix.schema.metrics.MetricsMetadataSourceImpl;
+import org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
+import org.apache.phoenix.util.ReadOnlyProps;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import java.util.Map;
+
+@Category(ParallelStatsDisabledTest.class)
+public class MetadataMetricsIT extends ParallelStatsDisabledIT {
+
+    @BeforeClass
+    public static void setup() throws Exception {
+        Map<String, String> props = Maps.newHashMapWithExpectedSize(3);
+        props.put(QueryServices.TASK_HANDLING_INITIAL_DELAY_MS_ATTRIB, Long.toString(Long.MAX_VALUE));
+        // disable renewing leases as this will force spooling to happen.
+        props.put(QueryServices.RENEW_LEASE_ENABLED, String.valueOf(false));
+        setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
+    }
+
+    @Test
+    public void testCreateTableMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementCreateTableCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.CREATE_TABLE_COUNT, registry);
+    }
+
+    @Test
+    public void testCreateViewMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementCreateViewCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.CREATE_VIEW_COUNT, registry);
+    }
+
+    @Test
+    public void testCreateIndexMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementCreateIndexCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.CREATE_INDEX_COUNT, registry);
+    }
+
+    @Test
+    public void testCreateSchemaMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementCreateSchemaCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.CREATE_SCHEMA_COUNT, registry);
+    }
+
+    @Test
+    public void testCreateFunctionMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementCreateFunctionCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.CREATE_FUNCTION_COUNT, registry);
+    }
+
+    @Test
+    public void testAlterAddColumnsMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementAlterAddColumnCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.ALTER_ADD_COLUMN_COUNT, registry);
+    }
+
+    @Test
+    public void testAlterDropColumnsMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementAlterDropColumnCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.ALTER_DROP_COLUMN_COUNT, registry);
+    }
+
+    @Test
+    public void testDropTableMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementDropTableCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.DROP_TABLE_COUNT, registry);
+    }
+
+    @Test
+    public void testDropViewMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementDropViewCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.DROP_VIEW_COUNT, registry);
+    }
+
+    @Test
+    public void testDropIndexMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementDropIndexCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.DROP_INDEX_COUNT, registry);
+    }
+
+    @Test
+    public void testDropSchemaMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementDropSchemaCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.DROP_SCHEMA_COUNT, registry);
+    }
+
+    @Test
+    public void testDropFunctionMetrics() {
+        MetricsMetadataSourceImpl metadataSource = new MetricsMetadataSourceImpl();
+        DynamicMetricsRegistry registry = metadataSource.getMetricsRegistry();
+
+        metadataSource.incrementDropFunctionCount();
+        IndexMetricsIT.verifyCounter(MetricsMetadataSource.DROP_FUNCTION_COUNT, registry);
+    }
+}
\ No newline at end of file
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
index b1ccb85571..b4bba44947 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/coprocessor/MetaDataEndpointImpl.java
@@ -220,6 +220,8 @@ import org.apache.phoenix.schema.SequenceKey;
 import org.apache.phoenix.schema.SequenceNotFoundException;
 import org.apache.phoenix.schema.SortOrder;
 import org.apache.phoenix.schema.TableNotFoundException;
+import org.apache.phoenix.schema.metrics.MetricsMetadataSource;
+import org.apache.phoenix.schema.metrics.MetricsMetadataSourceFactory;
 import org.apache.phoenix.schema.task.SystemTaskParams;
 import org.apache.phoenix.schema.task.Task;
 import org.apache.phoenix.schema.types.PBinary;
@@ -556,6 +558,8 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
     // before 4.15, so that we can rollback the upgrade to 4.15 if required
     private boolean allowSplittableSystemCatalogRollback;
 
+    private MetricsMetadataSource metricsSource;
+
     public static void setFailConcurrentMutateAddColumnOneTimeForTesting(boolean fail) {
         failConcurrentMutateAddColumnOneTimeForTesting = fail;
     }
@@ -596,6 +600,7 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
         // Start the phoenix trace collection
         Tracing.addTraceMetricsSource();
         Metrics.ensureConfigured();
+        metricsSource = MetricsMetadataSourceFactory.getMetadataMetricsSource();
     }
 
     @Override
@@ -2181,6 +2186,9 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                     builder.setTable(PTableImpl.toProto(newTable));
                 }
                 done.run(builder.build());
+
+                updateCreateTableDdlSuccessMetrics(tableType);
+                LOGGER.info("{} created successfully, tableName: {}", tableType, fullTableName);
             } finally {
                 ServerUtil.releaseRowLocks(locks);
             }
@@ -2191,6 +2199,16 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
         }
     }
 
+    private void updateCreateTableDdlSuccessMetrics(PTableType tableType) {
+        if (tableType == PTableType.TABLE || tableType == PTableType.SYSTEM) {
+            metricsSource.incrementCreateTableCount();
+        } else if (tableType == PTableType.VIEW) {
+            metricsSource.incrementCreateViewCount();
+        } else if (tableType == PTableType.INDEX) {
+            metricsSource.incrementCreateIndexCount();
+        }
+    }
+
     private long getViewIndexSequenceValue(PhoenixConnection connection, String tenantIdStr, PTable parentTable, PName physicalName) throws SQLException {
         int nSequenceSaltBuckets = connection.getQueryServices().getSequenceSaltBuckets();
 
@@ -2252,6 +2270,7 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
             byte[] tenantIdBytes = rowKeyMetaData[PhoenixDatabaseMetaData.TENANT_ID_INDEX];
             schemaName = rowKeyMetaData[PhoenixDatabaseMetaData.SCHEMA_NAME_INDEX];
             tableOrViewName = rowKeyMetaData[PhoenixDatabaseMetaData.TABLE_NAME_INDEX];
+            String fullTableName = SchemaUtil.getTableName(schemaName, tableOrViewName);
             PTableType pTableType = PTableType.fromSerializedValue(tableType);
             // Disallow deletion of a system table
             if (pTableType == PTableType.SYSTEM) {
@@ -2436,6 +2455,9 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
 
                 done.run(MetaDataMutationResult.toProto(result));
                 dropTableStats = true;
+
+                updateDropTableDdlSuccessMetrics(pTableType);
+                LOGGER.info("{} dropped successfully, tableName: {}", pTableType, fullTableName);
             } finally {
                 ServerUtil.releaseRowLocks(locks);
                 if (dropTableStats) {
@@ -2453,6 +2475,16 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
         }
     }
 
+    private void updateDropTableDdlSuccessMetrics(PTableType pTableType) {
+        if (pTableType == PTableType.TABLE || pTableType == PTableType.SYSTEM) {
+            metricsSource.incrementDropTableCount();
+        } else if (pTableType == PTableType.VIEW) {
+            metricsSource.incrementDropViewCount();
+        } else if (pTableType == PTableType.INDEX) {
+            metricsSource.incrementDropIndexCount();
+        }
+    }
+
     private static class StatsDeleteHandler implements Runnable {
         PTable deletedTable;
         List<byte[]> physicalTableNames;
@@ -3118,6 +3150,12 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                     request.getClientVersion(), parentTable, addingColumns);
             if (result != null) {
                 done.run(MetaDataMutationResult.toProto(result));
+
+                if (result.getMutationCode() == MutationCode.TABLE_ALREADY_EXISTS) {
+                    metricsSource.incrementAlterAddColumnCount();
+                    LOGGER.info("Column(s) added successfully, tableName: {}",
+                            result.getTable().getTableName());
+                }
             }
         } catch (Throwable e) {
             LOGGER.error("Add column failed: ", e);
@@ -3256,6 +3294,12 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                     request.getClientVersion(), parentTable, true);
             if (result != null) {
                 done.run(MetaDataMutationResult.toProto(result));
+
+                if (result.getMutationCode() == MutationCode.TABLE_ALREADY_EXISTS) {
+                    metricsSource.incrementAlterDropColumnCount();
+                    LOGGER.info("Column(s) dropped successfully, tableName: {}",
+                            result.getTable().getTableName());
+                }
             }
         } catch (Throwable e) {
             LOGGER.error("Drop column failed: ", e);
@@ -3324,6 +3368,10 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                 if (result.getMutationCode() != MutationCode.TABLE_ALREADY_EXISTS) {
                     return result;
                 }
+                metricsSource.incrementDropIndexCount();
+                LOGGER.info("INDEX dropped successfully, tableName: {}",
+                        result.getTable().getTableName());
+
                 // there should be no child links to delete since we are just dropping an index
                 if (!childLinksMutations.isEmpty()) {
                     LOGGER.error("Found unexpected child link mutations while dropping an index "
@@ -3985,7 +4033,10 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                 builder.setReturnCode(MetaDataProtos.MutationCode.FUNCTION_NOT_FOUND);
                 builder.setMutationTime(currentTimeStamp);
                 done.run(builder.build());
-                return;
+
+                metricsSource.incrementCreateFunctionCount();
+                LOGGER.info("FUNCTION created successfully, functionName: {}",
+                        Bytes.toString(functionName));
             } finally {
                 ServerUtil.releaseRowLocks(locks);
             }
@@ -4038,7 +4089,10 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                 }
 
                 done.run(MetaDataMutationResult.toProto(result));
-                return;
+
+                metricsSource.incrementDropFunctionCount();
+                LOGGER.info("FUNCTION dropped successfully, functionName: {}",
+                        Bytes.toString(functionName));
             } finally {
                 ServerUtil.releaseRowLocks(locks);
             }
@@ -4153,7 +4207,9 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                 builder.setReturnCode(MetaDataProtos.MutationCode.SCHEMA_NOT_FOUND);
                 builder.setMutationTime(currentTimeStamp);
                 done.run(builder.build());
-                return;
+
+                metricsSource.incrementCreateSchemaCount();
+                LOGGER.info("SCHEMA created successfully, schemaName: {}", schemaName);
             } finally {
                 ServerUtil.releaseRowLocks(locks);
             }
@@ -4197,7 +4253,9 @@ TABLE_FAMILY_BYTES, TABLE_SEQ_NUM_BYTES);
                     metaDataCache.put(ptr, newDeletedSchemaMarker(currentTime));
                 }
                 done.run(MetaDataMutationResult.toProto(result));
-                return;
+
+                metricsSource.incrementDropSchemaCount();
+                LOGGER.info("SCHEMA dropped successfully, schemaName: {}", schemaName);
             } finally {
                 ServerUtil.releaseRowLocks(locks);
             }
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceFactory.java
index b105290afc..baf27f4cf8 100644
--- a/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceFactory.java
+++ b/phoenix-core/src/main/java/org/apache/phoenix/hbase/index/metrics/MetricsIndexerSourceFactory.java
@@ -21,7 +21,7 @@ package org.apache.phoenix.hbase.index.metrics;
  */
 public class MetricsIndexerSourceFactory {
   private static final MetricsIndexerSourceFactory INSTANCE = new MetricsIndexerSourceFactory();
-  private MetricsIndexerSource indexerSource;
+  private volatile MetricsIndexerSource indexerSource;
   private GlobalIndexCheckerSource globalIndexCheckerSource;
 
   private MetricsIndexerSourceFactory() {}
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSource.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSource.java
new file mode 100644
index 0000000000..130b6c46f9
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSource.java
@@ -0,0 +1,122 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.phoenix.schema.metrics;
+
+public interface MetricsMetadataSource {
+    // Metrics2 and JMX constants
+    String METRICS_NAME = "PhoenixMetadata";
+    String METRICS_CONTEXT = "phoenix";
+    String METRICS_DESCRIPTION = "Metrics about the Phoenix MetadataEndpoint";
+    String METRICS_JMX_CONTEXT = "RegionServer,sub=" + METRICS_NAME;
+
+    String CREATE_TABLE_COUNT = "createTableCount";
+    String CREATE_TABLE_COUNT_DESC = "Count of CREATE TABLE DDL statements";
+
+    String CREATE_VIEW_COUNT = "createViewCount";
+    String CREATE_VIEW_COUNT_DESC = "Count of CREATE VIEW DDL statements";
+
+    String CREATE_INDEX_COUNT = "createIndexCount";
+    String CREATE_INDEX_COUNT_DESC = "Count of CREATE INDEX DDL statements";
+
+    String CREATE_SCHEMA_COUNT = "createSchemaCount";
+    String CREATE_SCHEMA_COUNT_DESC = "Count of CREATE SCHEMA DDL statements";
+
+    String CREATE_FUNCTION_COUNT = "createFunctionCount";
+    String CREATE_FUNCTION_COUNT_DESC = "Count of CREATE FUNCTION DDL statements";
+
+    String ALTER_ADD_COLUMN_COUNT = "alterAddColumnCount";
+    String ALTER_ADD_COLUMN_COUNT_DESC = "Count of ALTER statements that add columns";
+
+    String ALTER_DROP_COLUMN_COUNT = "alterDropColumnCount";
+    String ALTER_DROP_COLUMN_COUNT_DESC = "Count of ALTER statements that drop columns";
+
+    String DROP_TABLE_COUNT = "dropTableCount";
+    String DROP_TABLE_COUNT_DESC = "Count of DROP TABLE DDL statements";
+
+    String DROP_VIEW_COUNT = "dropViewCount";
+    String DROP_VIEW_COUNT_DESC = "Count of DROP VIEW DDL statements";
+
+    String DROP_INDEX_COUNT = "dropIndexCount";
+    String DROP_INDEX_COUNT_DESC = "Count of DROP INDEX DDL statements";
+
+    String DROP_SCHEMA_COUNT = "dropSchemaCount";
+    String DROP_SCHEMA_COUNT_DESC = "Count of DROP SCHEMA DDL statements";
+
+    String DROP_FUNCTION_COUNT = "dropFunctionCount";
+    String DROP_FUNCTION_COUNT_DESC = "Count of DROP FUNCTION DDL statements";
+
+    /**
+     * Updates the count of successful CREATE TABLE DDL operations
+     */
+    void incrementCreateTableCount();
+
+    /**
+     * Updates the count of successful CREATE VIEW DDL operations
+     */
+    void incrementCreateViewCount();
+
+    /**
+     * Updates the count of successful CREATE INDEX DDL operations
+     */
+    void incrementCreateIndexCount();
+
+    /**
+     * Updates the count of successful CREATE SCHEMA DDL operations
+     */
+    void incrementCreateSchemaCount();
+
+    /**
+     * Updates the count of successful CREATE FUNCTION DDL operations
+     */
+    void incrementCreateFunctionCount();
+
+    /**
+     * Updates the count of successful ALTER DDL operations that add columns
+     */
+    void incrementAlterAddColumnCount();
+
+    /**
+     * Updates the count of successful ALTER DDL operations that drop columns
+     */
+    void incrementAlterDropColumnCount();
+
+    /**
+     * Updates the count of successful DROP TABLE DDL operations
+     */
+    void incrementDropTableCount();
+
+    /**
+     * Updates the count of successful DROP VIEW DDL operations
+     */
+    void incrementDropViewCount();
+
+    /**
+     * Updates the count of successful DROP INDEX DDL operations
+     */
+    void incrementDropIndexCount();
+
+    /**
+     * Updates the count of successful DROP SCHEMA DDL operations
+     */
+    void incrementDropSchemaCount();
+
+    /**
+     * Updates the count of successful DROP FUNCTION DDL operations
+     */
+    void incrementDropFunctionCount();
+}
\ No newline at end of file
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceFactory.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceFactory.java
new file mode 100644
index 0000000000..d21e36abfd
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceFactory.java
@@ -0,0 +1,40 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.phoenix.schema.metrics;
+
+/**
+ * Factory class for creating {@link MetricsMetadataSource} instances
+ */
+public class MetricsMetadataSourceFactory {
+    private static final MetricsMetadataSourceFactory INSTANCE = new MetricsMetadataSourceFactory();
+
+    private volatile MetricsMetadataSource metricsMetadataSource;
+
+    private MetricsMetadataSourceFactory() {}
+
+    public static MetricsMetadataSourceFactory getInstance() {
+        return INSTANCE;
+    }
+
+    public static synchronized MetricsMetadataSource getMetadataMetricsSource() {
+        if (INSTANCE.metricsMetadataSource == null) {
+            INSTANCE.metricsMetadataSource = new MetricsMetadataSourceImpl();
+        }
+        return INSTANCE.metricsMetadataSource;
+    }
+}
\ No newline at end of file
diff --git a/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceImpl.java b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceImpl.java
new file mode 100644
index 0000000000..33c8ddf527
--- /dev/null
+++ b/phoenix-core/src/main/java/org/apache/phoenix/schema/metrics/MetricsMetadataSourceImpl.java
@@ -0,0 +1,123 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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 org.apache.phoenix.schema.metrics;
+
+import org.apache.hadoop.hbase.metrics.BaseSourceImpl;
+import org.apache.hadoop.metrics2.lib.MutableFastCounter;
+
+public class MetricsMetadataSourceImpl extends BaseSourceImpl implements MetricsMetadataSource {
+
+    private final MutableFastCounter createTableCount;
+    private final MutableFastCounter createViewCount;
+    private final MutableFastCounter createIndexCount;
+    private final MutableFastCounter createSchemaCount;
+    private final MutableFastCounter createFunctionCount;
+
+    private final MutableFastCounter alterAddColumnCount;
+    private final MutableFastCounter alterDropColumnCount;
+
+    private final MutableFastCounter dropTableCount;
+    private final MutableFastCounter dropViewCount;
+    private final MutableFastCounter dropIndexCount;
+    private final MutableFastCounter dropSchemaCount;
+    private final MutableFastCounter dropFunctionCount;
+
+    public MetricsMetadataSourceImpl() {
+        this(METRICS_NAME, METRICS_DESCRIPTION, METRICS_CONTEXT, METRICS_JMX_CONTEXT);
+    }
+
+    public MetricsMetadataSourceImpl(String metricsName, String metricsDescription,
+                                     String metricsContext, String metricsJmxContext) {
+        super(metricsName, metricsDescription, metricsContext, metricsJmxContext);
+
+        createTableCount = getMetricsRegistry().newCounter(CREATE_TABLE_COUNT,
+                CREATE_TABLE_COUNT_DESC, 0L);
+        createViewCount = getMetricsRegistry().newCounter(CREATE_VIEW_COUNT,
+                CREATE_VIEW_COUNT_DESC, 0L);
+        createIndexCount = getMetricsRegistry().newCounter(CREATE_INDEX_COUNT,
+                CREATE_INDEX_COUNT_DESC, 0L);
+        createFunctionCount = getMetricsRegistry().newCounter(CREATE_FUNCTION_COUNT,
+                CREATE_FUNCTION_COUNT_DESC, 0L);
+        createSchemaCount = getMetricsRegistry().newCounter(CREATE_SCHEMA_COUNT,
+                CREATE_SCHEMA_COUNT_DESC, 0L);
+
+        alterAddColumnCount = getMetricsRegistry().newCounter(ALTER_ADD_COLUMN_COUNT,
+                ALTER_ADD_COLUMN_COUNT_DESC, 0L);
+        alterDropColumnCount = getMetricsRegistry().newCounter(ALTER_DROP_COLUMN_COUNT,
+                ALTER_DROP_COLUMN_COUNT_DESC, 0L);
+
+        dropTableCount = getMetricsRegistry().newCounter(DROP_TABLE_COUNT,
+                DROP_TABLE_COUNT_DESC, 0L);
+        dropViewCount = getMetricsRegistry().newCounter(DROP_VIEW_COUNT,
+                DROP_VIEW_COUNT_DESC, 0L);
+        dropIndexCount = getMetricsRegistry().newCounter(DROP_INDEX_COUNT,
+                DROP_INDEX_COUNT_DESC, 0L);
+        dropSchemaCount = getMetricsRegistry().newCounter(DROP_SCHEMA_COUNT,
+                DROP_SCHEMA_COUNT_DESC, 0L);
+        dropFunctionCount = getMetricsRegistry().newCounter(DROP_FUNCTION_COUNT,
+                DROP_FUNCTION_COUNT_DESC, 0L);
+    }
+
+    @Override public void incrementCreateTableCount() {
+        createTableCount.incr();
+    }
+
+    @Override public void incrementCreateViewCount() {
+        createViewCount.incr();
+    }
+
+    @Override public void incrementCreateIndexCount() {
+        createIndexCount.incr();
+    }
+
+    @Override public void incrementCreateSchemaCount() {
+        createSchemaCount.incr();
+    }
+
+    @Override public void incrementCreateFunctionCount() {
+        createFunctionCount.incr();
+    }
+
+    @Override public void incrementAlterAddColumnCount() {
+        alterAddColumnCount.incr();
+    }
+
+    @Override public void incrementAlterDropColumnCount() {
+        alterDropColumnCount.incr();
+    }
+
+    @Override public void incrementDropTableCount() {
+        dropTableCount.incr();
+    }
+
+    @Override public void incrementDropViewCount() {
+        dropViewCount.incr();
+    }
+
+    @Override public void incrementDropIndexCount() {
+        dropIndexCount.incr();
+    }
+
+    @Override public void incrementDropSchemaCount() {
+        dropSchemaCount.incr();
+    }
+
+    @Override public void incrementDropFunctionCount() {
+        dropFunctionCount.incr();
+    }
+}
\ No newline at end of file