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

fanng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git


The following commit(s) were added to refs/heads/main by this push:
     new d26891f30f [#8820] feat(core): Add statistics event dispatcher (#8846)
d26891f30f is described below

commit d26891f30fc1db6e2b557c914c222f457fbcdbe6
Author: roryqi <[email protected]>
AuthorDate: Fri Oct 24 09:37:50 2025 +0800

    [#8820] feat(core): Add statistics event dispatcher (#8846)
    
    ### What changes were proposed in this pull request?
    
    Add statistics event dispatcher
    
    ### Why are the changes needed?
    
    Fix: #8820
    
    ### Does this PR introduce _any_ user-facing change?
    
    No.
    
    ### How was this patch tested?
    Add UT.
    
    ---------
    
    Co-authored-by: Copilot <[email protected]>
---
 .../java/org/apache/gravitino/GravitinoEnv.java    |  18 +-
 .../listener/StatisticEventDispatcher.java         | 188 +++++++
 .../listener/api/event/OperationType.java          |   8 +
 .../event/stats/DropPartitionStatisticsEvent.java  |  60 +++
 .../stats/DropPartitionStatisticsFailureEvent.java |  62 +++
 .../stats/DropPartitionStatisticsPreEvent.java     |  60 +++
 .../api/event/stats/DropStatisticsEvent.java       |  56 +++
 .../event/stats/DropStatisticsFailureEvent.java    |  58 +++
 .../api/event/stats/DropStatisticsPreEvent.java    |  57 +++
 .../event/stats/ListPartitionStatisticsEvent.java  |  57 +++
 .../stats/ListPartitionStatisticsFailureEvent.java |  58 +++
 .../stats/ListPartitionStatisticsPreEvent.java     |  57 +++
 .../api/event/stats/ListStatisticsEvent.java       |  43 ++
 .../event/stats/ListStatisticsFailureEvent.java    |  44 ++
 .../api/event/stats/ListStatisticsPreEvent.java    |  42 ++
 .../listener/api/event/stats/StatisticsEvent.java  |  44 ++
 .../api/event/stats/StatisticsFailureEvent.java    |  38 ++
 .../api/event/stats/StatisticsPreEvent.java        |  37 ++
 .../stats/UpdatePartitionStatisticsEvent.java      |  60 +++
 .../UpdatePartitionStatisticsFailureEvent.java     |  62 +++
 .../stats/UpdatePartitionStatisticsPreEvent.java   |  58 +++
 .../api/event/stats/UpdateStatisticsEvent.java     |  58 +++
 .../event/stats/UpdateStatisticsFailureEvent.java  |  61 +++
 .../api/event/stats/UpdateStatisticsPreEvent.java  |  58 +++
 .../gravitino/stats/StatisticDispatcher.java       |  95 ++++
 .../apache/gravitino/stats/StatisticManager.java   |  12 +-
 .../api/event/TestStatisticsEventDispatcher.java   | 543 +++++++++++++++++++++
 docs/gravitino-server-config.md                    |   2 +
 .../apache/gravitino/server/GravitinoServer.java   |   4 +-
 .../server/web/rest/StatisticOperations.java       |  20 +-
 .../server/web/rest/TestStatisticOperations.java   |   3 +-
 31 files changed, 1999 insertions(+), 24 deletions(-)

diff --git a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java 
b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
index d8746dcb2d..1e01333b15 100644
--- a/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
+++ b/core/src/main/java/org/apache/gravitino/GravitinoEnv.java
@@ -70,6 +70,7 @@ import org.apache.gravitino.listener.MetalakeEventDispatcher;
 import org.apache.gravitino.listener.ModelEventDispatcher;
 import org.apache.gravitino.listener.PartitionEventDispatcher;
 import org.apache.gravitino.listener.SchemaEventDispatcher;
+import org.apache.gravitino.listener.StatisticEventDispatcher;
 import org.apache.gravitino.listener.TableEventDispatcher;
 import org.apache.gravitino.listener.TagEventDispatcher;
 import org.apache.gravitino.listener.TopicEventDispatcher;
@@ -81,6 +82,7 @@ import org.apache.gravitino.metrics.MetricsSystem;
 import org.apache.gravitino.metrics.source.JVMMetricsSource;
 import org.apache.gravitino.policy.PolicyDispatcher;
 import org.apache.gravitino.policy.PolicyManager;
+import org.apache.gravitino.stats.StatisticDispatcher;
 import org.apache.gravitino.stats.StatisticManager;
 import org.apache.gravitino.storage.IdGenerator;
 import org.apache.gravitino.storage.RandomIdGenerator;
@@ -148,7 +150,7 @@ public class GravitinoEnv {
   private OwnerDispatcher ownerDispatcher;
   private FutureGrantManager futureGrantManager;
   private GravitinoAuthorizer gravitinoAuthorizer;
-  private StatisticManager statisticManager;
+  private StatisticDispatcher statisticDispatcher;
 
   protected GravitinoEnv() {}
 
@@ -416,8 +418,8 @@ public class GravitinoEnv {
     return jobOperationDispatcher;
   }
 
-  public StatisticManager statisticManager() {
-    return statisticManager;
+  public StatisticDispatcher statisticDispatcher() {
+    return statisticDispatcher;
   }
 
   public void start() {
@@ -473,11 +475,11 @@ public class GravitinoEnv {
       }
     }
 
-    if (statisticManager != null) {
+    if (statisticDispatcher != null) {
       try {
-        statisticManager.close();
+        statisticDispatcher.close();
       } catch (Exception e) {
-        LOG.warn("Failed to close StatisticManager", e);
+        LOG.warn("Failed to close StatisticDispatcher", e);
       }
     }
 
@@ -572,7 +574,9 @@ public class GravitinoEnv {
     ModelNormalizeDispatcher modelNormalizeDispatcher =
         new ModelNormalizeDispatcher(modelHookDispatcher, catalogManager);
     this.modelDispatcher = new ModelEventDispatcher(eventBus, 
modelNormalizeDispatcher);
-    this.statisticManager = new StatisticManager(entityStore, idGenerator, 
config);
+    this.statisticDispatcher =
+        new StatisticEventDispatcher(
+            eventBus, new StatisticManager(entityStore, idGenerator, config));
 
     // Create and initialize access control related modules
     boolean enableAuthorization = config.get(Configs.ENABLE_AUTHORIZATION);
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/StatisticEventDispatcher.java
 
b/core/src/main/java/org/apache/gravitino/listener/StatisticEventDispatcher.java
new file mode 100644
index 0000000000..7be50a9498
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/StatisticEventDispatcher.java
@@ -0,0 +1,188 @@
+/*
+ *  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.gravitino.listener;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.NameIdentifier;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsFailureEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.DropStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.DropStatisticsPreEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListPartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListPartitionStatisticsFailureEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListPartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.ListStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.ListStatisticsPreEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsFailureEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.UpdateStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdateStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.UpdateStatisticsPreEvent;
+import org.apache.gravitino.stats.PartitionRange;
+import org.apache.gravitino.stats.PartitionStatistics;
+import org.apache.gravitino.stats.PartitionStatisticsDrop;
+import org.apache.gravitino.stats.PartitionStatisticsUpdate;
+import org.apache.gravitino.stats.Statistic;
+import org.apache.gravitino.stats.StatisticDispatcher;
+import org.apache.gravitino.stats.StatisticValue;
+import org.apache.gravitino.utils.MetadataObjectUtil;
+import org.apache.gravitino.utils.PrincipalUtils;
+
+/**
+ * StatisticEventManager is a decorator for StatisticDispatcher that 
dispatches events to an
+ * EventBus.
+ */
+public class StatisticEventDispatcher implements StatisticDispatcher {
+  private final EventBus eventBus;
+  private final StatisticDispatcher dispatcher;
+
+  public StatisticEventDispatcher(EventBus eventBus, StatisticDispatcher 
dispatcher) {
+    this.dispatcher = dispatcher;
+    this.eventBus = eventBus;
+  }
+
+  @Override
+  public List<Statistic> listStatistics(String metalake, MetadataObject 
metadataObject) {
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new ListStatisticsPreEvent(user, identifier));
+
+    try {
+      List<Statistic> statistics = dispatcher.listStatistics(metalake, 
metadataObject);
+      eventBus.dispatchEvent(new ListStatisticsEvent(user, identifier));
+      return statistics;
+    } catch (Exception e) {
+      eventBus.dispatchEvent(new ListStatisticsFailureEvent(user, identifier, 
e));
+      throw e;
+    }
+  }
+
+  @Override
+  public void updateStatistics(
+      String metalake, MetadataObject metadataObject, Map<String, 
StatisticValue<?>> statistics) {
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new UpdateStatisticsPreEvent(user, identifier, 
statistics));
+
+    try {
+      dispatcher.updateStatistics(metalake, metadataObject, statistics);
+      eventBus.dispatchEvent(new UpdateStatisticsEvent(user, identifier, 
statistics));
+    } catch (Exception e) {
+      eventBus.dispatchEvent(new UpdateStatisticsFailureEvent(user, 
identifier, e, statistics));
+      throw e;
+    }
+  }
+
+  @Override
+  public boolean dropStatistics(
+      String metalake, MetadataObject metadataObject, List<String> statistics) 
{
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new DropStatisticsPreEvent(user, identifier, 
statistics));
+
+    try {
+      boolean result = dispatcher.dropStatistics(metalake, metadataObject, 
statistics);
+      eventBus.dispatchEvent(new DropStatisticsEvent(user, identifier, 
statistics));
+      return result;
+    } catch (Exception e) {
+      eventBus.dispatchEvent(new DropStatisticsFailureEvent(user, identifier, 
e, statistics));
+      throw e;
+    }
+  }
+
+  @Override
+  public boolean dropPartitionStatistics(
+      String metalake,
+      MetadataObject metadataObject,
+      List<PartitionStatisticsDrop> partitionStatistics) {
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(
+        new DropPartitionStatisticsPreEvent(user, identifier, 
partitionStatistics));
+
+    try {
+      boolean result =
+          dispatcher.dropPartitionStatistics(metalake, metadataObject, 
partitionStatistics);
+      eventBus.dispatchEvent(
+          new DropPartitionStatisticsEvent(user, identifier, 
partitionStatistics));
+      return result;
+    } catch (Exception e) {
+      eventBus.dispatchEvent(
+          new DropPartitionStatisticsFailureEvent(user, identifier, e, 
partitionStatistics));
+      throw e;
+    }
+  }
+
+  @Override
+  public void updatePartitionStatistics(
+      String metalake,
+      MetadataObject metadataObject,
+      List<PartitionStatisticsUpdate> partitionStatistics) {
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(
+        new UpdatePartitionStatisticsPreEvent(user, identifier, 
partitionStatistics));
+
+    try {
+      dispatcher.updatePartitionStatistics(metalake, metadataObject, 
partitionStatistics);
+      eventBus.dispatchEvent(
+          new UpdatePartitionStatisticsEvent(user, identifier, 
partitionStatistics));
+    } catch (Exception e) {
+      eventBus.dispatchEvent(
+          new UpdatePartitionStatisticsFailureEvent(user, identifier, e, 
partitionStatistics));
+      throw e;
+    }
+  }
+
+  @Override
+  public List<PartitionStatistics> listPartitionStatistics(
+      String metalake, MetadataObject metadataObject, PartitionRange range) {
+    NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new ListPartitionStatisticsPreEvent(user, 
identifier, range));
+
+    try {
+      List<PartitionStatistics> partitionStatistics =
+          dispatcher.listPartitionStatistics(metalake, metadataObject, range);
+      eventBus.dispatchEvent(new ListPartitionStatisticsEvent(user, 
identifier, range));
+      return partitionStatistics;
+    } catch (Exception e) {
+      eventBus.dispatchEvent(new ListPartitionStatisticsFailureEvent(user, 
identifier, e, range));
+      throw e;
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    dispatcher.close();
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java 
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
index 9354beae47..f6247815f0 100644
--- 
a/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/OperationType.java
@@ -160,5 +160,13 @@ public enum OperationType {
   GET_JOB,
   CANCEL_JOB,
 
+  // Statistics operations
+  LIST_STATISTICS,
+  LIST_PARTITION_STATISTICS,
+  DROP_STATISTICS,
+  DROP_PARTITION_STATISTICS,
+  UPDATE_STATISTICS,
+  UPDATE_PARTITION_STATISTICS,
+
   UNKNOWN,
 }
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsEvent.java
new file mode 100644
index 0000000000..ff89b78c86
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsEvent.java
@@ -0,0 +1,60 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsDrop;
+
+/** Event representing the dropping of partition statistics. */
+@DeveloperApi
+public class DropPartitionStatisticsEvent extends StatisticsEvent {
+  private final List<PartitionStatisticsDrop> partitionStatisticsDrops;
+
+  /**
+   * Creates a new DropPartitionStatisticsEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param partitionStatisticsDrops the list of partition statistics drops
+   */
+  public DropPartitionStatisticsEvent(
+      String user,
+      NameIdentifier identifier,
+      List<PartitionStatisticsDrop> partitionStatisticsDrops) {
+    super(user, identifier);
+    this.partitionStatisticsDrops = partitionStatisticsDrops;
+  }
+
+  /**
+   * Gets the partition statistics drops associated with this event.
+   *
+   * @return the partition statistics drops
+   */
+  public List<PartitionStatisticsDrop> partitionStatisticsDrops() {
+    return partitionStatisticsDrops;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsFailureEvent.java
new file mode 100644
index 0000000000..90fcb27e4f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsFailureEvent.java
@@ -0,0 +1,62 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsDrop;
+
+/** Event fired when there is a failure in dropping partition statistics. */
+@DeveloperApi
+public class DropPartitionStatisticsFailureEvent extends 
StatisticsFailureEvent {
+  private final List<PartitionStatisticsDrop> partitionStatisticsDrops;
+
+  /**
+   * Constructor for creating a drop partition statistics failure event.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param cause the exception that caused the failure
+   * @param partitionStatisticsDrops the partition statistics drops to be 
applied
+   */
+  public DropPartitionStatisticsFailureEvent(
+      String user,
+      NameIdentifier identifier,
+      Exception cause,
+      List<PartitionStatisticsDrop> partitionStatisticsDrops) {
+    super(user, identifier, cause);
+    this.partitionStatisticsDrops = partitionStatisticsDrops;
+  }
+
+  /**
+   * Gets the partition statistics drops associated with this event.
+   *
+   * @return the partition statistics drops
+   */
+  public List<PartitionStatisticsDrop> partitionStatisticsDrops() {
+    return partitionStatisticsDrops;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsPreEvent.java
new file mode 100644
index 0000000000..014ef7cf24
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropPartitionStatisticsPreEvent.java
@@ -0,0 +1,60 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsDrop;
+
+/** Event fired before dropping partition statistics. */
+@DeveloperApi
+public class DropPartitionStatisticsPreEvent extends StatisticsPreEvent {
+  private final List<PartitionStatisticsDrop> partitionStatisticsDrops;
+
+  /**
+   * Constructor for creating a drop partition statistics pre-event.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param partitionStatisticsDrops the partition statistics drops to be 
applied
+   */
+  public DropPartitionStatisticsPreEvent(
+      String user,
+      NameIdentifier identifier,
+      List<PartitionStatisticsDrop> partitionStatisticsDrops) {
+    super(user, identifier);
+    this.partitionStatisticsDrops = partitionStatisticsDrops;
+  }
+
+  /**
+   * Gets the partition statistics drops associated with this event.
+   *
+   * @return the partition statistics drops
+   */
+  public List<PartitionStatisticsDrop> partitionStatisticsDrops() {
+    return partitionStatisticsDrops;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsEvent.java
new file mode 100644
index 0000000000..a7c422ab7f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsEvent.java
@@ -0,0 +1,56 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired after dropping statistics on a metadata object. */
+@DeveloperApi
+public class DropStatisticsEvent extends StatisticsEvent {
+
+  private final List<String> statisticNames;
+  /**
+   * Constructor for DropStatisticsEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param statisticNames the names of the statistics that were dropped
+   */
+  public DropStatisticsEvent(String user, NameIdentifier identifier, 
List<String> statisticNames) {
+    super(user, identifier);
+    this.statisticNames = statisticNames;
+  }
+
+  /**
+   * Gets the names of the statistics that were dropped.
+   *
+   * @return list of statistic names
+   */
+  public List<String> statisticNames() {
+    return statisticNames;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsFailureEvent.java
new file mode 100644
index 0000000000..025f31cb31
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsFailureEvent.java
@@ -0,0 +1,58 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired when dropping metadata object statistics fails. */
+@DeveloperApi
+public class DropStatisticsFailureEvent extends StatisticsFailureEvent {
+  private final List<String> statisticNames;
+
+  /**
+   * Constructor for DropStatisticsFailureEvent.
+   *
+   * @param user the name of the user who initiated the drop statistics 
operation
+   * @param identifier the identifier of the metadata object whose statistics 
were to be dropped
+   * @param exception the exception that was thrown during the drop statistics 
operation
+   * @param statisticNames the list of statistic names that were attempted to 
be dropped
+   */
+  public DropStatisticsFailureEvent(
+      String user, NameIdentifier identifier, Exception exception, 
List<String> statisticNames) {
+    super(user, identifier, exception);
+    this.statisticNames = statisticNames;
+  }
+
+  /**
+   * Gets the names of the statistics that were attempted to be dropped.
+   *
+   * @return list of statistic names
+   */
+  public List<String> statisticNames() {
+    return statisticNames;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsPreEvent.java
new file mode 100644
index 0000000000..d813029afa
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/DropStatisticsPreEvent.java
@@ -0,0 +1,57 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired before dropping statistics of a metadata object. */
+@DeveloperApi
+public class DropStatisticsPreEvent extends StatisticsPreEvent {
+  private final List<String> statisticNames;
+
+  /**
+   * Creates a new DropStatisticsPreEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param statisticNames the names of the statistics to be dropped
+   */
+  public DropStatisticsPreEvent(
+      String user, NameIdentifier identifier, List<String> statisticNames) {
+    super(user, identifier);
+    this.statisticNames = statisticNames;
+  }
+
+  /**
+   * Gets the names of the statistics that are to be dropped.
+   *
+   * @return list of statistic names
+   */
+  public List<String> statisticNames() {
+    return statisticNames;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.DROP_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsEvent.java
new file mode 100644
index 0000000000..2bb24c8a2a
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsEvent.java
@@ -0,0 +1,57 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionRange;
+
+/** Event fired when listing partition statistics. */
+@DeveloperApi
+public class ListPartitionStatisticsEvent extends StatisticsEvent {
+  private final PartitionRange partitionRange;
+
+  /**
+   * Creates a new ListPartitionStatisticsEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param partitionRange the partition range for which statistics are being 
listed
+   */
+  public ListPartitionStatisticsEvent(
+      String user, NameIdentifier identifier, PartitionRange partitionRange) {
+    super(user, identifier);
+    this.partitionRange = partitionRange;
+  }
+
+  /**
+   * Gets the partition range for which statistics are being listed.
+   *
+   * @return the partition range
+   */
+  public PartitionRange partitionRange() {
+    return partitionRange;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsFailureEvent.java
new file mode 100644
index 0000000000..9d99747d18
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsFailureEvent.java
@@ -0,0 +1,58 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionRange;
+
+/** Event fired when listing partition statistics fails. */
+@DeveloperApi
+public class ListPartitionStatisticsFailureEvent extends 
StatisticsFailureEvent {
+  private final PartitionRange partitionRange;
+
+  /**
+   * Constructor for ListPartitionStatisticsFailureEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param exception the exception that occurred
+   * @param partitionRange the partition range for which statistics were being 
listed
+   */
+  public ListPartitionStatisticsFailureEvent(
+      String user, NameIdentifier identifier, Exception exception, 
PartitionRange partitionRange) {
+    super(user, identifier, exception);
+    this.partitionRange = partitionRange;
+  }
+
+  /**
+   * Gets the partition range for which statistics were being listed.
+   *
+   * @return the partition range
+   */
+  public PartitionRange partitionRange() {
+    return partitionRange;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsPreEvent.java
new file mode 100644
index 0000000000..8580d81e1a
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListPartitionStatisticsPreEvent.java
@@ -0,0 +1,57 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionRange;
+
+/** Event fired before listing partition statistics. */
+@DeveloperApi
+public class ListPartitionStatisticsPreEvent extends StatisticsPreEvent {
+  private final PartitionRange partitionRange;
+
+  /**
+   * Constructor for ListPartitionStatisticsPreEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param partitionRange the partition range for which statistics are to be 
listed
+   */
+  public ListPartitionStatisticsPreEvent(
+      String user, NameIdentifier identifier, PartitionRange partitionRange) {
+    super(user, identifier);
+    this.partitionRange = partitionRange;
+  }
+
+  /**
+   * Gets the partition range for which statistics are to be listed.
+   *
+   * @return the partition range
+   */
+  public PartitionRange partitionRange() {
+    return partitionRange;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsEvent.java
new file mode 100644
index 0000000000..d2fa4ee436
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsEvent.java
@@ -0,0 +1,43 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired when listing statistics. */
+@DeveloperApi
+public class ListStatisticsEvent extends StatisticsEvent {
+
+  /**
+   * Constructor for creating a list statistics event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   */
+  public ListStatisticsEvent(String user, NameIdentifier identifier) {
+    super(user, identifier);
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsFailureEvent.java
new file mode 100644
index 0000000000..5894745222
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsFailureEvent.java
@@ -0,0 +1,44 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired when listing statistics fails. */
+@DeveloperApi
+public class ListStatisticsFailureEvent extends StatisticsFailureEvent {
+
+  /**
+   * Constructor for creating a list statistics failure event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   * @param cause the exception that caused the failure
+   */
+  public ListStatisticsFailureEvent(String user, NameIdentifier identifier, 
Exception cause) {
+    super(user, identifier, cause);
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsPreEvent.java
new file mode 100644
index 0000000000..ea86a0d461
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/ListStatisticsPreEvent.java
@@ -0,0 +1,42 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+
+/** Event fired before listing statistics. */
+@DeveloperApi
+public class ListStatisticsPreEvent extends StatisticsPreEvent {
+
+  /**
+   * Constructor for creating a list statistics pre-event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   */
+  public ListStatisticsPreEvent(String user, 
org.apache.gravitino.NameIdentifier identifier) {
+    super(user, identifier);
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsEvent.java
new file mode 100644
index 0000000000..c4f8c3ed3d
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsEvent.java
@@ -0,0 +1,44 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.Event;
+import org.apache.gravitino.listener.api.event.OperationStatus;
+
+/** Base class for all statistics-related events. */
+@DeveloperApi
+public abstract class StatisticsEvent extends Event {
+
+  /**
+   * Constructor for creating a statistics event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   */
+  protected StatisticsEvent(String user, NameIdentifier identifier) {
+    super(user, identifier);
+  }
+
+  @Override
+  public OperationStatus operationStatus() {
+    return OperationStatus.SUCCESS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsFailureEvent.java
new file mode 100644
index 0000000000..c6d9cd95d8
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsFailureEvent.java
@@ -0,0 +1,38 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.FailureEvent;
+
+/** Base class for all statistics-related failure events. */
+@DeveloperApi
+public abstract class StatisticsFailureEvent extends FailureEvent {
+  /**
+   * Constructor for creating a statistics failure event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   * @param cause the exception that caused the failure
+   */
+  protected StatisticsFailureEvent(String user, NameIdentifier identifier, 
Exception cause) {
+    super(user, identifier, cause);
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsPreEvent.java
new file mode 100644
index 0000000000..86af95612e
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/StatisticsPreEvent.java
@@ -0,0 +1,37 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.PreEvent;
+
+/** Base class for all statistics-related pre-events. */
+@DeveloperApi
+public abstract class StatisticsPreEvent extends PreEvent {
+  /**
+   * Constructor for creating a statistics pre-event.
+   *
+   * @param user the user initiating the event
+   * @param identifier the name identifier associated with the event.
+   */
+  protected StatisticsPreEvent(String user, NameIdentifier identifier) {
+    super(user, identifier);
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsEvent.java
new file mode 100644
index 0000000000..71a52100b5
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsEvent.java
@@ -0,0 +1,60 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsUpdate;
+
+/** Event fired when updating partition statistics. */
+@DeveloperApi
+public class UpdatePartitionStatisticsEvent extends StatisticsEvent {
+  private final List<PartitionStatisticsUpdate> partitionStatisticsUpdates;
+
+  /**
+   * Constructor for UpdatePartitionStatisticsEvent.
+   *
+   * @param user
+   * @param identifier
+   * @param partitionStatisticsUpdates
+   */
+  public UpdatePartitionStatisticsEvent(
+      String user,
+      NameIdentifier identifier,
+      List<PartitionStatisticsUpdate> partitionStatisticsUpdates) {
+    super(user, identifier);
+    this.partitionStatisticsUpdates = partitionStatisticsUpdates;
+  }
+
+  /**
+   * Gets the partition statistics updates.
+   *
+   * @return the partition statistics updates
+   */
+  public List<PartitionStatisticsUpdate> partitionStatisticsUpdates() {
+    return partitionStatisticsUpdates;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsFailureEvent.java
new file mode 100644
index 0000000000..502f755b5c
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsFailureEvent.java
@@ -0,0 +1,62 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsUpdate;
+
+/** Event fired when updating partition statistics fails. */
+@DeveloperApi
+public class UpdatePartitionStatisticsFailureEvent extends 
StatisticsFailureEvent {
+  private final List<PartitionStatisticsUpdate> partitionStatisticsUpdates;
+
+  /**
+   * Constructor for UpdatePartitionStatisticsFailureEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param cause the exception that occurred
+   * @param partitionStatisticsUpdates the partition statistics updates that 
were being applied
+   */
+  public UpdatePartitionStatisticsFailureEvent(
+      String user,
+      NameIdentifier identifier,
+      Exception cause,
+      List<PartitionStatisticsUpdate> partitionStatisticsUpdates) {
+    super(user, identifier, cause);
+    this.partitionStatisticsUpdates = partitionStatisticsUpdates;
+  }
+
+  /**
+   * Gets the partition statistics updates.
+   *
+   * @return the partition statistics updates
+   */
+  public List<PartitionStatisticsUpdate> partitionStatisticsUpdates() {
+    return partitionStatisticsUpdates;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsPreEvent.java
new file mode 100644
index 0000000000..479400f1b0
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdatePartitionStatisticsPreEvent.java
@@ -0,0 +1,58 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.List;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.PartitionStatisticsUpdate;
+
+/** Event fired before updating partition statistics. */
+@DeveloperApi
+public class UpdatePartitionStatisticsPreEvent extends StatisticsPreEvent {
+  private final List<PartitionStatisticsUpdate> partitionStatisticsUpdates;
+
+  /**
+   * Constructor for UpdatePartitionStatisticsPreEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the table
+   * @param updates the partition statistics updates to be applied
+   */
+  public UpdatePartitionStatisticsPreEvent(
+      String user, NameIdentifier identifier, List<PartitionStatisticsUpdate> 
updates) {
+    super(user, identifier);
+    this.partitionStatisticsUpdates = updates;
+  }
+
+  /**
+   * Gets the partition statistics updates.
+   *
+   * @return the partition statistics updates
+   */
+  public List<PartitionStatisticsUpdate> partitionStatisticsUpdates() {
+    return partitionStatisticsUpdates;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_PARTITION_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsEvent.java
new file mode 100644
index 0000000000..7918525581
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsEvent.java
@@ -0,0 +1,58 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.Map;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.StatisticValue;
+
+/** Event fired when updating statistics. */
+@DeveloperApi
+public class UpdateStatisticsEvent extends StatisticsEvent {
+  private final Map<String, StatisticValue<?>> statistics;
+
+  /**
+   * Constructor for UpdateStatisticsEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param statistics the statistics to be updated
+   */
+  public UpdateStatisticsEvent(
+      String user, NameIdentifier identifier, Map<String, StatisticValue<?>> 
statistics) {
+    super(user, identifier);
+    this.statistics = statistics;
+  }
+
+  /**
+   * Gets the statistics to be updated.
+   *
+   * @return map of statistic names to their values
+   */
+  public Map<String, StatisticValue<?>> statistics() {
+    return statistics;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsFailureEvent.java
new file mode 100644
index 0000000000..2962aff89f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsFailureEvent.java
@@ -0,0 +1,61 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.Map;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.StatisticValue;
+
+/** Event triggered when there is a failure in updating statistics. */
+@DeveloperApi
+public class UpdateStatisticsFailureEvent extends StatisticsFailureEvent {
+  private final Map<String, StatisticValue<?>> statistics;
+  /**
+   * Constructor for UpdateStatisticsFailureEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param cause the exception that occurred
+   * @param statistics the statistics that were being updated
+   */
+  public UpdateStatisticsFailureEvent(
+      String user,
+      NameIdentifier identifier,
+      Exception cause,
+      Map<String, StatisticValue<?>> statistics) {
+    super(user, identifier, cause);
+    this.statistics = statistics;
+  }
+
+  /**
+   * Gets the statistics that were being updated.
+   *
+   * @return map of statistic names to their values
+   */
+  public Map<String, StatisticValue<?>> statistics() {
+    return statistics;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsPreEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsPreEvent.java
new file mode 100644
index 0000000000..0a7ad6539b
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/stats/UpdateStatisticsPreEvent.java
@@ -0,0 +1,58 @@
+/*
+ *  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.gravitino.listener.api.event.stats;
+
+import java.util.Map;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.event.OperationType;
+import org.apache.gravitino.stats.StatisticValue;
+
+/** Event fired before updating statistics. */
+@DeveloperApi
+public class UpdateStatisticsPreEvent extends StatisticsPreEvent {
+  private Map<String, StatisticValue<?>> statistics;
+
+  /**
+   * Constructor for UpdateStatisticsPreEvent.
+   *
+   * @param user the user performing the operation
+   * @param identifier the identifier of the metadata object
+   * @param statistics the statistics to be updated
+   */
+  public UpdateStatisticsPreEvent(
+      String user, NameIdentifier identifier, Map<String, StatisticValue<?>> 
statistics) {
+    super(user, identifier);
+    this.statistics = statistics;
+  }
+
+  /**
+   * Gets the statistics to be updated.
+   *
+   * @return map of statistic names to their values
+   */
+  public Map<String, StatisticValue<?>> statistics() {
+    return statistics;
+  }
+
+  @Override
+  public OperationType operationType() {
+    return OperationType.UPDATE_STATISTICS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/stats/StatisticDispatcher.java 
b/core/src/main/java/org/apache/gravitino/stats/StatisticDispatcher.java
new file mode 100644
index 0000000000..1675ed41f2
--- /dev/null
+++ b/core/src/main/java/org/apache/gravitino/stats/StatisticDispatcher.java
@@ -0,0 +1,95 @@
+/*
+ * 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.gravitino.stats;
+
+import java.io.Closeable;
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.exceptions.UnmodifiableStatisticException;
+
+/** The StatisticDispatcher interface provides methods to manage statistics 
for metadata objects */
+public interface StatisticDispatcher extends Closeable {
+
+  /**
+   * List statistics for a given metadata object in a metalake.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @return List of statistics
+   */
+  List<Statistic> listStatistics(String metalake, MetadataObject 
metadataObject);
+
+  /**
+   * Update statistics for a given metadata object in a metalake.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @param statistics the statistics to update
+   */
+  void updateStatistics(
+      String metalake, MetadataObject metadataObject, Map<String, 
StatisticValue<?>> statistics);
+
+  /**
+   * Drop statistics for a given metadata object in a metalake.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @param statistics the statistics to drop
+   * @return true if statistics were dropped, false otherwise
+   * @throws UnmodifiableStatisticException if any of the statistics cannot be 
modified
+   */
+  boolean dropStatistics(String metalake, MetadataObject metadataObject, 
List<String> statistics);
+
+  /**
+   * Drop partition statistics for a given metadata object in a metalake.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @param partitionStatistics the partition statistics to drop
+   * @return true if partition statistics were dropped, false otherwise
+   */
+  boolean dropPartitionStatistics(
+      String metalake,
+      MetadataObject metadataObject,
+      List<PartitionStatisticsDrop> partitionStatistics);
+
+  /**
+   * Update partition statistics for a given metadata object in a metalake.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @param partitionStatistics the partition statistics to update
+   */
+  void updatePartitionStatistics(
+      String metalake,
+      MetadataObject metadataObject,
+      List<PartitionStatisticsUpdate> partitionStatistics);
+
+  /**
+   * List partition statistics for a given metadata object in a metalake 
within a specified range.
+   *
+   * @param metalake the name of the metalake
+   * @param metadataObject the metadata object
+   * @param range the partition range
+   * @return List of partition statistics
+   */
+  List<PartitionStatistics> listPartitionStatistics(
+      String metalake, MetadataObject metadataObject, PartitionRange range);
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/stats/StatisticManager.java 
b/core/src/main/java/org/apache/gravitino/stats/StatisticManager.java
index 8ed1e87e87..fc10c1a5f0 100644
--- a/core/src/main/java/org/apache/gravitino/stats/StatisticManager.java
+++ b/core/src/main/java/org/apache/gravitino/stats/StatisticManager.java
@@ -38,7 +38,6 @@ import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
 import org.apache.gravitino.exceptions.NoSuchEntityException;
 import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
-import org.apache.gravitino.exceptions.UnmodifiableStatisticException;
 import org.apache.gravitino.lock.LockType;
 import org.apache.gravitino.lock.TreeLockUtils;
 import org.apache.gravitino.meta.AuditInfo;
@@ -56,7 +55,7 @@ import org.apache.gravitino.utils.PrincipalUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-public class StatisticManager implements Closeable {
+public class StatisticManager implements Closeable, StatisticDispatcher {
 
   private static final String OPTIONS_PREFIX = 
"gravitino.stats.partition.storageOption.";
   private static final Logger LOG = 
LoggerFactory.getLogger(StatisticManager.class);
@@ -86,6 +85,7 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public List<Statistic> listStatistics(String metalake, MetadataObject 
metadataObject) {
     try {
       NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
@@ -123,6 +123,7 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public void updateStatistics(
       String metalake, MetadataObject metadataObject, Map<String, 
StatisticValue<?>> statistics) {
     try {
@@ -171,9 +172,9 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public boolean dropStatistics(
-      String metalake, MetadataObject metadataObject, List<String> statistics)
-      throws UnmodifiableStatisticException {
+      String metalake, MetadataObject metadataObject, List<String> statistics) 
{
     try {
       NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake, 
metadataObject);
       Entity.EntityType type = 
StatisticEntity.getStatisticType(metadataObject.type());
@@ -208,6 +209,7 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public boolean dropPartitionStatistics(
       String metalake,
       MetadataObject metadataObject,
@@ -232,6 +234,7 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public void updatePartitionStatistics(
       String metalake,
       MetadataObject metadataObject,
@@ -260,6 +263,7 @@ public class StatisticManager implements Closeable {
     }
   }
 
+  @Override
   public List<PartitionStatistics> listPartitionStatistics(
       String metalake, MetadataObject metadataObject, PartitionRange range) {
     try {
diff --git 
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestStatisticsEventDispatcher.java
 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestStatisticsEventDispatcher.java
new file mode 100644
index 0000000000..6bf4004f87
--- /dev/null
+++ 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestStatisticsEventDispatcher.java
@@ -0,0 +1,543 @@
+/*
+ * 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.gravitino.listener.api.event;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import java.util.Collections;
+import java.util.Map;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.exceptions.GravitinoRuntimeException;
+import org.apache.gravitino.listener.DummyEventListener;
+import org.apache.gravitino.listener.EventBus;
+import org.apache.gravitino.listener.StatisticEventDispatcher;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsFailureEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropPartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.DropStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.DropStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.DropStatisticsPreEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListPartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListPartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.ListStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.ListStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.ListStatisticsPreEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsFailureEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdatePartitionStatisticsPreEvent;
+import org.apache.gravitino.listener.api.event.stats.UpdateStatisticsEvent;
+import 
org.apache.gravitino.listener.api.event.stats.UpdateStatisticsFailureEvent;
+import org.apache.gravitino.listener.api.event.stats.UpdateStatisticsPreEvent;
+import org.apache.gravitino.stats.PartitionRange;
+import org.apache.gravitino.stats.PartitionStatisticsDrop;
+import org.apache.gravitino.stats.PartitionStatisticsModification;
+import org.apache.gravitino.stats.PartitionStatisticsUpdate;
+import org.apache.gravitino.stats.StatisticDispatcher;
+import org.apache.gravitino.stats.StatisticValue;
+import org.apache.gravitino.stats.StatisticValues;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestInstance;
+
+@TestInstance(TestInstance.Lifecycle.PER_CLASS)
+public class TestStatisticsEventDispatcher {
+  private StatisticEventDispatcher dispatcher;
+  private StatisticEventDispatcher failureDispatcher;
+
+  private DummyEventListener dummyEventListener;
+
+  @BeforeAll
+  public void setup() {
+    this.dummyEventListener = new DummyEventListener();
+    EventBus eventBus = new 
EventBus(Collections.singletonList(dummyEventListener));
+    StatisticDispatcher statisticDispatcher = mockStatisticDispatcher();
+    this.dispatcher = new StatisticEventDispatcher(eventBus, 
statisticDispatcher);
+    StatisticDispatcher statisticExceptionDispatcher = 
mockExceptionStatisticDispatcher();
+    this.failureDispatcher = new StatisticEventDispatcher(eventBus, 
statisticExceptionDispatcher);
+  }
+
+  @Test
+  public void testListStatisticsEvent() {
+    dispatcher.listStatistics(
+        "metalake",
+        MetadataObjects.of(
+            Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE));
+
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(ListStatisticsPreEvent.class, preEvent.getClass());
+    Assertions.assertEquals(OperationType.LIST_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(ListStatisticsEvent.class, event.getClass());
+    Assertions.assertEquals(OperationType.LIST_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+  }
+
+  @Test
+  public void testListStatisticsFailureEvent() {
+    Assertions.assertThrows(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.listStatistics(
+                "metalake",
+                MetadataObjects.of(
+                    Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE)));
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(OperationType.LIST_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    Assertions.assertEquals(ListStatisticsFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((ListStatisticsFailureEvent) event).exception().getClass());
+  }
+
+  @Test
+  public void testListPartitionStatisticsEvent() {
+    dispatcher.listPartitionStatistics(
+        "metalake",
+        MetadataObjects.of(Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+        PartitionRange.upTo("p1", PartitionRange.BoundType.CLOSED));
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(ListPartitionStatisticsPreEvent.class, 
preEvent.getClass());
+    Assertions.assertEquals(OperationType.LIST_PARTITION_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(ListPartitionStatisticsEvent.class, 
event.getClass());
+    Assertions.assertEquals(OperationType.LIST_PARTITION_STATISTICS, 
event.operationType());
+  }
+
+  @Test
+  public void testUpdateStatisticsEvent() {
+    Map<String, StatisticValue<?>> stats = Maps.newHashMap();
+    stats.put("stat1", StatisticValues.longValue(100L));
+
+    dispatcher.updateStatistics(
+        "metalake",
+        MetadataObjects.of(Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+        stats);
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(UpdateStatisticsPreEvent.class, 
preEvent.getClass());
+    Assertions.assertEquals(OperationType.UPDATE_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+    Assertions.assertEquals(
+        100L, ((UpdateStatisticsPreEvent) 
preEvent).statistics().get("stat1").value());
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(UpdateStatisticsEvent.class, event.getClass());
+    Assertions.assertEquals(OperationType.UPDATE_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+    Assertions.assertEquals(
+        100L, ((UpdateStatisticsEvent) 
event).statistics().get("stat1").value());
+  }
+
+  @Test
+  public void testUpdateStatisticsFailureEvent() {
+    Map<String, StatisticValue<?>> stats = Maps.newHashMap();
+    stats.put("stat1", StatisticValues.longValue(100L));
+
+    Assertions.assertThrows(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.updateStatistics(
+                "metalake",
+                MetadataObjects.of(
+                    Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+                stats));
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(OperationType.UPDATE_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    Assertions.assertEquals(UpdateStatisticsFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((UpdateStatisticsFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(
+        100L, ((UpdateStatisticsFailureEvent) 
event).statistics().get("stat1").value());
+  }
+
+  @Test
+  public void testUpdatePartitionStatisticsEvent() {
+    Map<String, StatisticValue<?>> stats = Maps.newHashMap();
+    stats.put("stat1", StatisticValues.longValue(100L));
+
+    PartitionStatisticsUpdate update = 
PartitionStatisticsModification.update("p1", stats);
+    dispatcher.updatePartitionStatistics(
+        "metalake",
+        MetadataObjects.of(Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+        Lists.newArrayList(update));
+
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(UpdatePartitionStatisticsPreEvent.class, 
preEvent.getClass());
+    Assertions.assertEquals(OperationType.UPDATE_PARTITION_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+    Assertions.assertEquals(
+        1, ((UpdatePartitionStatisticsPreEvent) 
preEvent).partitionStatisticsUpdates().size());
+    Assertions.assertEquals(
+        "p1",
+        ((UpdatePartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .partitionName());
+    Assertions.assertEquals(
+        100L,
+        ((UpdatePartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .statistics()
+            .get("stat1")
+            .value());
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(UpdatePartitionStatisticsEvent.class, 
event.getClass());
+    Assertions.assertEquals(OperationType.UPDATE_PARTITION_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+    Assertions.assertEquals(
+        1, ((UpdatePartitionStatisticsEvent) 
event).partitionStatisticsUpdates().size());
+    Assertions.assertEquals(
+        "p1",
+        ((UpdatePartitionStatisticsEvent) event)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .partitionName());
+    Assertions.assertEquals(
+        100L,
+        ((UpdatePartitionStatisticsEvent) event)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .statistics()
+            .get("stat1")
+            .value());
+  }
+
+  @Test
+  public void testUpdatePartitionStatisticsFailureEvent() {
+    Map<String, StatisticValue<?>> stats = Maps.newHashMap();
+    stats.put("stat1", StatisticValues.longValue(100L));
+
+    PartitionStatisticsUpdate update = 
PartitionStatisticsModification.update("p1", stats);
+    Assertions.assertThrows(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.updatePartitionStatistics(
+                "metalake",
+                MetadataObjects.of(
+                    Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+                Lists.newArrayList(update)));
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(OperationType.UPDATE_PARTITION_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    Assertions.assertEquals(UpdatePartitionStatisticsFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((UpdatePartitionStatisticsFailureEvent) 
event).exception().getClass());
+    Assertions.assertEquals(
+        1, ((UpdatePartitionStatisticsFailureEvent) 
event).partitionStatisticsUpdates().size());
+    Assertions.assertEquals(
+        "p1",
+        ((UpdatePartitionStatisticsFailureEvent) event)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .partitionName());
+    Assertions.assertEquals(
+        100L,
+        ((UpdatePartitionStatisticsFailureEvent) event)
+            .partitionStatisticsUpdates()
+            .get(0)
+            .statistics()
+            .get("stat1")
+            .value());
+  }
+
+  @Test
+  public void testDropStatisticsEvent() {
+    dispatcher.dropStatistics(
+        "metalake",
+        MetadataObjects.of(Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+        Lists.newArrayList("stat1", "stat2"));
+
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(DropStatisticsPreEvent.class, preEvent.getClass());
+    Assertions.assertEquals(OperationType.DROP_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+    Assertions.assertEquals(2, ((DropStatisticsPreEvent) 
preEvent).statisticNames().size());
+    Assertions.assertTrue(((DropStatisticsPreEvent) 
preEvent).statisticNames().contains("stat1"));
+    Assertions.assertTrue(((DropStatisticsPreEvent) 
preEvent).statisticNames().contains("stat2"));
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(DropStatisticsEvent.class, event.getClass());
+    Assertions.assertEquals(OperationType.DROP_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+    Assertions.assertEquals(2, ((DropStatisticsEvent) 
event).statisticNames().size());
+    Assertions.assertTrue(((DropStatisticsEvent) 
event).statisticNames().contains("stat1"));
+    Assertions.assertTrue(((DropStatisticsEvent) 
event).statisticNames().contains("stat2"));
+  }
+
+  @Test
+  public void testDropStatisticsFailureEvent() {
+    Assertions.assertThrows(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.dropStatistics(
+                "metalake",
+                MetadataObjects.of(
+                    Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+                Lists.newArrayList("stat1", "stat2")));
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(OperationType.DROP_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    Assertions.assertEquals(DropStatisticsFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(2, ((DropStatisticsFailureEvent) 
event).statisticNames().size());
+    Assertions.assertTrue(((DropStatisticsFailureEvent) 
event).statisticNames().contains("stat1"));
+    Assertions.assertTrue(((DropStatisticsFailureEvent) 
event).statisticNames().contains("stat2"));
+  }
+
+  @Test
+  public void testDropPartitionStatisticsEvent() {
+    PartitionStatisticsDrop drop1 =
+        PartitionStatisticsModification.drop("p1", Lists.newArrayList("stat1", 
"stat2"));
+    PartitionStatisticsDrop drop2 =
+        PartitionStatisticsModification.drop("p2", 
Lists.newArrayList("stat3"));
+
+    dispatcher.dropPartitionStatistics(
+        "metalake",
+        MetadataObjects.of(Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+        Lists.newArrayList(drop1, drop2));
+
+    PreEvent preEvent = dummyEventListener.popPreEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
preEvent.identifier());
+    Assertions.assertEquals(DropPartitionStatisticsPreEvent.class, 
preEvent.getClass());
+    Assertions.assertEquals(OperationType.DROP_PARTITION_STATISTICS, 
preEvent.operationType());
+    Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
+    Assertions.assertEquals(
+        2, ((DropPartitionStatisticsPreEvent) 
preEvent).partitionStatisticsDrops().size());
+    Assertions.assertEquals(
+        "p1",
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(0)
+            .partitionName());
+    Assertions.assertEquals(
+        "p2",
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(1)
+            .partitionName());
+    Assertions.assertEquals(
+        2,
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        1,
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(1)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        "stat1",
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(0));
+    Assertions.assertEquals(
+        "stat2",
+        ((DropPartitionStatisticsPreEvent) preEvent)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(1));
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(DropPartitionStatisticsEvent.class, 
event.getClass());
+    Assertions.assertEquals(OperationType.DROP_PARTITION_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.SUCCESS, event.operationStatus());
+    Assertions.assertEquals(
+        2, ((DropPartitionStatisticsEvent) 
event).partitionStatisticsDrops().size());
+    Assertions.assertEquals(
+        "p1",
+        ((DropPartitionStatisticsEvent) 
event).partitionStatisticsDrops().get(0).partitionName());
+    Assertions.assertEquals(
+        "p2",
+        ((DropPartitionStatisticsEvent) 
event).partitionStatisticsDrops().get(1).partitionName());
+    Assertions.assertEquals(
+        2,
+        ((DropPartitionStatisticsEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        1,
+        ((DropPartitionStatisticsEvent) event)
+            .partitionStatisticsDrops()
+            .get(1)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        "stat1",
+        ((DropPartitionStatisticsEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(0));
+    Assertions.assertEquals(
+        "stat2",
+        ((DropPartitionStatisticsEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(1));
+  }
+
+  @Test
+  public void testDropPartitionStatisticsFailureEvent() {
+    PartitionStatisticsDrop drop1 =
+        PartitionStatisticsModification.drop("p1", Lists.newArrayList("stat1", 
"stat2"));
+    PartitionStatisticsDrop drop2 =
+        PartitionStatisticsModification.drop("p2", 
Lists.newArrayList("stat3"));
+
+    Assertions.assertThrows(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.dropPartitionStatistics(
+                "metalake",
+                MetadataObjects.of(
+                    Lists.newArrayList("catalog", "db", "table"), 
MetadataObject.Type.TABLE),
+                Lists.newArrayList(drop1, drop2)));
+
+    Event event = dummyEventListener.popPostEvent();
+    Assertions.assertEquals(
+        NameIdentifier.of("metalake", "catalog", "db", "table"), 
event.identifier());
+    Assertions.assertEquals(OperationType.DROP_PARTITION_STATISTICS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    Assertions.assertEquals(DropPartitionStatisticsFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        2, ((DropPartitionStatisticsFailureEvent) 
event).partitionStatisticsDrops().size());
+    Assertions.assertEquals(
+        "p1",
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .partitionName());
+    Assertions.assertEquals(
+        "p2",
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(1)
+            .partitionName());
+    Assertions.assertEquals(
+        2,
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        1,
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(1)
+            .statisticNames()
+            .size());
+    Assertions.assertEquals(
+        "stat1",
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(0));
+    Assertions.assertEquals(
+        "stat2",
+        ((DropPartitionStatisticsFailureEvent) event)
+            .partitionStatisticsDrops()
+            .get(0)
+            .statisticNames()
+            .get(1));
+  }
+
+  @AfterAll
+  public void tearDown() throws Exception {
+    dummyEventListener.clear();
+  }
+
+  private StatisticDispatcher mockExceptionStatisticDispatcher() {
+    return mock(
+        StatisticDispatcher.class,
+        invocation -> {
+          throw new GravitinoRuntimeException("Exception for all methods");
+        });
+  }
+
+  private StatisticDispatcher mockStatisticDispatcher() {
+    StatisticDispatcher dispatcher = mock(StatisticDispatcher.class);
+    when(dispatcher.dropPartitionStatistics(any(), any(), 
any())).thenReturn(true);
+    when(dispatcher.dropStatistics(any(), any(), any())).thenReturn(true);
+    when(dispatcher.listPartitionStatistics(any(), any(), any()))
+        .thenReturn(Collections.emptyList());
+    when(dispatcher.listStatistics(any(), 
any())).thenReturn(Collections.emptyList());
+
+    return dispatcher;
+  }
+}
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 3da93fda7d..6f597e72b6 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -189,6 +189,7 @@ Gravitino triggers a pre-event before the operation, a 
post-event after the comp
 | owner operation                         | `SetOwnerEvent`, `GetOwnerEvent`   
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 | Gravitino server job template operation | `RegisterJobTemplateEvent`, 
`GetJobTemplateEvent`, `ListJobTemplatesEvent`, `AlterJobTemplateEvent`, 
`DeleteJobTemplateEvent`, `RegisterJobTemplateFailureEvent`, 
`GetJobTemplateFailureEvent`, `ListJobTemplatesFailureEvent`, 
`AlterJobTemplateFailureEvent`, `DeleteJobTemplateFailureEvent`                 
                                                                                
                                                                 [...]
 | Gravitino server job operation          | `RunJobEvent`, `GetJobEvent`, 
`ListJobsEvent`, `CancelJobEvent`, `RunJobFailureEvent`, `GetJobFailureEvent`, 
`ListJobsFailureEvent`, `CancelJobFailureEvent`                                 
                                                                                
                                                                                
                                                                                
                    [...]
+| Gravitino server statistics operation   | `ListStatisticsEvent`, 
`UpdateStatisticsEvent`, `DropStatisticsEvent`, `ListPartitionStatisticsEvent`, 
`UpdatePartitionStatisticsEvent`, `DropPartitionStatisticsEvent`, 
`ListStatisticsFailureEvent`, `UpdateStatisticsFailureEvent`, 
`DropStatisticsFailureEvent`, `ListPartitionStatisticsFailureEvent`, 
`UpdatePartitionStatisticsFailureEvent`, `DropPartitionStatisticsFailureEvent`  
                                                                     [...]
 
 ##### Pre-event
 
@@ -209,6 +210,7 @@ Gravitino triggers a pre-event before the operation, a 
post-event after the comp
 | Gravitino server owner operation        | `SetOwnerPreEvent`, 
`GetOwnerPreEvent`                                                              
                                                                                
                                                                                
                                                       | 1.0.0            |
 | Gravitino server job template operation | `RegisterJobTemplatePreEvent`, 
`GetJobTemplatePreEvent`, `ListJobTemplatesPreEvent`, 
`AlterJobTemplatePreEvent`, `DeleteJobTemplatePreEvent`                         
                                                                                
                                                                      | 1.0.1   
         |
 | Gravitino server job operation          | `RunJobPreEvent`, 
`GetJobPreEvent`, `ListJobsPreEvent`, `CancelJobPreEvent`                       
                                                                                
                                                                                
                                                         | 1.0.1            |
+| Gravitino server statistics operation   | `ListStatisticsPreEvent`, 
`UpdateStatisticsPreEvent`, `DropStatisticsPreEvent`, 
`ListPartitionStatisticsPreEvent`, `UpdatePartitionStatisticsPreEvent`, 
`DropPartitionStatisticsPreEvent`                                               
                                                                                
   | 1.1.0            |
 
 #### Event listener plugin
 
diff --git 
a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java 
b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
index 48e22e0490..f86e35e2e4 100644
--- a/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
+++ b/server/src/main/java/org/apache/gravitino/server/GravitinoServer.java
@@ -56,7 +56,7 @@ import 
org.apache.gravitino.server.web.mapper.JsonMappingExceptionMapper;
 import org.apache.gravitino.server.web.mapper.JsonParseExceptionMapper;
 import org.apache.gravitino.server.web.mapper.JsonProcessingExceptionMapper;
 import org.apache.gravitino.server.web.ui.WebUIFilter;
-import org.apache.gravitino.stats.StatisticManager;
+import org.apache.gravitino.stats.StatisticDispatcher;
 import org.apache.gravitino.tag.TagDispatcher;
 import org.glassfish.hk2.api.InterceptionService;
 import org.glassfish.hk2.utilities.binding.AbstractBinder;
@@ -148,7 +148,7 @@ public class GravitinoServer extends ResourceConfig {
             
bind(gravitinoEnv.modelDispatcher()).to(ModelDispatcher.class).ranked(1);
             bind(lineageService).to(LineageDispatcher.class).ranked(1);
             
bind(gravitinoEnv.jobOperationDispatcher()).to(JobOperationDispatcher.class).ranked(1);
-            
bind(gravitinoEnv.statisticManager()).to(StatisticManager.class).ranked(1);
+            
bind(gravitinoEnv.statisticDispatcher()).to(StatisticDispatcher.class).ranked(1);
           }
         });
     register(JsonProcessingExceptionMapper.class);
diff --git 
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
 
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
index a7ef188bfb..9e9dbdbe65 100644
--- 
a/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
+++ 
b/server/src/main/java/org/apache/gravitino/server/web/rest/StatisticOperations.java
@@ -63,7 +63,7 @@ import org.apache.gravitino.stats.PartitionRange;
 import org.apache.gravitino.stats.PartitionStatistics;
 import org.apache.gravitino.stats.PartitionStatisticsModification;
 import org.apache.gravitino.stats.Statistic;
-import org.apache.gravitino.stats.StatisticManager;
+import org.apache.gravitino.stats.StatisticDispatcher;
 import org.apache.gravitino.stats.StatisticValue;
 import org.apache.gravitino.utils.MetadataObjectUtil;
 import org.slf4j.Logger;
@@ -78,11 +78,11 @@ public class StatisticOperations {
 
   @Context private HttpServletRequest httpRequest;
 
-  private final StatisticManager statisticManager;
+  private final StatisticDispatcher statisticDispatcher;
 
   @Inject
-  public StatisticOperations(StatisticManager statisticManager) {
-    this.statisticManager = statisticManager;
+  public StatisticOperations(StatisticDispatcher statisticDispatcher) {
+    this.statisticDispatcher = statisticDispatcher;
   }
 
   @GET
@@ -113,7 +113,7 @@ public class StatisticOperations {
 
             MetadataObjectUtil.checkMetadataObject(metalake, object);
 
-            List<Statistic> statistics = 
statisticManager.listStatistics(metalake, object);
+            List<Statistic> statistics = 
statisticDispatcher.listStatistics(metalake, object);
             return Utils.ok(
                 new StatisticListResponse(
                     DTOConverters.toDTOs(statistics.toArray(new 
Statistic[0]))));
@@ -164,7 +164,7 @@ public class StatisticOperations {
 
             MetadataObjectUtil.checkMetadataObject(metalake, object);
 
-            statisticManager.updateStatistics(metalake, object, statisticMaps);
+            statisticDispatcher.updateStatistics(metalake, object, 
statisticMaps);
             return Utils.ok(new BaseResponse(0));
           });
     } catch (Exception e) {
@@ -204,7 +204,7 @@ public class StatisticOperations {
             MetadataObjectUtil.checkMetadataObject(metalake, object);
 
             boolean dropped =
-                statisticManager.dropStatistics(
+                statisticDispatcher.dropStatistics(
                     metalake, object, Lists.newArrayList(request.getNames()));
             return Utils.ok(new DropResponse(dropped));
           });
@@ -271,7 +271,7 @@ public class StatisticOperations {
             }
 
             List<PartitionStatistics> statistics =
-                statisticManager.listPartitionStatistics(metalake, object, 
range);
+                statisticDispatcher.listPartitionStatistics(metalake, object, 
range);
 
             PartitionStatisticsDTO[] partitionStatistics =
                 statistics.stream()
@@ -343,7 +343,7 @@ public class StatisticOperations {
 
             MetadataObjectUtil.checkMetadataObject(metalake, object);
 
-            statisticManager.updatePartitionStatistics(
+            statisticDispatcher.updatePartitionStatistics(
                 metalake,
                 object,
                 updates.stream()
@@ -400,7 +400,7 @@ public class StatisticOperations {
 
             return Utils.ok(
                 new DropResponse(
-                    statisticManager.dropPartitionStatistics(
+                    statisticDispatcher.dropPartitionStatistics(
                         metalake,
                         object,
                         request.getDrops().stream()
diff --git 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
index 18d00de09a..b6f6eac26a 100644
--- 
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
+++ 
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestStatisticOperations.java
@@ -67,6 +67,7 @@ import org.apache.gravitino.rest.RESTUtils;
 import org.apache.gravitino.stats.PartitionRange;
 import org.apache.gravitino.stats.PartitionStatistics;
 import org.apache.gravitino.stats.Statistic;
+import org.apache.gravitino.stats.StatisticDispatcher;
 import org.apache.gravitino.stats.StatisticManager;
 import org.apache.gravitino.stats.StatisticValue;
 import org.apache.gravitino.stats.StatisticValues;
@@ -126,7 +127,7 @@ public class TestStatisticOperations extends JerseyTest {
         new AbstractBinder() {
           @Override
           protected void configure() {
-            bind(manager).to(StatisticManager.class).ranked(2);
+            bind(manager).to(StatisticDispatcher.class).ranked(2);
             
bindFactory(MockServletRequestFactory.class).to(HttpServletRequest.class);
           }
         });

Reply via email to