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 8cc094c81b [#6079] fix(core): Support model failure event to Gravitino 
server (#6670)
8cc094c81b is described below

commit 8cc094c81b4ce1a8f3cee6d964339f5f5885059a
Author: Lord of Abyss <[email protected]>
AuthorDate: Thu Mar 13 10:50:04 2025 +0800

    [#6079] fix(core): Support model failure event to Gravitino server (#6670)
    
    ### What changes were proposed in this pull request?
    
    Support model failure event to Gravitino server.
    
    ### Why are the changes needed?
    
    Fix: #6079
    
    ### Does this PR introduce _any_ user-facing change?
    
    yes, add model failure event to Gravitino server, offering insights into
    what went wrong and why the operation failed.
    
    ### How was this patch tested?
    
    local test.
---
 .../gravitino/listener/ModelEventDispatcher.java   |  81 ++--
 .../api/event/DeleteModelFailureEvent.java         |  53 +++
 .../api/event/DeleteModelVersionFailureEvent.java  |  85 ++++
 .../listener/api/event/GetModelFailureEvent.java   |  52 +++
 .../api/event/GetModelVersionFailureEvent.java     |  83 ++++
 .../api/event/LinkModelVersionFailureEvent.java    |  69 ++++
 .../listener/api/event/ListModelFailureEvent.java  |  64 +++
 .../api/event/ListModelVersionFailureEvent.java    |  55 +++
 .../listener/api/event/ModelFailureEvent.java      |  50 +++
 .../event/RegisterAndLinkModelFailureEvent.java    |  87 +++++
 .../api/event/RegisterModelFailureEvent.java       |  67 ++++
 .../gravitino/listener/api/info/ModelInfo.java     |  32 +-
 .../listener/api/info/ModelVersionInfo.java        |  23 +-
 .../listener/api/event/TestModelEvent.java         | 434 +++++++++++++++++----
 docs/gravitino-server-config.md                    |   1 +
 15 files changed, 1123 insertions(+), 113 deletions(-)

diff --git 
a/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java 
b/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
index 9ffd7e8ae6..b98d12f457 100644
--- a/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
+++ b/core/src/main/java/org/apache/gravitino/listener/ModelEventDispatcher.java
@@ -28,14 +28,23 @@ import 
org.apache.gravitino.exceptions.ModelVersionAliasesAlreadyExistException;
 import org.apache.gravitino.exceptions.NoSuchModelException;
 import org.apache.gravitino.exceptions.NoSuchModelVersionException;
 import org.apache.gravitino.exceptions.NoSuchSchemaException;
+import org.apache.gravitino.listener.api.event.DeleteModelFailureEvent;
 import org.apache.gravitino.listener.api.event.DeleteModelPreEvent;
+import org.apache.gravitino.listener.api.event.DeleteModelVersionFailureEvent;
 import org.apache.gravitino.listener.api.event.DeleteModelVersionPreEvent;
+import org.apache.gravitino.listener.api.event.GetModelFailureEvent;
 import org.apache.gravitino.listener.api.event.GetModelPreEvent;
+import org.apache.gravitino.listener.api.event.GetModelVersionFailureEvent;
 import org.apache.gravitino.listener.api.event.GetModelVersionPreEvent;
+import org.apache.gravitino.listener.api.event.LinkModelVersionFailureEvent;
 import org.apache.gravitino.listener.api.event.LinkModelVersionPreEvent;
+import org.apache.gravitino.listener.api.event.ListModelFailureEvent;
 import org.apache.gravitino.listener.api.event.ListModelPreEvent;
+import org.apache.gravitino.listener.api.event.ListModelVersionFailureEvent;
 import org.apache.gravitino.listener.api.event.ListModelVersionPreEvent;
+import 
org.apache.gravitino.listener.api.event.RegisterAndLinkModelFailureEvent;
 import org.apache.gravitino.listener.api.event.RegisterAndLinkModelPreEvent;
+import org.apache.gravitino.listener.api.event.RegisterModelFailureEvent;
 import org.apache.gravitino.listener.api.event.RegisterModelPreEvent;
 import org.apache.gravitino.listener.api.info.ModelInfo;
 import org.apache.gravitino.listener.api.info.ModelVersionInfo;
@@ -70,15 +79,15 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   @Override
   public Model registerModel(NameIdentifier ident, String comment, Map<String, 
String> properties)
       throws NoSuchSchemaException, ModelAlreadyExistsException {
+    String user = PrincipalUtils.getCurrentUserName();
     ModelInfo registerRequest = new ModelInfo(ident.name(), properties, 
comment);
-    eventBus.dispatchEvent(
-        new RegisterModelPreEvent(PrincipalUtils.getCurrentUserName(), ident, 
registerRequest));
+    eventBus.dispatchEvent(new RegisterModelPreEvent(user, ident, 
registerRequest));
     try {
       Model model = dispatcher.registerModel(ident, comment, properties);
       // TODO: ModelEvent
       return model;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new RegisterModelFailureEvent(user, ident, e, 
registerRequest));
       throw e;
     }
   }
@@ -96,18 +105,19 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
     ModelInfo registerModelRequest = new ModelInfo(ident.name(), properties, 
comment);
     ModelVersionInfo linkModelVersionRequest =
         new ModelVersionInfo(uri, comment, properties, aliases);
+    String user = PrincipalUtils.getCurrentUserName();
 
     RegisterAndLinkModelPreEvent registerAndLinkModelPreEvent =
         new RegisterAndLinkModelPreEvent(
-            PrincipalUtils.getCurrentUserName(),
-            ident,
-            registerModelRequest,
-            linkModelVersionRequest);
+            user, ident, registerModelRequest, linkModelVersionRequest);
     eventBus.dispatchEvent(registerAndLinkModelPreEvent);
     try {
       // TODO: ModelEvent
       return dispatcher.registerModel(ident, uri, aliases, comment, 
properties);
     } catch (Exception e) {
+      eventBus.dispatchEvent(
+          new RegisterAndLinkModelFailureEvent(
+              user, ident, e, registerModelRequest, linkModelVersionRequest));
       throw e;
     }
   }
@@ -115,13 +125,14 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public Model getModel(NameIdentifier ident) throws NoSuchModelException {
-    eventBus.dispatchEvent(new 
GetModelPreEvent(PrincipalUtils.getCurrentUserName(), ident));
+    String user = PrincipalUtils.getCurrentUserName();
+    eventBus.dispatchEvent(new GetModelPreEvent(user, ident));
     try {
       Model model = dispatcher.getModel(ident);
       // TODO: ModelEvent
       return model;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new GetModelFailureEvent(user, ident, e));
       throw e;
     }
   }
@@ -129,12 +140,13 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public boolean deleteModel(NameIdentifier ident) {
-    eventBus.dispatchEvent(new 
DeleteModelPreEvent(PrincipalUtils.getCurrentUserName(), ident));
+    String user = PrincipalUtils.getCurrentUserName();
+    eventBus.dispatchEvent(new DeleteModelPreEvent(user, ident));
     try {
       // TODO: ModelEvent
       return dispatcher.deleteModel(ident);
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new DeleteModelFailureEvent(user, ident, e));
       throw e;
     }
   }
