You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by vi...@apache.org on 2018/03/27 22:52:32 UTC

hive git commit: HIVE-18885 : DbNotificationListener has a deadlock between Java and DB locks (2.x line) (Vihang Karajgaonkar, reviewed by Alexander Kolbasov and Peter Vary)

Repository: hive
Updated Branches:
  refs/heads/branch-2 5842fd2f1 -> 29ad069c3


HIVE-18885 : DbNotificationListener has a deadlock between Java and DB locks (2.x line) (Vihang Karajgaonkar, reviewed by Alexander Kolbasov and Peter Vary)


Project: http://git-wip-us.apache.org/repos/asf/hive/repo
Commit: http://git-wip-us.apache.org/repos/asf/hive/commit/29ad069c
Tree: http://git-wip-us.apache.org/repos/asf/hive/tree/29ad069c
Diff: http://git-wip-us.apache.org/repos/asf/hive/diff/29ad069c

Branch: refs/heads/branch-2
Commit: 29ad069c35821ed8d3a79c64cd6be5f5b0024ea6
Parents: 5842fd2
Author: Vihang Karajgaonkar <vi...@cloudera.com>
Authored: Tue Mar 27 15:47:53 2018 -0700
Committer: Vihang Karajgaonkar <vi...@cloudera.com>
Committed: Tue Mar 27 15:47:53 2018 -0700

----------------------------------------------------------------------
 .../listener/DbNotificationListener.java        | 59 ++++++++++++--------
 1 file changed, 35 insertions(+), 24 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/hive/blob/29ad069c/hcatalog/server-extensions/src/main/java/org/apache/hive/hcatalog/listener/DbNotificationListener.java
----------------------------------------------------------------------
diff --git a/hcatalog/server-extensions/src/main/java/org/apache/hive/hcatalog/listener/DbNotificationListener.java b/hcatalog/server-extensions/src/main/java/org/apache/hive/hcatalog/listener/DbNotificationListener.java
index 41347c2..0cf51fb 100644
--- a/hcatalog/server-extensions/src/main/java/org/apache/hive/hcatalog/listener/DbNotificationListener.java
+++ b/hcatalog/server-extensions/src/main/java/org/apache/hive/hcatalog/listener/DbNotificationListener.java
@@ -68,23 +68,38 @@ import org.slf4j.LoggerFactory;
 import com.google.common.collect.Lists;
 
 /**
- * An implementation of {@link org.apache.hadoop.hive.metastore.MetaStoreEventListener} that
- * stores events in the database.
+ * <p>This listener takes a ListenerEvent, builds a NotificationEvent,
+ * and stores it in the database.This class can be configured as a listener (transaction or non-transactional) so that the listener
+ * events are logged into the database.
+ * </p>
  *
- * Design overview:  This listener takes any event, builds a NotificationEventResponse,
- * and puts it on a queue.  There is a dedicated thread that reads entries from the queue and
- * places them in the database.  The reason for doing it in a separate thread is that we want to
- * avoid slowing down other metadata operations with the work of putting the notification into
- * the database.  Also, occasionally the thread needs to clean the database of old records.  We
- * definitely don't want to do that as part of another metadata operation.
+ * <p>Design overview: This class implements{@link org.apache.hadoop.hive.metastore.MetaStoreEventListener}
+ * When a NotificationEvent is created, an unique and monotonically
+ * increasing EVENT_ID is generated from the database and it assigned to each NotificationEvent.
+ * It is important to note that this Listener can be configured as a transaction listener
+ * in cases where client applications rely on the fact that the event is generated only when
+ * the metadata transaction is successful. Each NotificationEvent requires a EVENT_ID which is generated
+ * using a RW row lock in the database in the method RawStore.addNotificationEvent. This means any
+ * other concurrent transaction thread trying to create a event at the same time will block until the
+ * lock is released. This is required to satisfy the strict constraints (monotonically increasing event id,
+ * with no holes and generated only when transaction successful) on the client side
+ * </p>
+ * <p>
+ * Also, there is a cleaner thread which deletes old notification events at a regular interval. The
+ * time-to-live for the Notification Events can be configured by setting
+ * hive.metastore.event.db.listener.timetolive appropriately
+ * </p>
+ * <p>TODO : The code to generate EVENT_ID gets a database R/W row lock using SELECT FOR UPDATE for the single row
+ * on the NOTIFICATION_SEQUENCE table which blocks all the other metadata transactions until the
+ * event is committed along with parent transaction. This is likely to cause performance problem and the
+ * design needs to be improved
+ * </p>
  */
 public class DbNotificationListener extends MetaStoreEventListener {
 
   private static final Logger LOG = LoggerFactory.getLogger(DbNotificationListener.class.getName());
   private static CleanerThread cleaner = null;
 
-  private static final Object NOTIFICATION_TBL_LOCK = new Object();
-
   // This is the same object as super.conf, but it's convenient to keep a copy of it as a
   // HiveConf rather than a Configuration.
   private HiveConf hiveConf;
@@ -478,19 +493,17 @@ public class DbNotificationListener extends MetaStoreEventListener {
    *                      DB_NOTIFICATION_EVENT_ID_KEY_NAME for future reference by other listeners.
    */
   private void process(NotificationEvent event, ListenerEvent listenerEvent) throws MetaException {
+    //no need for a synchronized block since synchronization accross multiple threads is done
+    //at database level in addNotificationEvent method.
     event.setMessageFormat(msgFactory.getMessageFormat());
-    synchronized (NOTIFICATION_TBL_LOCK) {
-      LOG.debug("DbNotificationListener: Processing : {}:{}", event.getEventId(),
-          event.getMessage());
-      HMSHandler.getMSForConf(hiveConf).addNotificationEvent(event);
-    }
+    LOG.debug("DbNotificationListener: Processing : {}:{}", event.getEventId(), event.getMessage());
+    HMSHandler.getMSForConf(hiveConf).addNotificationEvent(event);
 
-      // Set the DB_NOTIFICATION_EVENT_ID for future reference by other listeners.
-      if (event.isSetEventId()) {
-        listenerEvent.putParameter(
-            MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME,
-            Long.toString(event.getEventId()));
-      }
+    // Set the DB_NOTIFICATION_EVENT_ID for future reference by other listeners.
+    if (event.isSetEventId()) {
+      listenerEvent.putParameter(MetaStoreEventListenerConstants.DB_NOTIFICATION_EVENT_ID_KEY_NAME,
+          Long.toString(event.getEventId()));
+    }
   }
 
   private static class CleanerThread extends Thread {
@@ -509,9 +522,7 @@ public class DbNotificationListener extends MetaStoreEventListener {
     @Override
     public void run() {
       while (true) {
-        synchronized(NOTIFICATION_TBL_LOCK) {
-          rs.cleanNotificationEvents(ttl);
-        }
+        rs.cleanNotificationEvents(ttl);
         LOG.debug("Cleaner thread done");
         try {
           Thread.sleep(sleepTime);