You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sentry.apache.org by ha...@apache.org on 2017/01/27 23:20:49 UTC

[2/7] sentry git commit: SENTRY-1536

SENTRY-1536

Change-Id: I1d7d21891692c3dd7557b4e5f90d9b0c35d820bd


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

Branch: refs/heads/sentry-ha-redesign-1
Commit: ee2d3f7a2ebc409f74f7de07fd316fe74f03dd11
Parents: d5176b2
Author: hahao <ha...@cloudera.com>
Authored: Thu Jan 26 17:29:39 2017 -0800
Committer: hahao <ha...@cloudera.com>
Committed: Thu Jan 26 17:29:39 2017 -0800

----------------------------------------------------------------------
 .../org/apache/sentry/hdfs/PathsUpdate.java     |  26 +
 .../apache/sentry/hdfs/PermissionsUpdate.java   |  27 +
 .../sentry/hdfs/UpdateableAuthzPermissions.java |   1 +
 .../org/apache/sentry/hdfs/SentryPlugin.java    |  87 ++-
 .../provider/db/SentryPolicyStorePlugin.java    |  29 +-
 .../service/persistent/DelegateSentryStore.java |  79 +--
 .../db/service/model/MSentryPathChange.java     |  30 +-
 .../db/service/model/MSentryPermChange.java     |  28 +-
 .../provider/db/service/model/package.jdo       |   4 +-
 .../persistent/DeltaTransactionBlock.java       | 105 ++++
 .../db/service/persistent/SentryStore.java      | 549 +++++++++++++++++--
 .../service/persistent/TransactionManager.java  |  95 +++-
 .../thrift/SentryPolicyStoreProcessor.java      | 103 ++--
 .../sentry/service/thrift/HMSFollower.java      |  49 +-
 .../db/service/persistent/TestSentryStore.java  | 209 +++++++
 .../persistent/TestSentryStoreImportExport.java |   2 +-
 .../sentry/service/thrift/TestHMSFollower.java  |  13 +-
 17 files changed, 1219 insertions(+), 217 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PathsUpdate.java
----------------------------------------------------------------------
diff --git a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PathsUpdate.java b/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PathsUpdate.java
index 9ecd9e4..ffb0756 100644
--- a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PathsUpdate.java
+++ b/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PathsUpdate.java
@@ -173,4 +173,30 @@ public class PathsUpdate implements Updateable.Update {
     return ThriftSerializer.serializeToJSON(tPathsUpdate);
   }
 