@@ -142,13 +154,14 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public NameIdentifier[] listModels(Namespace namespace) throws 
NoSuchSchemaException {
-    eventBus.dispatchEvent(new 
ListModelPreEvent(PrincipalUtils.getCurrentUserName(), namespace));
+    String user = PrincipalUtils.getCurrentUserName();
+    eventBus.dispatchEvent(new ListModelPreEvent(user, namespace));
     try {
       NameIdentifier[] models = dispatcher.listModels(namespace);
       // TODO: ModelEvent
       return models;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new ListModelFailureEvent(user, namespace, e));
       throw e;
     }
   }
@@ -163,13 +176,14 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
       Map<String, String> properties)
       throws NoSuchModelException, ModelVersionAliasesAlreadyExistException {
     ModelVersionInfo linkModelRequest = new ModelVersionInfo(uri, comment, 
properties, aliases);
-    eventBus.dispatchEvent(
-        new LinkModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident, linkModelRequest));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new LinkModelVersionPreEvent(user, ident, 
linkModelRequest));
     try {
       dispatcher.linkModelVersion(ident, uri, aliases, comment, properties);
       // TODO: ModelEvent
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new LinkModelVersionFailureEvent(user, ident, e, 
linkModelRequest));
       throw e;
     }
   }
@@ -178,13 +192,14 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   @Override
   public ModelVersion getModelVersion(NameIdentifier ident, int version)
       throws NoSuchModelVersionException {
-    eventBus.dispatchEvent(
-        new GetModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident, null, version));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new GetModelVersionPreEvent(user, ident, null, 
version));
     try {
       // TODO: ModelEvent
       return dispatcher.getModelVersion(ident, version);
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new GetModelVersionFailureEvent(user, ident, e, 
null, version));
       throw e;
     }
   }
@@ -193,14 +208,15 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   @Override
   public ModelVersion getModelVersion(NameIdentifier ident, String alias)
       throws NoSuchModelVersionException {
-    eventBus.dispatchEvent(
-        new GetModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident, alias, null));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new GetModelVersionPreEvent(user, ident, alias, 
null));
     try {
       ModelVersion modelVersion = dispatcher.getModelVersion(ident, alias);
       // TODO: ModelEvent
       return modelVersion;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new GetModelVersionFailureEvent(user, ident, e, 
alias, null));
       throw e;
     }
   }
@@ -208,14 +224,15 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public boolean deleteModelVersion(NameIdentifier ident, int version) {
-    eventBus.dispatchEvent(
-        new DeleteModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident, null, version));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new DeleteModelVersionPreEvent(user, ident, null, 
version));
     try {
       boolean isExists = dispatcher.deleteModelVersion(ident, version);
       // TODO: ModelEvent
       return isExists;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new DeleteModelVersionFailureEvent(user, ident, 
e, null, version));
       throw e;
     }
   }
@@ -223,14 +240,15 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public boolean deleteModelVersion(NameIdentifier ident, String alias) {
-    eventBus.dispatchEvent(
-        new DeleteModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident, alias, null));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new DeleteModelVersionPreEvent(user, ident, alias, 
null));
     try {
       boolean isExists = dispatcher.deleteModelVersion(ident, alias);
       // TODO: ModelEvent
       return isExists;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new DeleteModelVersionFailureEvent(user, ident, 
e, alias, null));
       throw e;
     }
   }
@@ -238,14 +256,15 @@ public class ModelEventDispatcher implements 
ModelDispatcher {
   /** {@inheritDoc} */
   @Override
   public int[] listModelVersions(NameIdentifier ident) throws 
NoSuchModelException {
-    eventBus.dispatchEvent(
-        new ListModelVersionPreEvent(PrincipalUtils.getCurrentUserName(), 
ident));
+    String user = PrincipalUtils.getCurrentUserName();
+
+    eventBus.dispatchEvent(new ListModelVersionPreEvent(user, ident));
     try {
       int[] versions = dispatcher.listModelVersions(ident);
       // TODO: ModelEvent
       return versions;
     } catch (Exception e) {
-      // TODO: failureEvent
+      eventBus.dispatchEvent(new ListModelVersionFailureEvent(user, ident, e));
       throw e;
     }
   }
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelFailureEvent.java
new file mode 100644
index 0000000000..959398fd96
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelFailureEvent.java
@@ -0,0 +1,53 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is generated when an attempt to drop a model from 
the schema fails due
+ * to an exception.
+ */
+@DeveloperApi
+public class DeleteModelFailureEvent extends ModelFailureEvent {
+  /**
+   * Construct a new {@link DeleteModelFailureEvent} instance, capturing 
detailed information about
+   * the failed attempt to drop a model.
+   *
+   * @param user The user who initiated the drop model operation.
+   * @param identifier The identifier of the model that the operation 
attempted to drop.
+   * @param exception The exception that was thrown during the drop model 
operation, offering
+   *     insights into what went wrong and why the operation failed.
+   */
+  public DeleteModelFailureEvent(String user, NameIdentifier identifier, 
Exception exception) {
+    super(user, identifier, exception);
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.DELETE_MODEL;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelVersionFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelVersionFailureEvent.java
new file mode 100644
index 0000000000..69ebdfee6f
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/DeleteModelVersionFailureEvent.java
@@ -0,0 +1,85 @@
+/*
+ * 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 java.util.Optional;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is generated when an attempt to delete a model 
version from a model
+ * fails due to an exception.
+ */
+@DeveloperApi
+public class DeleteModelVersionFailureEvent extends ModelFailureEvent {
+  private final Optional<String> alias;
+  private final Optional<Integer> version;
+
+  /**
+   * Constructs a new instance of {@link DeleteModelVersionFailureEvent} 
instance, capturing
+   * detailed information about the failed attempt to delete a model version. 
only one of alias or
+   * version are valid.
+   *
+   * @param user The user who initiated the delete model version operation.
+   * @param identifier The identifier of the model that the operation 
attempted to delete a version
+   *     from.
+   * @param exception The exception that was thrown during the delete model 
version operation,
+   *     offering insights into what went wrong and why the operation failed.
+   * @param alias The alias of the model version to be deleted.
+   * @param version The version of the model version to be deleted.
+   */
+  public DeleteModelVersionFailureEvent(
+      String user, NameIdentifier identifier, Exception exception, String 
alias, Integer version) {
+    super(user, identifier, exception);
+
+    this.alias = Optional.ofNullable(alias);
+    this.version = Optional.ofNullable(version);
+  }
+
+  /**
+   * Returns the alias of the model version to be deleted.
+   *
+   * @return A {@link Optional} instance containing the alias if it was 
provided, or an empty {@link
+   *     Optional} otherwise.
+   */
+  public Optional<String> alias() {
+    return alias;
+  }
+
+  /**
+   * Returns the version of the model version to be deleted.
+   *
+   * @return A {@link Optional} instance containing the version if it was 
provided, or an empty
+   *     {@link Optional} otherwise.
+   */
+  public Optional<Integer> version() {
+    return version;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.DELETE_MODEL_VERSION;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelFailureEvent.java
new file mode 100644
index 0000000000..402a34ec82
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelFailureEvent.java
@@ -0,0 +1,52 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is generated when an attempt to get a model fails 
due to an exception.
+ */
+@DeveloperApi
+public class GetModelFailureEvent extends ModelFailureEvent {
+  /**
+   * Construct a new {@link GetModelFailureEvent} instance, capturing detailed 
information about the
+   * failed attempt to get a model.
+   *
+   * @param user The user who initiated the get model operation.
+   * @param identifier The identifier of the model that the operation 
attempted to get.
+   * @param exception The exception that was thrown during the get model 
operation, offering
+   *     insights into what went wrong and why the operation failed.
+   */
+  public GetModelFailureEvent(String user, NameIdentifier identifier, 
Exception exception) {
+    super(user, identifier, exception);
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.GET_MODEL;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelVersionFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelVersionFailureEvent.java
new file mode 100644
index 0000000000..bf4f621013
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/GetModelVersionFailureEvent.java
@@ -0,0 +1,83 @@
+/*
+ * 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 java.util.Optional;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is generated when an attempt to get a model 
version fails due to an
+ * exception.
+ */
+@DeveloperApi
+public class GetModelVersionFailureEvent extends ModelFailureEvent {
+  private final Optional<String> alias;
+  private final Optional<Integer> version;
+
+  /**
+   * Construct a new {@link GetModelVersionFailureEvent} instance, capturing 
detailed information
+   * about the failed attempt to get a model version. only one of alias or 
version are valid.
+   *
+   * @param user The user associated with the failed model operation.
+   * @param identifier The identifier of the model that was involved in the 
failed operation.
+   * @param exception The exception that was thrown during the get model 
version operation, offering
+   *     insights into what went wrong and why the operation failed.
+   * @param alias The alias of the model version to get.
+   * @param version The version of the model version to get.
+   */
+  public GetModelVersionFailureEvent(
+      String user, NameIdentifier identifier, Exception exception, String 
alias, Integer version) {
+    super(user, identifier, exception);
+
+    this.alias = Optional.ofNullable(alias);
+    this.version = Optional.ofNullable(version);
+  }
+
+  /**
+   * Returns the alias of the model version to get.
+   *
+   * @return A {@link Optional} instance containing the alias if it was 
provided, or an empty {@link
+   *     Optional} otherwise.
+   */
+  public Optional<String> alias() {
+    return alias;
+  }
+
+  /**
+   * Returns the version of the model version to get.
+   *
+   * @return A {@link Optional} instance containing the version if it was 
provided, or an empty
+   *     {@link Optional} otherwise.
+   */
+  public Optional<Integer> version() {
+    return version;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.GET_MODEL_VERSION;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/LinkModelVersionFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/LinkModelVersionFailureEvent.java
new file mode 100644
index 0000000000..e47a853f38
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/LinkModelVersionFailureEvent.java
@@ -0,0 +1,69 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.info.ModelVersionInfo;
+
+/**
+ * Represents an event that is triggered when an attempt to link a model 
version fails due to an
+ * exception.
+ */
+@DeveloperApi
+public class LinkModelVersionFailureEvent extends ModelFailureEvent {
+  private final ModelVersionInfo linkModelVersionRequest;
+  /**
+   * Construct a new {@link LinkModelVersionFailureEvent} instance, capturing 
information about the
+   * failed model version operation.
+   *
+   * @param user The username of the individual who initiated the operation to 
link a model version.
+   * @param identifier The identifier of the model that was involved in the 
failed operation.
+   * @param exception The exception encountered during the attempt to link a 
model version.
+   */
+  public LinkModelVersionFailureEvent(
+      String user,
+      NameIdentifier identifier,
+      Exception exception,
+      ModelVersionInfo linkModelVersionRequest) {
+    super(user, identifier, exception);
+
+    this.linkModelVersionRequest = linkModelVersionRequest;
+  }
+
+  /**
+   * Retrieves the linked model version information.
+   *
+   * @return the model version information.
+   */
+  public ModelVersionInfo linkModelVersionRequest() {
+    return linkModelVersionRequest;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.LINK_MODEL_VERSION;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelFailureEvent.java
new file mode 100644
index 0000000000..81b46ca7cc
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelFailureEvent.java
@@ -0,0 +1,64 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is triggered when an attempt to list models within 
a namespace fails due
+ * to an exception.
+ */
+@DeveloperApi
+public class ListModelFailureEvent extends ModelFailureEvent {
+  private final Namespace namespace;
+
+  /**
+   * Construct a new {@link ListModelFailureEvent} instance.
+   *
+   * @param user The username of the individual who initiated the operation to 
list models.
+   * @param namespace The namespace for which the model listing was attempted.
+   * @param exception The exception encountered during the attempt to list 
models.
+   */
+  public ListModelFailureEvent(String user, Namespace namespace, Exception 
exception) {
+    super(user, NameIdentifier.of(namespace.levels()), exception);
+    this.namespace = namespace;
+  }
+
+  /**
+   * Retrieves the namespace associated with this failure event.
+   *
+   * @return A {@link Namespace} instance for which the model listing was 
attempted
+   */
+  public Namespace namespace() {
+    return namespace;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_MODEL;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelVersionFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelVersionFailureEvent.java
new file mode 100644
index 0000000000..50bed4a929
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ListModelVersionFailureEvent.java
@@ -0,0 +1,55 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * Represents an event that is generated when an attempt to list versions of a 
model fails due to an
+ * exception.
+ */
+@DeveloperApi
+public class ListModelVersionFailureEvent extends ModelFailureEvent {
+
+  /**
+   * Constuct a new {@link ListModelVersionFailureEvent} instance, capturing 
detailed information
+   * about the failed attempt to list versions of a model.
+   *
+   * @param user The user who initiated the list model version operation.
+   * @param identifier The identifier of the model that the operation 
attempted to list versions
+   *     for.
+   * @param exception The exception that was thrown during the list model 
version operation,
+   *     offering insights into what went wrong and why the operation failed.
+   */
+  public ListModelVersionFailureEvent(String user, NameIdentifier identifier, 
Exception exception) {
+    super(user, identifier, exception);
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.LIST_MODEL_VERSIONS;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/ModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ModelFailureEvent.java
new file mode 100644
index 0000000000..6d55623294
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/ModelFailureEvent.java
@@ -0,0 +1,50 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+
+/**
+ * An abstract class representing events that are triggered when a model 
operation fails due to an
+ * exception. This class extends {@link FailureEvent} to provide a more 
specific context related to
+ * model operations, encapsulating details about the user who initiated the 
operation, the
+ * identifier of the model involved, and the exception that led to the failure.
+ *
+ * <p>Implementations of this class can be used to convey detailed information 
about failures in
+ * operations such as creating, updating, deleting, or querying tables, making 
it easier to diagnose
+ * and respond to issues.
+ */
+@DeveloperApi
+public abstract class ModelFailureEvent extends FailureEvent {
+
+  /**
+   * Creates a new instance of {@code ModelFailureEvent}, capturing 
information about the failed
+   * model operation.
+   *
+   * @param user The user associated with the failed model operation.
+   * @param identifier The identifier of the model that was involved in the 
failed operation.
+   * @param exception The exception that was thrown during the model 
operation, indicating the cause
+   *     of the failure.
+   */
+  protected ModelFailureEvent(String user, NameIdentifier identifier, 
Exception exception) {
+    super(user, identifier, exception);
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterAndLinkModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterAndLinkModelFailureEvent.java
new file mode 100644
index 0000000000..523883e99a
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterAndLinkModelFailureEvent.java
@@ -0,0 +1,87 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.info.ModelInfo;
+import org.apache.gravitino.listener.api.info.ModelVersionInfo;
+
+/**
+ * Represents an event that is generated when an attempt to register a model 
or link a model version
+ * of a model fails due to an exception.
+ */
+@DeveloperApi
+public class RegisterAndLinkModelFailureEvent extends ModelFailureEvent {
+  private final ModelInfo registerModelRequest;
+  private final ModelVersionInfo linkModelVersionRequest;
+
+  /**
+   * Create a new {@link RegisterAndLinkModelFailureEvent} instance, capturing 
detailed information
+   * about * the failed attempt to register a model.
+   *
+   * @param user The user who initiated the register/link model operation.
+   * @param identifier The identifier of the model that was involved in the 
failed operation.
+   * @param exception The exception encountered during the attempt to register 
a model or link a
+   *     model version.
+   * @param registerModelRequest the model information that was requested to 
be registered.
+   * @param linkModelVersionRequest The version information of the model that 
was requested to be
+   *     linked.
+   */
+  public RegisterAndLinkModelFailureEvent(
+      String user,
+      NameIdentifier identifier,
+      Exception exception,
+      ModelInfo registerModelRequest,
+      ModelVersionInfo linkModelVersionRequest) {
+    super(user, identifier, exception);
+
+    this.registerModelRequest = registerModelRequest;
+    this.linkModelVersionRequest = linkModelVersionRequest;
+  }
+
+  /**
+   * Retrieves the registered model information.
+   *
+   * @return the model information.
+   */
+  public ModelInfo registerModelRequest() {
+    return registerModelRequest;
+  }
+
+  /**
+   * Retrieves the linked model version information.
+   *
+   * @return the model version information.
+   */
+  public ModelVersionInfo linkModelVersionRequest() {
+    return linkModelVersionRequest;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.REGISTER_AND_LINK_MODEL_VERSION;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterModelFailureEvent.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterModelFailureEvent.java
new file mode 100644
index 0000000000..90d14f4228
--- /dev/null
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/event/RegisterModelFailureEvent.java
@@ -0,0 +1,67 @@
+/*
+ * 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 org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.annotation.DeveloperApi;
+import org.apache.gravitino.listener.api.info.ModelInfo;
+
+/**
+ * Represents an event that is generated when an attempt to register a model 
fails due to an
+ * exception.
+ */
+@DeveloperApi
+public class RegisterModelFailureEvent extends ModelFailureEvent {
+  private final ModelInfo registerModelRequest;
+  /**
+   * Construct a {@link RegisterModelFailureEvent} instance, capturing 
detailed information about
+   * the failed attempt to register a model.
+   *
+   * @param user The user who initiated the register model operation.
+   * @param identifier The identifier of the model that the operation 
attempted to register.
+   * @param exception The exception that was thrown during the register model 
operation, offering
+   *     insights into what went wrong and why the operation failed.
+   * @param registerModelRequest the model information that was requested to 
be registered.
+   */
+  public RegisterModelFailureEvent(
+      String user, NameIdentifier identifier, Exception exception, ModelInfo 
registerModelRequest) {
+    super(user, identifier, exception);
+    this.registerModelRequest = registerModelRequest;
+  }
+
+  /**
+   * Retrieves the registered model information.
+   *
+   * @return the model information.
+   */
+  public ModelInfo registerModelRequest() {
+    return registerModelRequest;
+  }
+
+  /**
+   * Returns the type of operation.
+   *
+   * @return the operation type.
+   */
+  @Override
+  public OperationType operationType() {
+    return OperationType.REGISTER_MODEL;
+  }
+}
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/info/ModelInfo.java 
b/core/src/main/java/org/apache/gravitino/listener/api/info/ModelInfo.java
index 870f725277..88e85caea7 100644
--- a/core/src/main/java/org/apache/gravitino/listener/api/info/ModelInfo.java
+++ b/core/src/main/java/org/apache/gravitino/listener/api/info/ModelInfo.java
@@ -39,24 +39,40 @@ public class ModelInfo {
   private final Optional<Integer> lastVersion;
 
   /**
-   * Constructs model information based on a given model.
+   * Constructs a {@link ModelInfo} instance based on a given model.
    *
    * @param model the model to expose information for.
    */
   public ModelInfo(Model model) {
-    this.name = model.name();
-    this.properties =
-        model.properties() == null ? ImmutableMap.of() : 
ImmutableMap.copyOf(model.properties());
-
-    this.comment = Optional.ofNullable(model.comment());
-    this.audit = Optional.ofNullable(model.auditInfo());
-    this.lastVersion = Optional.ofNullable(model.latestVersion());
+    this(
+        model.name(),
+        model.properties(),
+        model.comment(),
+        model.auditInfo(),
+        model.latestVersion());
   }
 
+  /**
+   * Constructs a {@link ModelInfo} instance based on name, properties, and 
comment.
+   *
+   * @param name the name of the model.
+   * @param properties the properties of the model.
+   * @param comment the comment of the model.
+   */
   public ModelInfo(String name, Map<String, String> properties, String 
comment) {
     this(name, properties, comment, null, null);
   }
 
+  /**
+   * Constructs a {@link ModelInfo} instance based on name, properties, 
comment, audit, and last
+   * version.
+   *
+   * @param name the name of the model.
+   * @param properties the properties of the model.
+   * @param comment the comment of the model.
+   * @param audit the audit information of the model.
+   * @param lastVersion the last version of the model.
+   */
   public ModelInfo(
       String name,
       Map<String, String> properties,
diff --git 
a/core/src/main/java/org/apache/gravitino/listener/api/info/ModelVersionInfo.java
 
b/core/src/main/java/org/apache/gravitino/listener/api/info/ModelVersionInfo.java
index a9a953a86e..5eea942d1f 100644
--- 
a/core/src/main/java/org/apache/gravitino/listener/api/info/ModelVersionInfo.java
+++ 
b/core/src/main/java/org/apache/gravitino/listener/api/info/ModelVersionInfo.java
@@ -38,7 +38,7 @@ public class ModelVersionInfo {
   private final Optional<Audit> auditInfo;
 
   /**
-   * Constructs model version information based on a given {@link 
ModelVersion}.
+   * Constructs a {@link ModelVersionInfo} instance based on a given {@link 
ModelVersion}.
    *
    * @param modelVersion the model version to expose information for.
    */
@@ -51,18 +51,29 @@ public class ModelVersionInfo {
         modelVersion.auditInfo());
   }
 
+  /**
+   * Constructs a {@link ModelVersionInfo} instance based on given URI, 
comment, properties, and
+   * aliases.
+   *
+   * @param uri The URI of the model version.
+   * @param comment The comment of the model version.
+   * @param properties The properties of the model version.
+   * @param aliases The aliases of the model version.
+   */
   public ModelVersionInfo(
       String uri, String comment, Map<String, String> properties, String[] 
aliases) {
     this(uri, comment, properties, aliases, null);
   }
 
   /**
-   * Constructs model version information based on a given arguments.
+   * Constructs a {@link ModelVersionInfo} instance based on given uri, 
comment, properties,
+   * aliases, and audit information.
    *
-   * @param uri
-   * @param aliases
-   * @param comment
-   * @param properties
+   * @param uri The URI of the model version.
+   * @param comment The comment of the model version.
+   * @param properties The properties of the model version.
+   * @param aliases The aliases of the model version.
+   * @param auditInfo The audit information of the model version.
    */
   public ModelVersionInfo(
       String uri,
diff --git 
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
index 8489301435..3ffa7c38b9 100644
--- 
a/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
+++ 
b/core/src/test/java/org/apache/gravitino/listener/api/event/TestModelEvent.java
@@ -26,6 +26,7 @@ import com.google.common.collect.ImmutableMap;
 import java.time.Instant;
 import java.util.Collections;
 import java.util.Map;
+import java.util.Optional;
 import org.apache.gravitino.Audit;
 import org.apache.gravitino.NameIdentifier;
 import org.apache.gravitino.Namespace;
@@ -61,23 +62,24 @@ public class TestModelEvent {
   @BeforeAll
   void init() {
     this.namespace = Namespace.of("metalake", "catalog", "schema");
+    this.existingIdentA = NameIdentifierUtil.ofModel("metalake", "catalog", 
"schema", "modelA");
+    this.existingIdentB = NameIdentifierUtil.ofModel("metalake", "catalog", 
"schema", "modelB");
+
     this.modelA = getMockModel("modelA", "commentA");
     this.modelB = getMockModel("modelB", "commentB");
+
     this.firstModelVersion =
         mockModelVersion("uriA", new String[] {"aliasProduction"}, 
"versionInfoA");
     this.secondModelVersion = mockModelVersion("uriB", new String[] 
{"aliasTest"}, "versionInfoB");
-    System.out.println(secondModelVersion.toString());
-    this.existingIdentA = NameIdentifierUtil.ofModel("metalake", "catalog", 
"schema", "modelA");
-    this.existingIdentB = NameIdentifierUtil.ofModel("metalake", "catalog", 
"schema", "modelB");
+
     this.notExistingIdent =
         NameIdentifierUtil.ofModel("metalake", "catalog", "schema", 
"not_exist");
+
     this.dummyEventListener = new DummyEventListener();
 
     EventBus eventBus = new 
EventBus(Collections.singletonList(dummyEventListener));
     this.dispatcher = new ModelEventDispatcher(eventBus, mockTagDispatcher());
     this.failureDispatcher = new ModelEventDispatcher(eventBus, 
mockExceptionModelDispatcher());
-    // TODO: add failure dispatcher tests.
-    System.out.println(this.failureDispatcher.toString());
   }
 
   @Test
@@ -85,19 +87,7 @@ public class TestModelEvent {
     Model mockModel = getMockModel("model", "comment");
     ModelInfo modelInfo = new ModelInfo(mockModel);
 
-    Assertions.assertEquals("model", modelInfo.name());
-    Assertions.assertEquals(1, modelInfo.properties().size());
-    Assertions.assertEquals("#FFFFFF", modelInfo.properties().get("color"));
-
-    Assertions.assertTrue(modelInfo.comment().isPresent());
-    String comment = modelInfo.comment().get();
-    Assertions.assertEquals("comment", comment);
-
-    Assertions.assertFalse(modelInfo.audit().isPresent());
-
-    Assertions.assertTrue(modelInfo.lastVersion().isPresent());
-    int lastVersion = modelInfo.lastVersion().get();
-    Assertions.assertEquals(1, lastVersion);
+    checkModelInfo(modelInfo, mockModel);
   }
 
   @Test
@@ -105,7 +95,7 @@ public class TestModelEvent {
     Model mockModel = getMockModel("model", null);
     ModelInfo modelInfo = new ModelInfo(mockModel);
 
-    Assertions.assertFalse(modelInfo.comment().isPresent());
+    checkModelInfo(modelInfo, mockModel);
   }
 
   @Test
@@ -113,20 +103,33 @@ public class TestModelEvent {
     Model mockModel = getMockModelWithAudit("model", "comment");
     ModelInfo modelInfo = new ModelInfo(mockModel);
 
-    Assertions.assertEquals("model", modelInfo.name());
-    Assertions.assertEquals(1, modelInfo.properties().size());
-    Assertions.assertEquals("#FFFFFF", modelInfo.properties().get("color"));
+    checkModelInfo(modelInfo, mockModel);
+  }
 
-    Assertions.assertTrue(modelInfo.comment().isPresent());
-    String comment = modelInfo.comment().get();
-    Assertions.assertEquals("comment", comment);
+  @Test
+  void testModelVersionInfo() {
+    ModelVersion modelVersion =
+        mockModelVersion("uriA", new String[] {"aliasProduction"}, 
"versionInfoA");
+    ModelVersionInfo modelVersionInfo = new ModelVersionInfo(modelVersion);
 
-    Assertions.assertTrue(modelInfo.audit().isPresent());
-    Audit audit = modelInfo.audit().get();
-    Assertions.assertEquals("demo_user", audit.creator());
-    Assertions.assertEquals(1611111111111L, audit.createTime().toEpochMilli());
-    Assertions.assertEquals("demo_user", audit.lastModifier());
-    Assertions.assertEquals(1611111111111L, 
audit.lastModifiedTime().toEpochMilli());
+    checkModelVersionInfo(modelVersionInfo, modelVersion);
+  }
+
+  @Test
+  void testModelVersionInfoWithoutComment() {
+    ModelVersion modelVersion = mockModelVersion("uriA", new String[] 
{"aliasProduction"}, null);
+    ModelVersionInfo modelVersionInfo = new ModelVersionInfo(modelVersion);
+
+    checkModelVersionInfo(modelVersionInfo, modelVersion);
+  }
+
+  @Test
+  void testModelVersionInfoWithAudit() {
+    ModelVersion modelVersion =
+        getMockModelWithAudit("uriA", new String[] {"aliasProduction"}, 
"versionInfoA");
+    ModelVersionInfo modelVersionInfo = new ModelVersionInfo(modelVersion);
+
+    checkModelVersionInfo(modelVersionInfo, modelVersion);
   }
 
   @Test
@@ -144,13 +147,30 @@ public class TestModelEvent {
     Assertions.assertEquals(existingIdentA, 
registerModelPreEvent.identifier());
     ModelInfo modelInfoPreEvent = registerModelPreEvent.registerModelRequest();
 
-    Assertions.assertEquals("modelA", modelInfoPreEvent.name());
-    Assertions.assertEquals(ImmutableMap.of("color", "#FFFFFF"), 
modelInfoPreEvent.properties());
-    Assertions.assertTrue(modelInfoPreEvent.comment().isPresent());
-    String comment = modelInfoPreEvent.comment().get();
-    Assertions.assertEquals("commentA", comment);
-    Assertions.assertFalse(modelInfoPreEvent.audit().isPresent());
-    Assertions.assertFalse(modelInfoPreEvent.lastVersion().isPresent());
+    checkModelInfo(modelInfoPreEvent, modelA);
+  }
+
+  @Test
+  void testRegisterModelFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.registerModel(
+                existingIdentA, "commentA", ImmutableMap.of("color", 
"#FFFFFF")));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(existingIdentA, event.identifier());
+    Assertions.assertEquals(RegisterModelFailureEvent.class, event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((RegisterModelFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.REGISTER_MODEL, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+    ModelInfo registerModelRequest = ((RegisterModelFailureEvent) 
event).registerModelRequest();
+
+    // check model-info
+    checkModelInfo(registerModelRequest, modelA);
   }
 
   @Test
@@ -158,7 +178,7 @@ public class TestModelEvent {
     dispatcher.registerModel(
         existingIdentA,
         "uriA",
-        new String[] {"aliasProduction", "aliasTest"},
+        new String[] {"aliasProduction"},
         "commentA",
         ImmutableMap.of("color", "#FFFFFF"));
     // validate pre-event
@@ -173,23 +193,54 @@ public class TestModelEvent {
         (RegisterAndLinkModelPreEvent) preEvent;
     ModelInfo registerModelRequest = 
registerAndLinkModelPreEvent.registerModelRequest();
 
-    Assertions.assertEquals("modelA", registerModelRequest.name());
-    Assertions.assertEquals(ImmutableMap.of("color", "#FFFFFF"), 
registerModelRequest.properties());
-    Assertions.assertTrue(registerModelRequest.comment().isPresent());
-    String comment = registerModelRequest.comment().get();
-    Assertions.assertEquals("commentA", comment);
-    Assertions.assertFalse(registerModelRequest.audit().isPresent());
-    Assertions.assertFalse(registerModelRequest.lastVersion().isPresent());
+    checkModelInfo(registerModelRequest, modelA);
 
     // validate pre-event model version info
-    ModelVersionInfo modelVersionInfoPreEvent =
+    ModelVersionInfo linkModelVersionRequest =
         registerAndLinkModelPreEvent.linkModelVersionRequest();
-    Assertions.assertEquals("uriA", modelVersionInfoPreEvent.uri());
-    Assertions.assertTrue(modelVersionInfoPreEvent.aliases().isPresent());
-    String[] aliases = modelVersionInfoPreEvent.aliases().get();
-    Assertions.assertEquals(2, aliases.length);
-    Assertions.assertEquals("aliasProduction", aliases[0]);
-    Assertions.assertEquals("aliasTest", aliases[1]);
+
+    Assertions.assertEquals(firstModelVersion.uri(), 
linkModelVersionRequest.uri());
+    Assertions.assertEquals("commentA", 
linkModelVersionRequest.comment().orElse(null));
+    checkArray(firstModelVersion.aliases(), 
linkModelVersionRequest.aliases().orElse(null));
+    checkProperties(firstModelVersion.properties(), 
linkModelVersionRequest.properties());
+    checkAudit(firstModelVersion.auditInfo(), linkModelVersionRequest.audit());
+  }
+
+  @Test
+  void testRegisterAndLinkModelFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.registerModel(
+                existingIdentA,
+                "uriA",
+                new String[] {"aliasProduction"},
+                "commentA",
+                ImmutableMap.of("color", "#FFFFFF")));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(RegisterAndLinkModelFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(existingIdentA, event.identifier());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((RegisterAndLinkModelFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.REGISTER_AND_LINK_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    // validate model info
+    RegisterAndLinkModelFailureEvent registerAndLinkModelFailureEvent =
+        (RegisterAndLinkModelFailureEvent) event;
+    ModelInfo registerModelRequest = 
registerAndLinkModelFailureEvent.registerModelRequest();
+    ModelVersionInfo linkModelVersionRequest =
+        registerAndLinkModelFailureEvent.linkModelVersionRequest();
+
+    checkModelInfo(registerModelRequest, modelA);
+    Assertions.assertEquals(firstModelVersion.uri(), 
linkModelVersionRequest.uri());
+    Assertions.assertEquals("commentA", 
linkModelVersionRequest.comment().orElse(null));
+    checkArray(firstModelVersion.aliases(), 
linkModelVersionRequest.aliases().orElse(null));
+    checkProperties(firstModelVersion.properties(), 
linkModelVersionRequest.properties());
+    checkAudit(firstModelVersion.auditInfo(), linkModelVersionRequest.audit());
   }
 
   @Test
@@ -207,7 +258,24 @@ public class TestModelEvent {
   }
 
   @Test
-  void testDeleteExistsModelEvent() {
+  void testGetModelFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class, () -> 
failureDispatcher.getModel(notExistingIdent));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(GetModelFailureEvent.class, event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class, ((GetModelFailureEvent) 
event).exception().getClass());
+    Assertions.assertEquals(OperationType.GET_MODEL, event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    GetModelFailureEvent getModelFailureEvent = (GetModelFailureEvent) event;
+    Assertions.assertEquals(notExistingIdent, 
getModelFailureEvent.identifier());
+  }
+
+  @Test
+  void testDeleteModelEvent() {
     dispatcher.deleteModel(existingIdentA);
 
     // validate pre-event
@@ -220,6 +288,23 @@ public class TestModelEvent {
     Assertions.assertEquals(existingIdentA, deleteModelPreEvent.identifier());
   }
 
+  @Test
+  void testDeleteModelFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class, () -> 
failureDispatcher.deleteModel(notExistingIdent));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(DeleteModelFailureEvent.class, event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class, ((DeleteModelFailureEvent) 
event).exception().getClass());
+    Assertions.assertEquals(OperationType.DELETE_MODEL, event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    DeleteModelFailureEvent deleteModelFailureEvent = 
(DeleteModelFailureEvent) event;
+    Assertions.assertEquals(notExistingIdent, 
deleteModelFailureEvent.identifier());
+  }
+
   @Test
   void testListModelEvent() {
     dispatcher.listModels(namespace);
@@ -234,12 +319,29 @@ public class TestModelEvent {
     Assertions.assertEquals(namespace, listModelPreEvent.namespace());
   }
 
+  @Test
+  void testListModelFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class, () -> 
failureDispatcher.listModels(namespace));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(ListModelFailureEvent.class, event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class, ((ListModelFailureEvent) 
event).exception().getClass());
+    Assertions.assertEquals(OperationType.LIST_MODEL, event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    ListModelFailureEvent listModelFailureEvent = (ListModelFailureEvent) 
event;
+    checkArray(namespace.levels(), listModelFailureEvent.namespace().levels());
+  }
+
   @Test
   void testLinkModelVersionEvent() {
     dispatcher.linkModelVersion(
         existingIdentA,
         "uriA",
-        new String[] {"aliasProduction", "aliasTest"},
+        new String[] {"aliasProduction"},
         "versionInfoA",
         ImmutableMap.of("color", "#FFFFFF"));
 
@@ -253,21 +355,35 @@ public class TestModelEvent {
     Assertions.assertEquals(existingIdentA, 
linkModelVersionPreEvent.identifier());
     ModelVersionInfo modelVersionInfo = 
linkModelVersionPreEvent.linkModelVersionRequest();
 
-    Assertions.assertEquals(1, modelVersionInfo.properties().size());
-    Assertions.assertEquals("#FFFFFF", 
modelVersionInfo.properties().get("color"));
+    checkModelVersionInfo(modelVersionInfo, firstModelVersion);
+  }
 
-    Assertions.assertEquals("uriA", modelVersionInfo.uri());
-    Assertions.assertTrue(modelVersionInfo.aliases().isPresent());
-    String[] aliases = modelVersionInfo.aliases().get();
-    Assertions.assertEquals(2, aliases.length);
-    Assertions.assertEquals("aliasProduction", aliases[0]);
-    Assertions.assertEquals("aliasTest", aliases[1]);
+  @Test
+  void testLinkModelVersionFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () ->
+            failureDispatcher.linkModelVersion(
+                existingIdentA,
+                "uriA",
+                new String[] {"aliasProduction"},
+                "versionInfoA",
+                ImmutableMap.of("color", "#FFFFFF")));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(LinkModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((LinkModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.LINK_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
 
-    Assertions.assertTrue(modelVersionInfo.comment().isPresent());
-    String comment = modelVersionInfo.comment().get();
-    Assertions.assertEquals("versionInfoA", comment);
+    LinkModelVersionFailureEvent linkModelVersionFailureEvent =
+        (LinkModelVersionFailureEvent) event;
+    ModelVersionInfo modelVersionInfo = 
linkModelVersionFailureEvent.linkModelVersionRequest();
 
-    Assertions.assertFalse(modelVersionInfo.audit().isPresent());
+    checkModelVersionInfo(modelVersionInfo, firstModelVersion);
   }
 
   @Test
@@ -282,6 +398,32 @@ public class TestModelEvent {
 
     GetModelVersionPreEvent getModelVersionPreEvent = 
(GetModelVersionPreEvent) preEvent;
     Assertions.assertEquals(existingIdentA, 
getModelVersionPreEvent.identifier());
+
+    // validate pre-event fields
+    Assertions.assertTrue(getModelVersionPreEvent.version().isPresent());
+    Assertions.assertEquals(1, getModelVersionPreEvent.version().get());
+    Assertions.assertFalse(getModelVersionPreEvent.alias().isPresent());
+  }
+
+  @Test
+  void testGetModelVersionFailureEventViaVersion() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () -> failureDispatcher.getModelVersion(existingIdentA, 3));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(GetModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((GetModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.GET_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    GetModelVersionFailureEvent getModelVersionFailureEvent = 
(GetModelVersionFailureEvent) event;
+    Assertions.assertTrue(getModelVersionFailureEvent.version().isPresent());
+    Assertions.assertEquals(3, getModelVersionFailureEvent.version().get());
+    Assertions.assertFalse(getModelVersionFailureEvent.alias().isPresent());
   }
 
   @Test
@@ -294,6 +436,7 @@ public class TestModelEvent {
     Assertions.assertEquals(OperationType.GET_MODEL_VERSION, 
preEvent.operationType());
     Assertions.assertEquals(OperationStatus.UNPROCESSED, 
preEvent.operationStatus());
 
+    // validate pre-event fields
     GetModelVersionPreEvent getModelVersionPreEvent = 
(GetModelVersionPreEvent) preEvent;
     Assertions.assertEquals(existingIdentB, 
getModelVersionPreEvent.identifier());
     Assertions.assertTrue(getModelVersionPreEvent.alias().isPresent());
@@ -301,6 +444,27 @@ public class TestModelEvent {
     Assertions.assertFalse(getModelVersionPreEvent.version().isPresent());
   }
 
+  @Test
+  void testGetModelVersionFailureEventViaAlias() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () -> failureDispatcher.getModelVersion(existingIdentB, 
"aliasNotExist"));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(GetModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((GetModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.GET_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    GetModelVersionFailureEvent getModelVersionFailureEvent = 
(GetModelVersionFailureEvent) event;
+    Assertions.assertTrue(getModelVersionFailureEvent.alias().isPresent());
+    Assertions.assertEquals("aliasNotExist", 
getModelVersionFailureEvent.alias().get());
+    Assertions.assertFalse(getModelVersionFailureEvent.version().isPresent());
+  }
+
   @Test
   void testDeleteModelVersionEventViaVersion() {
     dispatcher.deleteModelVersion(existingIdentA, 1);
@@ -318,6 +482,28 @@ public class TestModelEvent {
     Assertions.assertFalse(deleteModelVersionPreEvent.alias().isPresent());
   }
 
+  @Test
+  void testDeleteModelVersionFailureEventViaVersion() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () -> failureDispatcher.deleteModelVersion(existingIdentA, 3));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(DeleteModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((DeleteModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.DELETE_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    DeleteModelVersionFailureEvent deleteModelVersionFailureEvent =
+        (DeleteModelVersionFailureEvent) event;
+    
Assertions.assertTrue(deleteModelVersionFailureEvent.version().isPresent());
+    Assertions.assertEquals(3, deleteModelVersionFailureEvent.version().get());
+    Assertions.assertFalse(deleteModelVersionFailureEvent.alias().isPresent());
+  }
+
   @Test
   void testDeleteModelVersionEventViaAlias() {
     dispatcher.deleteModelVersion(existingIdentB, "aliasTest");
@@ -335,6 +521,28 @@ public class TestModelEvent {
     Assertions.assertFalse(deleteModelVersionPreEvent.version().isPresent());
   }
 
+  @Test
+  void testDeleteModelVersionFailureEventViaAlias() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class,
+        () -> failureDispatcher.deleteModelVersion(existingIdentB, 
"aliasNotExist"));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(DeleteModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((DeleteModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.DELETE_MODEL_VERSION, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    DeleteModelVersionFailureEvent deleteModelVersionFailureEvent =
+        (DeleteModelVersionFailureEvent) event;
+    Assertions.assertTrue(deleteModelVersionFailureEvent.alias().isPresent());
+    Assertions.assertEquals("aliasNotExist", 
deleteModelVersionFailureEvent.alias().get());
+    
Assertions.assertFalse(deleteModelVersionFailureEvent.version().isPresent());
+  }
+
   @Test
   void testListModelVersionsEvent() {
     dispatcher.listModelVersions(existingIdentA);
@@ -349,6 +557,25 @@ public class TestModelEvent {
     Assertions.assertEquals(existingIdentA, 
listModelVersionsPreEvent.identifier());
   }
 
+  @Test
+  void testListModelVersionsFailureEvent() {
+    Assertions.assertThrowsExactly(
+        GravitinoRuntimeException.class, () -> 
failureDispatcher.listModelVersions(existingIdentA));
+
+    Event event = dummyEventListener.popPostEvent();
+
+    Assertions.assertEquals(ListModelVersionFailureEvent.class, 
event.getClass());
+    Assertions.assertEquals(
+        GravitinoRuntimeException.class,
+        ((ListModelVersionFailureEvent) event).exception().getClass());
+    Assertions.assertEquals(OperationType.LIST_MODEL_VERSIONS, 
event.operationType());
+    Assertions.assertEquals(OperationStatus.FAILURE, event.operationStatus());
+
+    ListModelVersionFailureEvent listModelVersionsFailureEvent =
+        (ListModelVersionFailureEvent) event;
+    Assertions.assertEquals(existingIdentA, 
listModelVersionsFailureEvent.identifier());
+  }
+
   private ModelDispatcher mockExceptionModelDispatcher() {
     return mock(
         ModelDispatcher.class,
@@ -419,9 +646,80 @@ public class TestModelEvent {
     when(modelVersion.version()).thenReturn(1);
     when(modelVersion.uri()).thenReturn(uri);
     when(modelVersion.aliases()).thenReturn(aliases);
-    when(modelVersion.comment()).thenReturn("model version " + comment);
+    when(modelVersion.comment()).thenReturn(comment);
     when(modelVersion.properties()).thenReturn(ImmutableMap.of("color", 
"#FFFFFF"));
 
     return modelVersion;
   }
+
+  private ModelVersion getMockModelWithAudit(String uri, String[] aliases, 
String comment) {
+    ModelVersion modelVersion = mockModelVersion(uri, aliases, comment);
+    Audit mockAudit = mock(Audit.class);
+
+    when(mockAudit.creator()).thenReturn("demo_user");
+    
when(mockAudit.createTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
+    when(mockAudit.lastModifier()).thenReturn("demo_user");
+    
when(mockAudit.lastModifiedTime()).thenReturn(Instant.ofEpochMilli(1611111111111L));
+    when(modelVersion.auditInfo()).thenReturn(mockAudit);
+
+    return modelVersion;
+  }
+
+  private void checkModelInfo(ModelInfo modelInfo, Model model) {
+    // check normal fields
+    Assertions.assertEquals(model.name(), modelInfo.name());
+    Assertions.assertEquals(model.comment(), modelInfo.comment().orElse(null));
+
+    // check properties
+    checkProperties(model.properties(), modelInfo.properties());
+
+    // check audit
+    checkAudit(model.auditInfo(), modelInfo.audit());
+  }
+
+  private void checkModelVersionInfo(ModelVersionInfo modelVersionInfo, 
ModelVersion modelVersion) {
+    // check normal fields
+    Assertions.assertEquals(modelVersion.uri(), modelVersionInfo.uri());
+    Assertions.assertEquals(modelVersion.comment(), 
modelVersionInfo.comment().orElse(null));
+
+    // check aliases
+    checkArray(modelVersion.aliases(), 
modelVersionInfo.aliases().orElse(null));
+
+    // check properties
+    checkProperties(modelVersion.properties(), modelVersionInfo.properties());
+
+    // check audit
+    checkAudit(modelVersion.auditInfo(), modelVersionInfo.audit());
+  }
+
+  private void checkProperties(
+      Map<String, String> expectedProperties, Map<String, String> properties) {
+    Assertions.assertEquals(expectedProperties.size(), properties.size());
+    expectedProperties.forEach(
+        (key, value) -> {
+          Assertions.assertTrue(properties.containsKey(key));
+          Assertions.assertEquals(value, properties.get(key));
+        });
+  }
+
+  private void checkAudit(Audit expectedAudit, Optional<Audit> audit) {
+    if (expectedAudit == null) {
+      Assertions.assertFalse(audit.isPresent());
+      return;
+    }
+
+    Assertions.assertTrue(audit.isPresent());
+    Audit auditInfo = audit.get();
+
+    Assertions.assertEquals(auditInfo.creator(), auditInfo.creator());
+    Assertions.assertEquals(auditInfo.createTime(), auditInfo.createTime());
+    Assertions.assertEquals(auditInfo.lastModifier(), 
auditInfo.lastModifier());
+    Assertions.assertEquals(auditInfo.lastModifiedTime(), 
auditInfo.lastModifiedTime());
+  }
+
+  private void checkArray(String[] expected, String[] actual) {
+    Assertions.assertNotNull(actual);
+    Assertions.assertEquals(expected.length, actual.length);
+    Assertions.assertArrayEquals(expected, actual);
+  }
 }
diff --git a/docs/gravitino-server-config.md b/docs/gravitino-server-config.md
index 3f30c2f2c2..5cb4de6b6a 100644
--- a/docs/gravitino-server-config.md
+++ b/docs/gravitino-server-config.md
@@ -126,6 +126,7 @@ Gravitino triggers a pre-event before the operation, a 
post-event after the comp
 | metalake operation                  | `CreateMetalakeEvent`, 
`AlterMetalakeEvent`, `DropMetalakeEvent`, `LoadMetalakeEvent`, 
`ListMetalakeEvent`, `CreateMetalakeFailureEvent`, `AlterMetalakeFailureEvent`, 
`DropMetalakeFailureEvent`, `LoadMetalakeFailureEvent`, 
`ListMetalakeFailureEvent`                                                      
                                                                                
                               | 0.5.0            |
 | Iceberg REST server table operation | `IcebergCreateTableEvent`, 
`IcebergUpdateTableEvent`, `IcebergDropTableEvent`, `IcebergLoadTableEvent`, 
`IcebergListTableEvent`, `IcebergTableExistsEvent`, `IcebergRenameTableEvent`, 
`IcebergCreateTableFailureEvent`, `IcebergUpdateTableFailureEvent`, 
`IcebergDropTableFailureEvent`, `IcebergLoadTableFailureEvent`, 
`IcebergListTableFailureEvent`, `IcebergRenameTableFailureEvent`, 
`IcebergTableExistsFailureEvent` | 0.7.0-incubating |
 | tag operation                       | `ListTagsEvent`, `ListTagsInfoEvent`, 
`CreateTagEvent`, `GetTagEvent`, `AlterTagEvent`, `DeleteTagEvent`, 
`ListMetadataObjectsForTagEvent`, `ListTagsForMetadataObjectEvent`, 
`ListTagsInfoForMetadataObjectEvent`, `AssociateTagsForMetadataObjectEvent`, 
`GetTagForMetadataObjectEvent`, `ListTagsFailureEvent`, 
`ListTagInfoFailureEvent`, `CreateTagFailureEvent`, `GetTagFailureEvent`, 
`AlterTagFailureEvent`, `DeleteTagFailureEvent`, `ListMetadataObjectsFo [...]
+| model operation                     | `DeleteModelFailureEvent`, 
`DeleteModelVersionFailureEvent`, `GetModelFailureEvent`, 
`GetModelVersionFailureEvent`, `LinkModelVersionFailureEvent`, 
`ListModelFailureEvent`, `ListModelVersionFailureEvent`, 
`RegisterAndLinkModelFailureEvent`, `RegisterModelFailureEvent`                 
                                                                                
                                                                                
        [...]
 
 ##### Pre-event
 

Reply via email to