+  @Override
+  public int hashCode() {
+    return (tPathsUpdate == null) ? 0 : tPathsUpdate.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+
+    if (this == obj) {
+      return true;
+    }
+
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+
+    PathsUpdate other = (PathsUpdate) obj;
+    if (tPathsUpdate == null) {
+      return other.tPathsUpdate == null;
+    }
+    return tPathsUpdate.equals(other.tPathsUpdate);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PermissionsUpdate.java
----------------------------------------------------------------------
diff --git a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PermissionsUpdate.java b/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PermissionsUpdate.java
index a346587..14a4a0f 100644
--- a/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PermissionsUpdate.java
+++ b/sentry-hdfs/sentry-hdfs-common/src/main/java/org/apache/sentry/hdfs/PermissionsUpdate.java
@@ -117,4 +117,31 @@ public class PermissionsUpdate implements Updateable.Update {
   public String JSONSerialize() throws TException {
     return ThriftSerializer.serializeToJSON(tPermUpdate);
   }
+
+  @Override
+  public int hashCode() {
+    return (tPermUpdate == null) ? 0 : tPermUpdate.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+
+    if (this == obj) {
+      return true;
+    }
+
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+
+    PermissionsUpdate other = (PermissionsUpdate) obj;
+    if (tPermUpdate == null) {
+      return other.tPermUpdate == null;
+    }
+    return tPermUpdate.equals(other.tPermUpdate);
+  }
+
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/UpdateableAuthzPermissions.java
----------------------------------------------------------------------
diff --git a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/UpdateableAuthzPermissions.java b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/UpdateableAuthzPermissions.java
index 2472928..766611c 100644
--- a/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/UpdateableAuthzPermissions.java
+++ b/sentry-hdfs/sentry-hdfs-namenode-plugin/src/main/java/org/apache/sentry/hdfs/UpdateableAuthzPermissions.java
@@ -217,6 +217,7 @@ public class UpdateableAuthzPermissions implements AuthzPermissions, Updateable<
 
   @Override
   public PermissionsUpdate createFullImageUpdate(long currSeqNum) {
+    // Using in-memory cache perms to create a full permission snapshot.
     PermissionsUpdate retVal = new PermissionsUpdate(currSeqNum, true);
     for (PrivilegeInfo pInfo : perms.getAllPrivileges()) {
       TPrivilegeChanges pUpdate = retVal.addPrivilegeUpdate(pInfo.getAuthzObj());

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
----------------------------------------------------------------------
diff --git a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
index 47c9f9d..4c3e9d6 100644
--- a/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
+++ b/sentry-hdfs/sentry-hdfs-service/src/main/java/org/apache/sentry/hdfs/SentryPlugin.java
@@ -34,6 +34,7 @@ import org.apache.sentry.hdfs.service.thrift.TPermissionsUpdate;
 import org.apache.sentry.hdfs.service.thrift.TPrivilegeChanges;
 import org.apache.sentry.hdfs.service.thrift.TRoleChanges;
 import org.apache.sentry.provider.db.SentryPolicyStorePlugin;
+import org.apache.sentry.provider.db.service.persistent.DeltaTransactionBlock;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsRequest;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
@@ -162,12 +163,13 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
 
   private UpdateForwarder<PathsUpdate> pathsUpdater;
   private UpdateForwarder<PermissionsUpdate> permsUpdater;
+  // TODO: Each perm change sequence number should be generated during persistence at sentry store.
   private final AtomicLong permSeqNum = new AtomicLong(5);
   private PermImageRetriever permImageRetriever;
   private boolean outOfSync = false;
   /*
    * This number is smaller than starting sequence numbers used by NN and HMS
-   * so in both cases its effect is to creat appearence of out-of-sync
+   * so in both cases its effect is to create appearance of out-of-sync
    * updates on the Sentry server (as if there were no previous updates at all).
    * It, in turn, triggers a) pushing full update from HMS to Sentry and
    * b) pulling full update from Sentry to NameNode.
@@ -287,19 +289,21 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
   }
 
   @Override
-  public void onAlterSentryRoleAddGroups(
+  public DeltaTransactionBlock onAlterSentryRoleAddGroups(
       TAlterSentryRoleAddGroupsRequest request) throws SentryPluginException {
     PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
     TRoleChanges rUpdate = update.addRoleUpdate(request.getRoleName());
     for (TSentryGroup group : request.getGroups()) {
       rUpdate.addToAddGroups(group.getGroupName());
     }
+
     permsUpdater.handleUpdateNotification(update);
     LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + request.getRoleName() + "]..");
+    return new DeltaTransactionBlock(update);
   }
 
   @Override
-  public void onAlterSentryRoleDeleteGroups(
+  public DeltaTransactionBlock onAlterSentryRoleDeleteGroups(
       TAlterSentryRoleDeleteGroupsRequest request)
           throws SentryPluginException {
     PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
@@ -307,38 +311,50 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
     for (TSentryGroup group : request.getGroups()) {
       rUpdate.addToDelGroups(group.getGroupName());
     }
+
     permsUpdater.handleUpdateNotification(update);
     LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + request.getRoleName() + "]..");
+    return new DeltaTransactionBlock(update);
   }
 
   @Override
-  public void onAlterSentryRoleGrantPrivilege(
-      TAlterSentryRoleGrantPrivilegeRequest request)
-          throws SentryPluginException {
+  public void onAlterSentryRoleGrantPrivilege(TAlterSentryRoleGrantPrivilegeRequest request,
+          Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap) throws SentryPluginException {
+
     if (request.isSetPrivileges()) {
       String roleName = request.getRoleName();
+
       for (TSentryPrivilege privilege : request.getPrivileges()) {
         if(!("COLUMN".equalsIgnoreCase(privilege.getPrivilegeScope()))) {
-          onAlterSentryRoleGrantPrivilegeCore(roleName, privilege);
+          PermissionsUpdate update = onAlterSentryRoleGrantPrivilegeCore(roleName, privilege);
+          DeltaTransactionBlock tb = new DeltaTransactionBlock(update);
+
+          if (update != null && privilegesUpdateMap != null) {
+            privilegesUpdateMap.put(privilege, tb);
+          }
         }
       }
     }
   }
 
-  private void onAlterSentryRoleGrantPrivilegeCore(String roleName, TSentryPrivilege privilege)
+  private PermissionsUpdate onAlterSentryRoleGrantPrivilegeCore(String roleName, TSentryPrivilege privilege)
       throws SentryPluginException {
     String authzObj = getAuthzObj(privilege);
-    if (authzObj != null) {
-      PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
-      update.addPrivilegeUpdate(authzObj).putToAddPrivileges(
-          roleName, privilege.getAction().toUpperCase());
-      permsUpdater.handleUpdateNotification(update);
-      LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + "]..");
+    if (authzObj == null) {
+      return null;
     }
+
+    PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
+    update.addPrivilegeUpdate(authzObj).putToAddPrivileges(
+        roleName, privilege.getAction().toUpperCase());
+
+    permsUpdater.handleUpdateNotification(update);
+    LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + "]..");
+    return update;
   }
 
   @Override
-  public void onRenameSentryPrivilege(TRenamePrivilegesRequest request)
+  public DeltaTransactionBlock onRenameSentryPrivilege(TRenamePrivilegesRequest request)
       throws SentryPluginException {
     String oldAuthz = getAuthzObj(request.getOldAuthorizable());
     String newAuthz = getAuthzObj(request.getNewAuthorizable());
@@ -346,19 +362,28 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
     TPrivilegeChanges privUpdate = update.addPrivilegeUpdate(PermissionsUpdate.RENAME_PRIVS);
     privUpdate.putToAddPrivileges(newAuthz, newAuthz);
     privUpdate.putToDelPrivileges(oldAuthz, oldAuthz);
+
     permsUpdater.handleUpdateNotification(update);
     LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + newAuthz + ", " + oldAuthz + "]..");
+    return new DeltaTransactionBlock(update);
   }
 
   @Override
-  public void onAlterSentryRoleRevokePrivilege(
-      TAlterSentryRoleRevokePrivilegeRequest request)
+  public void onAlterSentryRoleRevokePrivilege(TAlterSentryRoleRevokePrivilegeRequest request,
+      Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap)
           throws SentryPluginException {
+
     if (request.isSetPrivileges()) {
       String roleName = request.getRoleName();
+
       for (TSentryPrivilege privilege : request.getPrivileges()) {
         if(!("COLUMN".equalsIgnoreCase(privilege.getPrivilegeScope()))) {
-          onAlterSentryRoleRevokePrivilegeCore(roleName, privilege);
+          PermissionsUpdate update = onAlterSentryRoleRevokePrivilegeCore(roleName, privilege);
+          DeltaTransactionBlock tb = new DeltaTransactionBlock(update);
+
+          if (update != null && privilegesUpdateMap != null) {
+            privilegesUpdateMap.put(privilege, tb);
+          }
         }
       }
     }
@@ -372,38 +397,46 @@ public class SentryPlugin implements SentryPolicyStorePlugin, SigUtils.SigListen
     this.outOfSync = outOfSync;
   }
 
-  private void onAlterSentryRoleRevokePrivilegeCore(String roleName, TSentryPrivilege privilege)
+  private PermissionsUpdate onAlterSentryRoleRevokePrivilegeCore(String roleName, TSentryPrivilege privilege)
       throws SentryPluginException {
     String authzObj = getAuthzObj(privilege);
-    if (authzObj != null) {
-      PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
-      update.addPrivilegeUpdate(authzObj).putToDelPrivileges(
-          roleName, privilege.getAction().toUpperCase());
-      permsUpdater.handleUpdateNotification(update);
-      LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + authzObj + "]..");
+    if (authzObj == null) {
+      return null;
     }
+
+    PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
+    update.addPrivilegeUpdate(authzObj).putToDelPrivileges(
+        roleName, privilege.getAction().toUpperCase());
+
+    permsUpdater.handleUpdateNotification(update);
+    LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + authzObj + "]..");
+    return update;
   }
 
   @Override
-  public void onDropSentryRole(TDropSentryRoleRequest request)
+  public DeltaTransactionBlock onDropSentryRole(TDropSentryRoleRequest request)
       throws SentryPluginException {
     PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
     update.addPrivilegeUpdate(PermissionsUpdate.ALL_AUTHZ_OBJ).putToDelPrivileges(
         request.getRoleName(), PermissionsUpdate.ALL_AUTHZ_OBJ);
     update.addRoleUpdate(request.getRoleName()).addToDelGroups(PermissionsUpdate.ALL_GROUPS);
+
     permsUpdater.handleUpdateNotification(update);
     LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + request.getRoleName() + "]..");
+    return new DeltaTransactionBlock(update);
   }
 
   @Override
-  public void onDropSentryPrivilege(TDropPrivilegesRequest request)
+  public DeltaTransactionBlock onDropSentryPrivilege(TDropPrivilegesRequest request)
       throws SentryPluginException {
     PermissionsUpdate update = new PermissionsUpdate(permSeqNum.incrementAndGet(), false);
     String authzObj = getAuthzObj(request.getAuthorizable());
     update.addPrivilegeUpdate(authzObj).putToDelPrivileges(
         PermissionsUpdate.ALL_ROLES, PermissionsUpdate.ALL_ROLES);
+
     permsUpdater.handleUpdateNotification(update);
     LOGGER.debug("Authz Perm preUpdate [" + update.getSeqNum() + ", " + authzObj + "]..");
+    return new DeltaTransactionBlock(update);
   }
 
   @Override

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
index 2ff715f..0b34e36 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/SentryPolicyStorePlugin.java
@@ -20,6 +20,7 @@ package org.apache.sentry.provider.db;
 
 import org.apache.hadoop.conf.Configuration;
 import org.apache.sentry.core.common.exception.SentryUserException;
+import org.apache.sentry.provider.db.service.persistent.DeltaTransactionBlock;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleAddGroupsRequest;
 import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleDeleteGroupsRequest;
@@ -28,7 +29,18 @@ import org.apache.sentry.provider.db.service.thrift.TAlterSentryRoleRevokePrivil
 import org.apache.sentry.provider.db.service.thrift.TDropPrivilegesRequest;
 import org.apache.sentry.provider.db.service.thrift.TDropSentryRoleRequest;
 import org.apache.sentry.provider.db.service.thrift.TRenamePrivilegesRequest;
+import org.apache.sentry.provider.db.service.thrift.TSentryPrivilege;
 
+import java.util.Map;
+
+/**
+ * Interface for processing delta changes of Sentry permission and generate corresponding
+ * update. The updates will be persisted into Sentry store afterwards along with the actual
+ * operation.
+ *
+ * TODO: SENTRY-1588: add user level privilege change support. e.g. onAlterSentryRoleDeleteUsers,
+ * TODO: onAlterSentryRoleDeleteUsers.
+ */
 public interface SentryPolicyStorePlugin {
 
   @SuppressWarnings("serial")
@@ -43,18 +55,19 @@ public interface SentryPolicyStorePlugin {
 
   void initialize(Configuration conf, SentryStore sentryStore) throws SentryPluginException;
 
-  void onAlterSentryRoleAddGroups(TAlterSentryRoleAddGroupsRequest tRequest) throws SentryPluginException;
-
-  void onAlterSentryRoleDeleteGroups(TAlterSentryRoleDeleteGroupsRequest tRequest) throws SentryPluginException;
+  DeltaTransactionBlock onAlterSentryRoleAddGroups(TAlterSentryRoleAddGroupsRequest tRequest) throws SentryPluginException;
 
-  void onAlterSentryRoleGrantPrivilege(TAlterSentryRoleGrantPrivilegeRequest tRequest) throws SentryPluginException;
+  DeltaTransactionBlock onAlterSentryRoleDeleteGroups(TAlterSentryRoleDeleteGroupsRequest tRequest) throws SentryPluginException;
 
-  void onAlterSentryRoleRevokePrivilege(TAlterSentryRoleRevokePrivilegeRequest tRequest) throws SentryPluginException;
+  void onAlterSentryRoleGrantPrivilege(TAlterSentryRoleGrantPrivilegeRequest tRequest,
+        Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap) throws SentryPluginException;
 
-  void onDropSentryRole(TDropSentryRoleRequest tRequest) throws SentryPluginException;
+  void onAlterSentryRoleRevokePrivilege(TAlterSentryRoleRevokePrivilegeRequest tRequest,
+        Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap) throws SentryPluginException;
 
-  void onRenameSentryPrivilege(TRenamePrivilegesRequest request) throws SentryPluginException;
+  DeltaTransactionBlock onDropSentryRole(TDropSentryRoleRequest tRequest) throws SentryPluginException;
 
-  void onDropSentryPrivilege(TDropPrivilegesRequest request) throws SentryPluginException;
+  DeltaTransactionBlock onRenameSentryPrivilege(TRenamePrivilegesRequest request) throws SentryPluginException;
 
+  DeltaTransactionBlock onDropSentryPrivilege(TDropPrivilegesRequest request) throws SentryPluginException;
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java
index 8e2a6d5..1171a89 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/generic/service/persistent/DelegateSentryStore.java
@@ -36,6 +36,7 @@ import org.apache.sentry.core.common.exception.SentryNoSuchObjectException;
 import org.apache.sentry.provider.db.service.model.MSentryGMPrivilege;
 import org.apache.sentry.provider.db.service.model.MSentryGroup;
 import org.apache.sentry.provider.db.service.model.MSentryRole;
+import org.apache.sentry.provider.db.service.persistent.DeltaTransactionBlock;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.persistent.TransactionBlock;
 import org.apache.sentry.provider.db.service.thrift.SentryPolicyStoreProcessor;
@@ -92,7 +93,10 @@ public class DelegateSentryStore implements SentryStoreLayer {
   @Override
   public Object dropRole(final String component, final String role, final String requestor)
       throws Exception {
-    delegate.dropSentryRole(toTrimmedLower(role));
+    // UpdateTransactionBlock is null for generic model. As we are
+    // only tracking permission update of Hive model for HDFS sync.
+    DeltaTransactionBlock deltaTransactionBlock = null;
+    delegate.dropSentryRole(toTrimmedLower(role), deltaTransactionBlock);
     return null;
   }
 
@@ -104,15 +108,22 @@ public class DelegateSentryStore implements SentryStoreLayer {
   @Override
   public Object alterRoleAddGroups(String component, String role,
       Set<String> groups, String requestor) throws Exception {
-    delegate.alterSentryRoleAddGroups(requestor, role, toTSentryGroups(groups));
+    // UpdateTransactionBlock is null for generic model. As we are
+    // only tracking permission update of Hive model for HDFS sync.
+    DeltaTransactionBlock deltaTransactionBlock = null;
+    delegate.alterSentryRoleAddGroups(requestor, role, toTSentryGroups(groups),
+        deltaTransactionBlock);
     return null;
   }
 
   @Override
   public Object alterRoleDeleteGroups(String component, String role,
       Set<String> groups, String requestor) throws Exception {
-  //called to old sentryStore
-    delegate.alterSentryRoleDeleteGroups(role, toTSentryGroups(groups));
+    // Called to old sentryStore. UpdateTransactionBlock is null for generic model.
+    // As we are only tracking permission update of Hive model for HDFS sync.
+    DeltaTransactionBlock deltaTransactionBlock = null;
+    delegate.alterSentryRoleDeleteGroups(role, toTSentryGroups(groups),
+        deltaTransactionBlock);
     return null;
   }
 
@@ -120,23 +131,21 @@ public class DelegateSentryStore implements SentryStoreLayer {
   public Object alterRoleGrantPrivilege(final String component, final String role,
       final PrivilegeObject privilege, final String grantorPrincipal)
       throws Exception {
-    delegate.getTransactionManager().executeTransactionWithRetry(
-        new TransactionBlock() {
-          public Object execute(PersistenceManager pm) throws Exception {
-            String trimmedRole = toTrimmedLower(role);
-            MSentryRole mRole = getRole(trimmedRole, pm);
-            if (mRole == null) {
-              throw new SentryNoSuchObjectException("Role: " + trimmedRole + " doesn't exist");
-            }
-            /*
-             * check with grant option
-             */
-            grantOptionCheck(privilege, grantorPrincipal, pm);
+    delegate.getTransactionManager().executeTransactionWithRetry(new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        String trimmedRole = toTrimmedLower(role);
+        MSentryRole mRole = getRole(trimmedRole, pm);
+        if (mRole == null) {
+          throw new SentryNoSuchObjectException("Role: " + trimmedRole + " doesn't exist");
+        }
 
-            privilegeOperator.grantPrivilege(privilege, mRole, pm);
-            return null;
-          }
-        });
+        // check with grant option
+        grantOptionCheck(privilege, grantorPrincipal, pm);
+
+        privilegeOperator.grantPrivilege(privilege, mRole, pm);
+        return null;
+      }
+    });
     return null;
   }
 
@@ -144,23 +153,21 @@ public class DelegateSentryStore implements SentryStoreLayer {
   public Object alterRoleRevokePrivilege(final String component,
       final String role, final PrivilegeObject privilege, final String grantorPrincipal)
       throws Exception {
-    delegate.getTransactionManager().executeTransactionWithRetry(
-        new TransactionBlock() {
-          public Object execute(PersistenceManager pm) throws Exception {
-            String trimmedRole = toTrimmedLower(role);
-            MSentryRole mRole = getRole(trimmedRole, pm);
-            if (mRole == null) {
-              throw new SentryNoSuchObjectException("Role: " + trimmedRole + " doesn't exist");
-            }
-            /*
-             * check with grant option
-             */
-            grantOptionCheck(privilege, grantorPrincipal, pm);
+    delegate.getTransactionManager().executeTransactionWithRetry(new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        String trimmedRole = toTrimmedLower(role);
+        MSentryRole mRole = getRole(trimmedRole, pm);
+        if (mRole == null) {
+          throw new SentryNoSuchObjectException("Role: " + trimmedRole + " doesn't exist");
+        }
 
-            privilegeOperator.revokePrivilege(privilege, mRole, pm);
-            return null;
-          }
-        });
+        // check with grant option
+        grantOptionCheck(privilege, grantorPrincipal, pm);
+
+        privilegeOperator.revokePrivilege(privilege, mRole, pm);
+        return null;
+      }
+    });
     return null;
   }
 

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPathChange.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPathChange.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPathChange.java
index b88e7d1..0738589 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPathChange.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPathChange.java
@@ -63,24 +63,15 @@ public class MSentryPathChange {
   private String pathChange;
   private long createTimeMs;
 
-  public MSentryPathChange(long changeID, String pathChange, long createTime) {
-    this.changeID = changeID;
+  public MSentryPathChange(String pathChange) {
     this.pathChange = pathChange;
-    this.createTimeMs = createTime;
-  }
-
-  public void setCreateTimeMs(long createTimeMs) {
-    this.createTimeMs = createTimeMs;
+    this.createTimeMs = System.currentTimeMillis();
   }
 
   public long getCreateTimeMs() {
     return createTimeMs;
   }
 
-  public void setPathChange(String pathChange) {
-    this.pathChange = pathChange;
-  }
-
   public String getPathChange() {
     return pathChange;
   }
@@ -89,13 +80,10 @@ public class MSentryPathChange {
     return changeID;
   }
 
-  public void setChangeID(long changeID) {
-    this.changeID = changeID;
-  }
-
   @Override
   public String toString() {
-    return "MSentryChange [changeID=" + changeID + " , pathChange= " + pathChange + ", createTime=" + createTimeMs +  "]";
+    return "MSentryChange [changeID=" + changeID + " , pathChange= " + pathChange +
+        ", createTime=" + createTimeMs +  "]";
   }
 
   @Override
@@ -126,14 +114,14 @@ public class MSentryPathChange {
       return false;
     }
 
-    if (!pathChange.equals(other.pathChange)) {
+    if (createTimeMs != other.createTimeMs) {
       return false;
     }
 
-    if (createTimeMs != other.createTimeMs) {
-      return false;
+    if (pathChange == null) {
+      return other.pathChange == null;
     }
 
-    return true;
+    return pathChange.equals(other.pathChange);
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPermChange.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPermChange.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPermChange.java
index 2ccace0..2100561 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPermChange.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/MSentryPermChange.java
@@ -61,24 +61,15 @@ public class MSentryPermChange {
   private String permChange;
   private long createTimeMs;
 
-  public MSentryPermChange(long changeID, String permChange, long createTimeMs) {
-    this.changeID = changeID;
+  public MSentryPermChange(String permChange) {
     this.permChange = permChange;
-    this.createTimeMs = createTimeMs;
-  }
-
-  public void setCreateTimeMs(long createTimeMs) {
-    this.createTimeMs = createTimeMs;
+    this.createTimeMs = System.currentTimeMillis();
   }
 
   public long getCreateTimeMs() {
     return createTimeMs;
   }
 
-  public void setPermChange(String permChange) {
-    this.permChange = permChange;
-  }
-
   public String getPermChange() {
     return permChange;
   }
@@ -87,13 +78,10 @@ public class MSentryPermChange {
     return changeID;
   }
 
-  public void setChangeID(long changeID) {
-    this.changeID = changeID;
-  }
-
   @Override
   public String toString() {
-    return "MSentryPermChange [changeID=" + changeID + ", permChange= " + permChange + ", createTimeMs=" + createTimeMs +  "]";
+    return "MSentryPermChange [changeID=" + changeID + ", permChange= " + permChange +
+        ", createTimeMs=" + createTimeMs +  "]";
   }
 
   @Override
@@ -128,10 +116,10 @@ public class MSentryPermChange {
       return false;
     }
 
-    if (!permChange.equals(other.permChange)) {
-      return false;
+    if (permChange == null) {
+      return other.permChange == null;
     }
 
-    return true;
+    return permChange.equals(other.permChange);
   }
-}
\ No newline at end of file
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
index dc8fdbf..94ede1d 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/model/package.jdo
@@ -263,7 +263,7 @@
      </class>
 
      <class name="MSentryPermChange" table="SENTRY_PERM_CHANGE" identity-type="application" detachable="true">
-       <field name="changeID" primary-key="true">
+       <field name="changeID" primary-key="true" value-strategy="increment">
          <column name="CHANGE_ID" jdbc-type="BIGINT" allows-null="false"/>
        </field>
        <field name ="permChange">
@@ -275,7 +275,7 @@
      </class>
 
      <class name="MSentryPathChange" table="SENTRY_PATH_CHANGE" identity-type="application" detachable="true">
-       <field name="changeID" primary-key="true">
+       <field name="changeID" primary-key="true" value-strategy="increment">
          <column name="CHANGE_ID" jdbc-type="BIGINT" allows-null="false"/>
        </field>
        <field name ="pathChange">

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/DeltaTransactionBlock.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/DeltaTransactionBlock.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/DeltaTransactionBlock.java
new file mode 100644
index 0000000..61145ab
--- /dev/null
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/DeltaTransactionBlock.java
@@ -0,0 +1,105 @@
+/**
+ * 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.sentry.provider.db.service.persistent;
+
+import org.apache.sentry.hdfs.PathsUpdate;
+import org.apache.sentry.hdfs.PermissionsUpdate;
+import org.apache.sentry.provider.db.service.model.MSentryPathChange;
+import org.apache.sentry.provider.db.service.model.MSentryPermChange;
+import static org.apache.sentry.hdfs.Updateable.Update;
+
+import javax.jdo.PersistenceManager;
+
+/**
+ * A subclass of {@link TransactionBlock} to manage the code should be executed for
+ * each delta update. The update could be {@link PathsUpdate} or {@link PermissionsUpdate}.
+ * Based on update type, the update would be persisted into corresponding update table,
+ * e.g {@link MSentryPathChange} {@link MSentryPermChange}.
+ * <p>
+ * Delta update should not have full image, hence update contains full image would not
+ * be executed.
+ */
+public class DeltaTransactionBlock implements TransactionBlock {
+  private final Update update;
+
+  public DeltaTransactionBlock(Update update) {
+    this.update = update;
+  }
+
+  @Override
+  public Object execute(PersistenceManager pm) throws Exception {
+    persistUpdate(pm, update);
+    return null;
+  }
+
+  /**
+   * Persist the delta change into corresponding type based on its type.
+   * Atomic increasing primary key changeID by 1. Return without any
+   * operation if update is null or update is a fullImage.
+   *
+   * @param pm PersistenceManager
+   * @param update update
+   * @throws Exception
+   */
+  private void persistUpdate(PersistenceManager pm, Update update)
+      throws Exception {
+
+    // persistUpdate cannot handle full image update, instead
+    // it only handles delta updates.
+    if (update == null || update.hasFullImage()) {
+      return;
+    }
+
+    // Persist the update into corresponding tables based on its type.
+    // changeID is the primary key in MSentryPXXXChange table. If same
+    // changeID is trying to be persisted twice, the transaction would
+    // fail.
+    if (update instanceof PermissionsUpdate) {
+      pm.makePersistent(new MSentryPermChange(update.JSONSerialize()));
+    } else if (update instanceof PathsUpdate) {
+      pm.makePersistent(new MSentryPathChange(update.JSONSerialize()));
+    }
+  }
+
+  @Override
+  public int hashCode() {
+    return (update == null) ? 0 : update.hashCode();
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj == null) {
+      return false;
+    }
+
+    if (this == obj) {
+      return true;
+    }
+
+    if (getClass() != obj.getClass()) {
+      return false;
+    }
+
+    DeltaTransactionBlock other = (DeltaTransactionBlock) obj;
+    if (update == null) {
+      return other.update == null;
+    }
+    return update.equals(other.update);
+  }
+}

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
index 0712e2c..1bf4e82 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/SentryStore.java
@@ -100,6 +100,12 @@ public class SentryStore {
   public static final String GRANT_OPTION = "grantOption";
   public static final String ROLE_NAME = "roleName";
 
+  // Initial change ID for permission/path change. Auto increment
+  // is starting from 1.
+  public static final long INIT_CHANGE_ID = 1L;
+
+  private static final long EMPTY_CHANGE_ID = 0L;
+
   // For counters, representation of the "unknown value"
   private static final long COUNT_VALUE_UNKNOWN = -1;
 
@@ -426,43 +432,110 @@ public class SentryStore {
   }
 
   /**
-   * Grant privilege for a role
+   * Alter a given sentry role to grant a privilege.
+   *
    * @param grantorPrincipal User name
-   * @param roleName Role name
-   * @param privilege Privilege to grant
+   * @param roleName the given role name
+   * @param privilege the given privilege
    * @throws Exception
    */
-  void alterSentryRoleGrantPrivilege(String grantorPrincipal,
-      String roleName, TSentryPrivilege privilege) throws Exception {
-    alterSentryRoleGrantPrivileges(grantorPrincipal, roleName,
-            Sets.newHashSet(privilege));
+  void alterSentryRoleGrantPrivilege(final String grantorPrincipal,
+      final String roleName, final TSentryPrivilege privilege) throws Exception {
+
+    tm.executeTransactionWithRetry(
+      new TransactionBlock() {
+        public Object execute(PersistenceManager pm) throws Exception {
+          String trimmedRoleName = trimAndLower(roleName);
+          // first do grant check
+          grantOptionCheck(pm, grantorPrincipal, privilege);
+
+          // Alter sentry Role and grant Privilege.
+          MSentryPrivilege mPrivilege = alterSentryRoleGrantPrivilegeCore(
+            pm, trimmedRoleName, privilege);
+
+          if (mPrivilege != null) {
+            // update the privilege to be the one actually updated.
+            convertToTSentryPrivilege(mPrivilege, privilege);
+          }
+          return null;
+        }
+      });
   }
 
   /**
-   * Grant multiple privileges
+   * Alter a given sentry role to grant a set of privileges.
+   * Internally calls alterSentryRoleGrantPrivilege.
+   *
    * @param grantorPrincipal User name
    * @param roleName Role name
    * @param privileges Set of privileges
    * @throws Exception
    */
-  public void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
+  void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
       final String roleName, final Set<TSentryPrivilege> privileges) throws Exception {
-    tm.executeTransactionWithRetry(
-        new TransactionBlock() {
-          public Object execute(PersistenceManager pm) throws Exception {
-            String trimmedRoleName = trimAndLower(roleName);
-            for (TSentryPrivilege privilege : privileges) {
-              // first do grant check
-              grantOptionCheck(pm, grantorPrincipal, privilege);
-              MSentryPrivilege mPrivilege = alterSentryRoleGrantPrivilegeCore(
-                  pm, trimmedRoleName, privilege);
-              if (mPrivilege != null) {
-                convertToTSentryPrivilege(mPrivilege, privilege);
-              }
-            }
-            return null;
-          }
-        });
+    for (TSentryPrivilege privilege : privileges) {
+      alterSentryRoleGrantPrivilege(grantorPrincipal, roleName, privilege);
+    }
+  }
+
+  /**
+   * Alter a given sentry role to grant a privilege, as well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param grantorPrincipal User name
+   * @param roleName the given role name
+   * @param privilege the given privilege
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws Exception
+   *
+   */
+  void alterSentryRoleGrantPrivilege(final String grantorPrincipal,
+      final String roleName, final TSentryPrivilege privilege,
+      final DeltaTransactionBlock deltaTransactionBlock) throws Exception {
+
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        String trimmedRoleName = trimAndLower(roleName);
+        // first do grant check
+        grantOptionCheck(pm, grantorPrincipal, privilege);
+
+        // Alter sentry Role and grant Privilege.
+        MSentryPrivilege mPrivilege = alterSentryRoleGrantPrivilegeCore(pm,
+          trimmedRoleName, privilege);
+
+        if (mPrivilege != null) {
+          // update the privilege to be the one actually updated.
+          convertToTSentryPrivilege(mPrivilege, privilege);
+        }
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Alter a given sentry role to grant a set of privileges, as well as persist the
+   * corresponding permission change to MSentryPermChange table in a single transaction.
+   * Internally calls alterSentryRoleGrantPrivilege.
+   *
+   * @param grantorPrincipal User name
+   * @param roleName the given role name
+   * @param privileges a Set of privileges
+   * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map
+   * @throws Exception
+   *
+   */
+  public void alterSentryRoleGrantPrivileges(final String grantorPrincipal,
+      final String roleName, final Set<TSentryPrivilege> privileges,
+      final Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap) throws Exception {
+
+    for (TSentryPrivilege privilege : privileges) {
+      DeltaTransactionBlock deltaTransactionBlock = null;
+      if (privilegesUpdateMap != null) {
+        deltaTransactionBlock = privilegesUpdateMap.get(privilege);
+      }
+      alterSentryRoleGrantPrivilege(grantorPrincipal, roleName, privilege,
+          deltaTransactionBlock);
+    }
   }
 
   private MSentryPrivilege alterSentryRoleGrantPrivilegeCore(PersistenceManager pm,
@@ -522,27 +595,99 @@ public class SentryStore {
     return mPrivilege;
   }
 
-  void alterSentryRoleRevokePrivilege(String grantorPrincipal,
-      String roleName, TSentryPrivilege tPrivilege) throws Exception {
-    alterSentryRoleRevokePrivileges(grantorPrincipal, roleName,
-            Sets.newHashSet(tPrivilege));
+  /**
+  * Alter a given sentry role to revoke a privilege.
+  *
+  * @param grantorPrincipal User name
+  * @param roleName the given role name
+  * @param tPrivilege the given privilege
+  * @throws Exception
+  *
+  */
+  void alterSentryRoleRevokePrivilege(final String grantorPrincipal,
+      final String roleName, final TSentryPrivilege tPrivilege) throws Exception {
+
+    tm.executeTransactionWithRetry(
+      new TransactionBlock() {
+        public Object execute(PersistenceManager pm) throws Exception {
+          String trimmedRoleName = safeTrimLower(roleName);
+          // first do revoke check
+          grantOptionCheck(pm, grantorPrincipal, tPrivilege);
+
+          alterSentryRoleRevokePrivilegeCore(pm, trimmedRoleName, tPrivilege);
+          return null;
+        }
+      });
   }
 
-  public void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
+  /**
+   * Alter a given sentry role to revoke a set of privileges.
+   * Internally calls alterSentryRoleRevokePrivilege.
+   *
+   * @param grantorPrincipal User name
+   * @param roleName the given role name
+   * @param tPrivileges a Set of privileges
+   * @throws Exception
+   *
+   */
+  void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
       final String roleName, final Set<TSentryPrivilege> tPrivileges) throws Exception {
-    tm.executeTransactionWithRetry(
-        new TransactionBlock() {
-          public Object execute(PersistenceManager pm) throws Exception {
-            String trimmedRoleName = safeTrimLower(roleName);
-            for (TSentryPrivilege tPrivilege : tPrivileges) {
-              // first do revoke check
-              grantOptionCheck(pm, grantorPrincipal, tPrivilege);
+    for (TSentryPrivilege tPrivilege : tPrivileges) {
+      alterSentryRoleRevokePrivilege(grantorPrincipal, roleName, tPrivilege);
+    }
+  }
 
-              alterSentryRoleRevokePrivilegeCore(pm, trimmedRoleName, tPrivilege);
-            }
-            return null;
-          }
-        });
+  /**
+   * Alter a given sentry role to revoke a privilege, as well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param grantorPrincipal User name
+   * @param roleName the given role name
+   * @param tPrivilege the given privilege
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws Exception
+   *
+   */
+  void alterSentryRoleRevokePrivilege(final String grantorPrincipal,
+      final String roleName, final TSentryPrivilege tPrivilege,
+      final DeltaTransactionBlock deltaTransactionBlock) throws Exception {
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        String trimmedRoleName = safeTrimLower(roleName);
+        // first do revoke check
+        grantOptionCheck(pm, grantorPrincipal, tPrivilege);
+
+        alterSentryRoleRevokePrivilegeCore(pm, trimmedRoleName, tPrivilege);
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Alter a given sentry role to revoke a set of privileges, as well as persist the
+   * corresponding permission change to MSentryPermChange table in a single transaction.
+   * Internally calls alterSentryRoleRevokePrivilege.
+   *
+   * @param grantorPrincipal User name
+   * @param roleName the given role name
+   * @param tPrivileges a Set of privileges
+   * @param privilegesUpdateMap the corresponding <privilege, DeltaTransactionBlock> map
+   * @throws Exception
+   *
+   */
+  public void alterSentryRoleRevokePrivileges(final String grantorPrincipal,
+      final String roleName, final Set<TSentryPrivilege> tPrivileges,
+      final Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap)
+          throws Exception {
+
+    for (TSentryPrivilege tPrivilege : tPrivileges) {
+      DeltaTransactionBlock deltaTransactionBlock = null;
+      if (privilegesUpdateMap != null) {
+        deltaTransactionBlock = privilegesUpdateMap.get(tPrivilege);
+      }
+      alterSentryRoleRevokePrivilege(grantorPrincipal, roleName, tPrivilege,
+          deltaTransactionBlock);
+    }
   }
 
   private void alterSentryRoleRevokePrivilegeCore(PersistenceManager pm,
@@ -795,6 +940,12 @@ public class SentryStore {
     return (MSentryPrivilege)query.executeWithMap(paramBuilder.getArguments());
   }
 
+  /**
+   * Drop a given sentry role.
+   *
+   * @param roleName the given role name
+   * @throws Exception
+   */
   public void dropSentryRole(final String roleName) throws Exception {
     tm.executeTransactionWithRetry(
         new TransactionBlock() {
@@ -805,6 +956,25 @@ public class SentryStore {
         });
   }
 
+  /**
+   * Drop a given sentry role. As well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param roleName the given role name
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws Exception
+   */
+  public void dropSentryRole(final String roleName,
+      final DeltaTransactionBlock deltaTransactionBlock) throws Exception {
+
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        dropSentryRoleCore(pm, roleName);
+        return null;
+      }
+    });
+  }
+
   private void dropSentryRoleCore(PersistenceManager pm, String roleName)
       throws SentryNoSuchObjectException {
     String lRoleName = trimAndLower(roleName);
@@ -820,7 +990,15 @@ public class SentryStore {
     pm.deletePersistent(sentryRole);
   }
 
-  public void alterSentryRoleAddGroups(final String grantorPrincipal,
+  /**
+   * Assign a given role to a set of groups.
+   *
+   * @param grantorPrincipal grantorPrincipal currently is not used.
+   * @param roleName the role to be assigned to the groups.
+   * @param groupNames the list of groups to be added to the role,
+   * @throws Exception
+   */
+  void alterSentryRoleAddGroups(final String grantorPrincipal,
       final String roleName, final Set<TSentryGroup> groupNames) throws Exception {
     tm.executeTransactionWithRetry(
         new TransactionBlock() {
@@ -831,13 +1009,39 @@ public class SentryStore {
         });
   }
 
+  /**
+   * Assign a given role to a set of groups. As well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param grantorPrincipal grantorPrincipal currently is not used.
+   * @param roleName the role to be assigned to the groups.
+   * @param groupNames the list of groups to be added to the role,
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws Exception
+   */
+  public void alterSentryRoleAddGroups(final String grantorPrincipal,
+      final String roleName, final Set<TSentryGroup> groupNames,
+      final DeltaTransactionBlock deltaTransactionBlock) throws Exception {
+
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        alterSentryRoleAddGroupsCore(pm, roleName, groupNames);
+        return null;
+      }
+    });
+  }
+
   private void alterSentryRoleAddGroupsCore(PersistenceManager pm, String roleName,
       Set<TSentryGroup> groupNames) throws SentryNoSuchObjectException {
+
+    // All role names are stored in lowercase.
     String lRoleName = trimAndLower(roleName);
     MSentryRole role = getRole(pm, lRoleName);
     if (role == null) {
       throw noSuchRole(lRoleName);
     }
+
+    // Add the group to the specified role if it does not belong to the role yet.
     Query query = pm.newQuery(MSentryGroup.class);
     query.setFilter("this.groupName == :groupName");
     query.setUnique(true);
@@ -917,7 +1121,14 @@ public class SentryStore {
         });
   }
 
-  public void alterSentryRoleDeleteGroups(final String roleName,
+  /**
+   * Revoke a given role to a set of groups.
+   *
+   * @param roleName the role to be assigned to the groups.
+   * @param groupNames the list of groups to be added to the role,
+   * @throws Exception
+   */
+  void alterSentryRoleDeleteGroups(final String roleName,
       final Set<TSentryGroup> groupNames) throws Exception {
     tm.executeTransactionWithRetry(
         new TransactionBlock() {
@@ -945,6 +1156,45 @@ public class SentryStore {
         });
   }
 
+  /**
+   * Revoke a given role to a set of groups. As well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param roleName the role to be assigned to the groups.
+   * @param groupNames the list of groups to be added to the role,
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws Exception
+   */
+  public void alterSentryRoleDeleteGroups(final String roleName,
+      final Set<TSentryGroup> groupNames, final DeltaTransactionBlock deltaTransactionBlock)
+          throws Exception {
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+        String trimmedRoleName = trimAndLower(roleName);
+        MSentryRole role = getRole(pm, trimmedRoleName);
+        if (role == null) {
+          throw noSuchRole(trimmedRoleName);
+        }
+
+        // Remove the group from the specified role if it belongs to the role.
+        Query query = pm.newQuery(MSentryGroup.class);
+        query.setFilter("this.groupName == :groupName");
+        query.setUnique(true);
+        List<MSentryGroup> groups = Lists.newArrayList();
+        for (TSentryGroup tGroup : groupNames) {
+          String groupName = tGroup.getGroupName().trim();
+          MSentryGroup group = (MSentryGroup) query.execute(groupName);
+          if (group != null) {
+            group.removeRole(role);
+            groups.add(group);
+          }
+        }
+        pm.makePersistentAll(groups);
+        return null;
+      }
+    });
+  }
+
   @VisibleForTesting
   MSentryRole getMSentryRoleByName(final String roleName) throws Exception {
     return tm.executeTransaction(
@@ -1545,14 +1795,19 @@ public class SentryStore {
   }
 
   /**
-   * Drop given privilege from all roles
+   * Drop the given privilege from all roles.
+   *
+   * @param tAuthorizable the given authorizable object.
+   * @throws Exception
    */
-  public void dropPrivilege(final TSentryAuthorizable tAuthorizable) throws Exception {
+  void dropPrivilege(final TSentryAuthorizable tAuthorizable) throws Exception {
     tm.executeTransactionWithRetry(
         new TransactionBlock() {
           public Object execute(PersistenceManager pm) throws Exception {
 
+            // Drop the give privilege for all possible actions from all roles.
             TSentryPrivilege tPrivilege = toSentryPrivilege(tAuthorizable);
+
             try {
               if (isMultiActionsSupported(tPrivilege)) {
                 for (String privilegeAction : ALL_ACTIONS) {
@@ -1572,20 +1827,58 @@ public class SentryStore {
   }
 
   /**
-   * Rename given privilege from all roles drop the old privilege and create the new one
-   * @param tAuthorizable
-   * @param newTAuthorizable
+   * Drop the given privilege from all roles. As well as persist the corresponding
+   * permission change to MSentryPermChange table in a single transaction.
+   *
+   * @param tAuthorizable the given authorizable object.
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block.
+   * @throws Exception
+   */
+  public void dropPrivilege(final TSentryAuthorizable tAuthorizable,
+      final DeltaTransactionBlock deltaTransactionBlock) throws Exception {
+
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+
+        // Drop the give privilege for all possible actions from all roles.
+        TSentryPrivilege tPrivilege = toSentryPrivilege(tAuthorizable);
+
+        try {
+          if (isMultiActionsSupported(tPrivilege)) {
+            for (String privilegeAction : ALL_ACTIONS) {
+              tPrivilege.setAction(privilegeAction);
+              dropPrivilegeForAllRoles(pm, new TSentryPrivilege(tPrivilege));
+            }
+          } else {
+            dropPrivilegeForAllRoles(pm, new TSentryPrivilege(tPrivilege));
+          }
+        } catch (JDODataStoreException e) {
+          throw new SentryInvalidInputException("Failed to get privileges: "
+          + e.getMessage());
+        }
+        return null;
+      }
+    });
+  }
+
+  /**
+   * Rename the privilege for all roles. Drop the old privilege name and create the new one.
+   *
+   * @param oldTAuthorizable the old authorizable name needs to be renamed.
+   * @param newTAuthorizable the new authorizable name
    * @throws SentryNoSuchObjectException
    * @throws SentryInvalidInputException
    */
-  public void renamePrivilege(final TSentryAuthorizable tAuthorizable,
+  void renamePrivilege(final TSentryAuthorizable oldTAuthorizable,
       final TSentryAuthorizable newTAuthorizable) throws Exception {
     tm.executeTransactionWithRetry(
         new TransactionBlock() {
           public Object execute(PersistenceManager pm) throws Exception {
 
-            TSentryPrivilege tPrivilege = toSentryPrivilege(tAuthorizable);
+            // Drop the give privilege for all possible actions from all roles.
+            TSentryPrivilege tPrivilege = toSentryPrivilege(oldTAuthorizable);
             TSentryPrivilege newPrivilege = toSentryPrivilege(newTAuthorizable);
+
             try {
               // In case of tables or DBs, check all actions
               if (isMultiActionsSupported(tPrivilege)) {
@@ -1606,6 +1899,48 @@ public class SentryStore {
         });
   }
 
+  /**
+   * Rename the privilege for all roles. Drop the old privilege name and create the new one.
+   * As well as persist the corresponding permission change to MSentryPermChange table in a
+   * single transaction.
+   *
+   * @param oldTAuthorizable the old authorizable name needs to be renamed.
+   * @param newTAuthorizable the new authorizable name
+   * @param deltaTransactionBlock the corresponding permission delta update transaction block
+   * @throws SentryNoSuchObjectException
+   * @throws SentryInvalidInputException
+   */
+  public void renamePrivilege(final TSentryAuthorizable oldTAuthorizable,
+      final TSentryAuthorizable newTAuthorizable, final DeltaTransactionBlock deltaTransactionBlock)
+        throws Exception {
+
+    execute(deltaTransactionBlock, new TransactionBlock() {
+      public Object execute(PersistenceManager pm) throws Exception {
+
+        // Drop the give privilege for all possible actions from all roles.
+        TSentryPrivilege tPrivilege = toSentryPrivilege(oldTAuthorizable);
+        TSentryPrivilege newPrivilege = toSentryPrivilege(newTAuthorizable);
+
+        try {
+          // In case of tables or DBs, check all actions
+          if (isMultiActionsSupported(tPrivilege)) {
+            for (String privilegeAction : ALL_ACTIONS) {
+              tPrivilege.setAction(privilegeAction);
+              newPrivilege.setAction(privilegeAction);
+              renamePrivilegeForAllRoles(pm, tPrivilege, newPrivilege);
+            }
+          } else {
+            renamePrivilegeForAllRoles(pm, tPrivilege, newPrivilege);
+          }
+        } catch (JDODataStoreException e) {
+          throw new SentryInvalidInputException("Failed to get privileges: "
+          + e.getMessage());
+        }
+        return null;
+      }
+    });
+  }
+
   // Currently INSERT/SELECT/ALL are supported for Table and DB level privileges
   private boolean isMultiActionsSupported(TSentryPrivilege tPrivilege) {
     return tPrivilege.getDbName() != null;
@@ -2620,6 +2955,15 @@ public class SentryStore {
   }
 
   /**
+   * Return exception for nonexistent update
+   * @param changeID change ID
+   * @return SentryNoSuchObjectException with appropriate message
+   */
+  private SentryNoSuchObjectException noSuchUpdate(final long changeID) {
+    return new SentryNoSuchObjectException("nonexistent update + " + changeID);
+  }
+
+  /**
    * Add common filter for set of roles
    * @param query Query used for search
    * @param paramBuilder paramBuilder for parameters
@@ -2926,4 +3270,107 @@ public class SentryStore {
       return this;
     }
   }
+
+  /**
+   * Get the last processed perm change ID.
+   *
+   * @param pm the PersistenceManager
+   * @return the last processed perm changedID
+   */
+  private long getLastProcessedPermChangeIDCore(PersistenceManager pm) {
+    Query query = pm.newQuery(MSentryPermChange.class);
+    query.setResult("max(this.changeID)");
+    Long changeID = (Long) query.execute();
+    if (changeID == null) {
+      return EMPTY_CHANGE_ID;
+    } else {
+      return changeID;
+    }
+  }
+
+  /**
+   * Get the MSentryPermChange object by ChangeID. Internally invoke
+   * getLastProcessedPermChangeIDCore().
+   *
+   * @return MSentryPermChange
+   */
+  @VisibleForTesting
+  long getLastProcessedPermChangeID() throws Exception {
+    return tm.executeTransaction(
+      new TransactionBlock<Long>() {
+        public Long execute(PersistenceManager pm) throws Exception {
+          return getLastProcessedPermChangeIDCore(pm);
+        }
+      });
+  }
+
+  /**
+   * Get the MSentryPermChange object by ChangeID.
+   *
+   * @param changeID the given changeID.
+   * @return MSentryPermChange
+   */
+  public MSentryPermChange getMSentryPermChangeByID(final long changeID) throws Exception {
+    return (MSentryPermChange) tm.executeTransaction(
+      new TransactionBlock() {
+        public Object execute(PersistenceManager pm) throws Exception {
+          Query query = pm.newQuery(MSentryPermChange.class);
+          query.setFilter("this.changeID == t");
+          query.declareParameters("long t");
+          List<MSentryPermChange> permChanges = (List<MSentryPermChange>)query.execute(changeID);
+          if (permChanges == null) {
+            noSuchUpdate(changeID);
+          } else if (permChanges.size() > 1) {
+            throw new Exception("Each change ID should only corresponds to one perm change!");
+          }
+
+          return permChanges.get(0);
+        }
+      });
+  }
+
+  /**
+   * Get the MSentryPathChange object by ChangeID.
+   */
+  public MSentryPathChange getMSentryPathChangeByID(final long changeID) throws Exception {
+    return (MSentryPathChange) tm.executeTransaction(
+      new TransactionBlock() {
+        public Object execute(PersistenceManager pm) throws Exception {
+          Query query = pm.newQuery(MSentryPathChange.class);
+          query.setFilter("this.changeID == t");
+          query.declareParameters("long t");
+          List<MSentryPathChange> pathChanges = (List<MSentryPathChange>)query.execute(changeID);
+          if (pathChanges == null) {
+            noSuchUpdate(changeID);
+          } else if (pathChanges.size() > 1) {
+            throw new Exception("Each change ID should only corresponds to one path change!");
+          }
+
+          return pathChanges.get(0);
+        }
+      });
+  }
+
+  /**
+   * Execute Perm/Path UpdateTransaction and corresponding actual
+   * action transaction, e.g dropSentryRole, in a single transaction.
+   * The order of the transaction does not matter because there is no
+   * any return value.
+   * <p>
+   * Failure in any TransactionBlock would cause the whole transaction
+   * to fail.
+   *
+   * @param deltaTransactionBlock
+   * @param transactionBlock
+   * @throws Exception
+   */
+  private void execute(DeltaTransactionBlock deltaTransactionBlock,
+        TransactionBlock<Object> transactionBlock) throws Exception {
+    List<TransactionBlock<Object>> tbs = Lists.newArrayList();
+    if (deltaTransactionBlock != null) {
+      tbs.add(deltaTransactionBlock);
+    }
+    tbs.add(transactionBlock);
+    tm.executeTransactionBlocksWithRetry(tbs);
+  }
 }

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/TransactionManager.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/TransactionManager.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/TransactionManager.java
index 6428a0c..40fd58b 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/TransactionManager.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/persistent/TransactionManager.java
@@ -34,12 +34,14 @@ import javax.jdo.Transaction;
 
 import org.apache.sentry.provider.db.service.thrift.SentryMetrics;
 
+import java.util.List;
 
 /**
  * TransactionManager is used for executing the database transaction, it supports
  * the transaction with retry mechanism for the unexpected exceptions,
  * except <em>SentryUserExceptions</em>, eg, <em>SentryNoSuchObjectException</em>,
- * <em>SentryAlreadyExistsException</em> etc. <p>
+ * <em>SentryAlreadyExistsException</em> etc. For <em>SentryUserExceptions</em>,
+ * will simply throw the exception without retry<p>
  *
  * The purpose of the class is to separate all transaction housekeeping (opening
  * transaction, rolling back failed transactions) from the actual transaction
@@ -99,7 +101,8 @@ public class TransactionManager {
    * Execute some code as a single transaction, the code in tb.execute()
    * should not start new transaction or manipulate transactions with the
    * PersistenceManager.
-   * @param tb transaction block with code to execute
+   * 
+   * @param tb transaction block with code to be executed
    * @return Object with the result of tb.execute()
    */
   public <T> T executeTransaction(TransactionBlock<T> tb) throws Exception {
@@ -129,7 +132,46 @@ public class TransactionManager {
   }
 
   /**
-   * Execute some code as a single transaction with retry mechanism
+   * Execute a list of TransactionBlock code as a single transaction.
+   * The code in tb.execute() should not start new transaction or
+   * manipulate transactions with the PersistenceManager. It returns
+   * the result of the last transaction block execution.
+   *
+   * @param tbs transaction blocks with code to be executed
+   * @return the result of the last result of tb.execute()
+   */
+  public <T> T executeTransaction(Iterable<TransactionBlock<T>> tbs) throws Exception {
+    final Timer.Context context = transactionTimer.time();
+    try (PersistenceManager pm = pmf.getPersistenceManager()) {
+      Transaction transaction = pm.currentTransaction();
+      transaction.begin();
+      try {
+        T result = null;
+        for (TransactionBlock<T> tb : tbs) {
+          result = tb.execute(pm);
+        }
+        transaction.commit();
+        return result;
+      } catch (Exception e) {
+        // Count total failed transactions
+        failedTransactionsCount.inc();
+        // Count specific exceptions
+        SentryMetrics.getInstance().getCounter(name(TransactionManager.class,
+            "exception", e.getClass().getSimpleName())).inc();
+        // Re-throw the exception
+        throw e;
+      } finally {
+        context.stop();
+        if (transaction.isActive()) {
+          transaction.rollback();
+        }
+      }
+    }
+  }
+
+  /**
+   * Execute some code as a single transaction with retry mechanism.
+   *
    * @param tb transaction block with code to execute
    * @return Object with the result of tb.execute()
    */
@@ -139,20 +181,55 @@ public class TransactionManager {
     while (retryNum < transactionRetryMax) {
       try {
         return executeTransaction(tb);
-      } catch (Exception e) {
+      } catch (SentryUserException e) {
         // throw the sentry exception without retry
-        if (e instanceof SentryUserException) {
-          throw e;
+        throw e;
+      } catch (Exception e) {
+        retryNum++;
+        if (retryNum >= transactionRetryMax) {
+          String message = "The transaction has reached max retry numbe, r"
+              + e.getMessage();
+          LOGGER.error(message, e);
+          throw new Exception(message, e);
         }
+        retryCount.inc();
+        LOGGER.warn("Exception during transaction execution, retrying "
+            + retryNum + "times. The max retry num is: " + transactionRetryMax, e);
+        Thread.sleep(retryWaitTimeMills);
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Execute a list of TransactionBlock code as a single transaction.
+   * If any of the TransactionBlock fail, all the TransactionBlocks would
+   * retry. It returns the result of the last transaction block
+   * execution.
+   *
+   * @param tbs a list of transaction blocks with code to be executed.
+   * @return the result of the last transaction block execution.
+   */
+  public <T> T executeTransactionBlocksWithRetry(Iterable<TransactionBlock<T>> tbs)
+          throws Exception {
+    int retryNum = 0;
+    while (retryNum < transactionRetryMax) {
+      try {
+        return executeTransaction(tbs);
+      } catch (SentryUserException e) {
+        // throw the sentry exception without retry
+        throw e;
+      } catch (Exception e) {
         retryNum++;
         if (retryNum >= transactionRetryMax) {
-          String message = "The transaction has reached max retry number, will not retry again.";
+          String message = "The transaction has reached max retry number, "
+              + e.getMessage();
           LOGGER.error(message, e);
           throw new Exception(message, e);
         }
         retryCount.inc();
-        LOGGER.warn("Exception is thrown, retry the transaction, current retry num is:"
-            + retryNum + ", the max retry num is: " + transactionRetryMax, e);
+        LOGGER.warn("Exception during transaction execution, retrying "
+            + retryNum + "times. The max retry num is: " + transactionRetryMax, e);
         Thread.sleep(retryWaitTimeMills);
       }
     }

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
index 5121740..d38a2dc 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/provider/db/service/thrift/SentryPolicyStoreProcessor.java
@@ -20,6 +20,7 @@ package org.apache.sentry.provider.db.service.thrift;
 
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
@@ -46,6 +47,7 @@ import org.apache.sentry.provider.db.log.entity.JsonLogEntity;
 import org.apache.sentry.provider.db.log.entity.JsonLogEntityFactory;
 import org.apache.sentry.provider.db.log.util.Constants;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
+import org.apache.sentry.provider.db.service.persistent.DeltaTransactionBlock;
 import org.apache.sentry.provider.db.service.thrift.PolicyStoreConstants.PolicyStoreServerConfig;
 import org.apache.sentry.service.thrift.SentryServiceUtil;
 import org.apache.sentry.service.thrift.ServiceConstants;
@@ -74,8 +76,6 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
 
   public static final String SENTRY_POLICY_SERVICE_NAME = "SentryPolicyService";
 
-  public static volatile SentryPolicyStoreProcessor instance;
-
   private final String name;
   private final Configuration conf;
   private final SentryStore sentryStore;
@@ -109,9 +109,6 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       plugin.initialize(conf, sentryStore);
       sentryPlugins.add(plugin);
     }
-    if (instance == null) {
-      instance = this;
-    }
     initMetrics();
   }
 
@@ -244,8 +241,17 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       if (request.isSetPrivilege()) {
         request.setPrivileges(Sets.newHashSet(request.getPrivilege()));
       }
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap = new HashMap<>();
+      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+        plugin.onAlterSentryRoleGrantPrivilege(request, privilegesUpdateMap);
+      }
+
       sentryStore.alterSentryRoleGrantPrivileges(request.getRequestorUserName(),
-          request.getRoleName(), request.getPrivileges());
+              request.getRoleName(), request.getPrivileges(), privilegesUpdateMap);
       response.setStatus(Status.OK());
       response.setPrivileges(request.getPrivileges());
       // Maintain compatibility for old API: Set privilege field to response
@@ -254,9 +260,6 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       }
       notificationHandlerInvoker.alter_sentry_role_grant_privilege(request,
               response);
-      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleGrantPrivilege(request);
-      }
     } catch (SentryNoSuchObjectException e) {
       String msg = "Role: " + request.getRoleName() + " doesn't exist";
       LOGGER.error(msg, e);
@@ -308,14 +311,20 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       if (request.isSetPrivilege()) {
         request.setPrivileges(Sets.newHashSet(request.getPrivilege()));
       }
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      Map<TSentryPrivilege, DeltaTransactionBlock> privilegesUpdateMap = new HashMap<> ();
+      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+        plugin.onAlterSentryRoleRevokePrivilege(request, privilegesUpdateMap);
+      }
+
       sentryStore.alterSentryRoleRevokePrivileges(request.getRequestorUserName(),
-          request.getRoleName(), request.getPrivileges());
+              request.getRoleName(), request.getPrivileges(), privilegesUpdateMap);
       response.setStatus(Status.OK());
       notificationHandlerInvoker.alter_sentry_role_revoke_privilege(request,
               response);
-      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleRevokePrivilege(request);
-      }
     } catch (SentryNoSuchObjectException e) {
       StringBuilder msg = new StringBuilder();
       if (request.getPrivileges().size() > 0) {
@@ -378,12 +387,18 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(),
           getRequestorGroups(request.getRequestorUserName()));
-      sentryStore.dropSentryRole(request.getRoleName());
-      response.setStatus(Status.OK());
-      notificationHandlerInvoker.drop_sentry_role(request, response);
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      DeltaTransactionBlock deltaTransactionBlock = null;
       for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onDropSentryRole(request);
+        deltaTransactionBlock = plugin.onDropSentryRole(request);
       }
+
+      sentryStore.dropSentryRole(request.getRoleName(), deltaTransactionBlock);
+      response.setStatus(Status.OK());
+      notificationHandlerInvoker.drop_sentry_role(request, response);
     } catch (SentryNoSuchObjectException e) {
       String msg = "Role :" + request + " doesn't exist";
       LOGGER.error(msg, e);
@@ -422,15 +437,21 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(),
           getRequestorGroups(request.getRequestorUserName()));
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      DeltaTransactionBlock deltaTransactionBlock = null;
+      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+        deltaTransactionBlock = plugin.onAlterSentryRoleAddGroups(request);
+      }
+
       sentryStore.alterSentryRoleAddGroups(
           request.getRequestorUserName(), request.getRoleName(),
-          request.getGroups());
+          request.getGroups(), deltaTransactionBlock);
       response.setStatus(Status.OK());
       notificationHandlerInvoker.alter_sentry_role_add_groups(request,
-              response);
-      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleAddGroups(request);
-      }
+          response);
     } catch (SentryNoSuchObjectException e) {
       String msg = "Role: " + request + " doesn't exist";
       LOGGER.error(msg, e);
@@ -550,14 +571,20 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(),
           getRequestorGroups(request.getRequestorUserName()));
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      DeltaTransactionBlock deltaTransactionBlock = null;
+      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
+        deltaTransactionBlock = plugin.onAlterSentryRoleDeleteGroups(request);
+      }
+
       sentryStore.alterSentryRoleDeleteGroups(request.getRoleName(),
-              request.getGroups());
+          request.getGroups(), deltaTransactionBlock);
       response.setStatus(Status.OK());
       notificationHandlerInvoker.alter_sentry_role_delete_groups(request,
-              response);
-      for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onAlterSentryRoleDeleteGroups(request);
-      }
+          response);
     } catch (SentryNoSuchObjectException e) {
       String msg = "Role: " + request + " does not exist.";
       LOGGER.error(msg, e);
@@ -829,10 +856,16 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
     try {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(), adminGroups);
-      sentryStore.dropPrivilege(request.getAuthorizable());
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      DeltaTransactionBlock deltaTransactionBlock = null;
       for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onDropSentryPrivilege(request);
+        deltaTransactionBlock = plugin.onDropSentryPrivilege(request);
       }
+
+      sentryStore.dropPrivilege(request.getAuthorizable(), deltaTransactionBlock);
       response.setStatus(Status.OK());
     } catch (SentryAccessDeniedException e) {
       LOGGER.error(e.getMessage(), e);
@@ -859,11 +892,17 @@ public class SentryPolicyStoreProcessor implements SentryPolicyService.Iface {
     try {
       validateClientVersion(request.getProtocol_version());
       authorize(request.getRequestorUserName(), adminGroups);
-      sentryStore.renamePrivilege(request.getOldAuthorizable(),
-          request.getNewAuthorizable());
+
+      // TODO: now only has SentryPlugin. Once add more SentryPolicyStorePlugins,
+      // TODO: need to differentiate the updates for different Plugins.
+      Preconditions.checkState(sentryPlugins.size() <= 1);
+      DeltaTransactionBlock deltaTransactionBlock = null;
       for (SentryPolicyStorePlugin plugin : sentryPlugins) {
-        plugin.onRenameSentryPrivilege(request);
+        deltaTransactionBlock = plugin.onRenameSentryPrivilege(request);
       }
+
+      sentryStore.renamePrivilege(request.getOldAuthorizable(),
+          request.getNewAuthorizable(), deltaTransactionBlock);
       response.setStatus(Status.OK());
     } catch (SentryAccessDeniedException e) {
       LOGGER.error(e.getMessage(), e);

http://git-wip-us.apache.org/repos/asf/sentry/blob/ee2d3f7a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HMSFollower.java
----------------------------------------------------------------------
diff --git a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HMSFollower.java b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HMSFollower.java
index 749c2ce..59646b6 100644
--- a/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HMSFollower.java
+++ b/sentry-provider/sentry-provider-db/src/main/java/org/apache/sentry/service/thrift/HMSFollower.java
@@ -30,8 +30,12 @@ import org.apache.hadoop.security.SaslRpcServer;
 import org.apache.hive.hcatalog.messaging.HCatEventMessage;
 import org.apache.sentry.binding.hive.conf.HiveAuthzConf;
 import org.apache.sentry.core.common.exception.*;
+import org.apache.sentry.hdfs.PermissionsUpdate;
 import org.apache.sentry.hdfs.UpdateableAuthzPaths;
 import org.apache.sentry.hdfs.FullUpdateInitializer;
+import org.apache.sentry.hdfs.service.thrift.TPrivilegeChanges;
+import org.apache.sentry.provider.db.SentryPolicyStorePlugin;
+import org.apache.sentry.provider.db.service.persistent.DeltaTransactionBlock;
 import org.apache.sentry.provider.db.service.persistent.SentryStore;
 import org.apache.sentry.provider.db.service.thrift.TSentryAuthorizable;
 import org.apache.thrift.TException;
@@ -439,14 +443,16 @@ public class HMSFollower implements Runnable {
   private void dropSentryDbPrivileges(String dbName) throws Exception {
     TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance);
     authorizable.setDb(dbName);
-    sentryStore.dropPrivilege(authorizable);
+    sentryStore.dropPrivilege(authorizable, onDropSentryPrivilege(authorizable));
   }
+
   private void dropSentryTablePrivileges(String dbName, String tableName) throws Exception {
     TSentryAuthorizable authorizable = new TSentryAuthorizable(hiveInstance);
     authorizable.setDb(dbName);
     authorizable.setTable(tableName);
-    sentryStore.dropPrivilege(authorizable);
+    sentryStore.dropPrivilege(authorizable, onDropSentryPrivilege(authorizable));
   }
+
   private void renamePrivileges(String oldDbName, String oldTableName, String newDbName, String newTableName) throws
       Exception {
     TSentryAuthorizable oldAuthorizable = new TSentryAuthorizable(hiveInstance);
@@ -455,6 +461,43 @@ public class HMSFollower implements Runnable {
     TSentryAuthorizable newAuthorizable = new TSentryAuthorizable(hiveInstance);
     newAuthorizable.setDb(newDbName);
     newAuthorizable.setTable(newTableName);
-    sentryStore.renamePrivilege(oldAuthorizable, newAuthorizable);
+    DeltaTransactionBlock deltaTransactionBlock =
+        onRenameSentryPrivilege(oldAuthorizable, newAuthorizable);
+    sentryStore.renamePrivilege(oldAuthorizable, newAuthorizable, deltaTransactionBlock);
+  }
+
+  @VisibleForTesting
+  static DeltaTransactionBlock onDropSentryPrivilege(TSentryAuthorizable authorizable) {
+    PermissionsUpdate update = new PermissionsUpdate(SentryStore.INIT_CHANGE_ID, false);
+    String authzObj = getAuthzObj(authorizable);
+    update.addPrivilegeUpdate(authzObj).putToDelPrivileges(PermissionsUpdate.ALL_ROLES, PermissionsUpdate.ALL_ROLES);
+    return new DeltaTransactionBlock(update);
+  }
+
+  @VisibleForTesting
+  static DeltaTransactionBlock onRenameSentryPrivilege(TSentryAuthorizable oldAuthorizable,
+            TSentryAuthorizable newAuthorizable)
+          throws SentryPolicyStorePlugin.SentryPluginException {
+    String oldAuthz = getAuthzObj(oldAuthorizable);
+    String newAuthz = getAuthzObj(newAuthorizable);
+    PermissionsUpdate update = new PermissionsUpdate(SentryStore.INIT_CHANGE_ID, false);
+    TPrivilegeChanges privUpdate = update.addPrivilegeUpdate(PermissionsUpdate.RENAME_PRIVS);
+    privUpdate.putToAddPrivileges(newAuthz, newAuthz);
+    privUpdate.putToDelPrivileges(oldAuthz, oldAuthz);
+    return new DeltaTransactionBlock(update);
+  }
+
+  private static String getAuthzObj(TSentryAuthorizable authzble) {
+    String authzObj = null;
+    if (!SentryStore.isNULL(authzble.getDb())) {
+      String dbName = authzble.getDb();
+      String tblName = authzble.getTable();
+      if (SentryStore.isNULL(tblName)) {
+        authzObj = dbName;
+      } else {
+        authzObj = dbName + "." + tblName;
+      }
+    }
+    return authzObj == null ? null : authzObj.toLowerCase();
   }
 }