diff options
author | Myroslav Papirkovskyi <mpapyrkovskyy@hortonworks.com> | 2016-10-26 14:40:13 +0300 |
---|---|---|
committer | Myroslav Papirkovskyi <mpapyrkovskyy@hortonworks.com> | 2016-10-26 21:05:16 +0300 |
commit | 83f275106b93d04600d718353c186c4194cc44a2 (patch) | |
tree | 55d139ed1b3a5e426ddbac2e63562157a8c08ac3 | |
parent | f10452f6140fe5e9b9e7cabcca0e733dc047f1ff (diff) |
AMBARI-18698. Filter by roles in Users List page takes upto 20 secs to load with 1000+ users. (mpapirkovskyy)
2 files changed, 109 insertions, 9 deletions
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java index 009c38b632..ba32a5f162 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProvider.java @@ -17,6 +17,9 @@ */ package org.apache.ambari.server.controller.internal; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import org.apache.ambari.server.controller.spi.NoSuchParentResourceException; import org.apache.ambari.server.controller.spi.NoSuchResourceException; import org.apache.ambari.server.controller.spi.Predicate; @@ -30,6 +33,7 @@ import org.apache.ambari.server.orm.dao.UserDAO; import org.apache.ambari.server.orm.dao.ViewInstanceDAO; import org.apache.ambari.server.orm.entities.ClusterEntity; import org.apache.ambari.server.orm.entities.GroupEntity; +import org.apache.ambari.server.orm.entities.PrincipalEntity; import org.apache.ambari.server.orm.entities.PrincipalTypeEntity; import org.apache.ambari.server.orm.entities.PrivilegeEntity; import org.apache.ambari.server.orm.entities.UserEntity; @@ -48,6 +52,8 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.TreeMap; +import java.util.concurrent.TimeUnit; /** * Resource provider for user privilege resources. @@ -142,6 +148,87 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { keyPropertyIds.put(Resource.Type.UserPrivilege, PRIVILEGE_PRIVILEGE_ID_PROPERTY_ID); } + private ThreadLocal<LoadingCache<Long, ClusterEntity>> clusterCache = + new ThreadLocal<LoadingCache<Long, ClusterEntity>>(){ + @Override + protected LoadingCache<Long, ClusterEntity> initialValue() { + CacheLoader<Long, ClusterEntity> loader = new CacheLoader<Long, ClusterEntity>() { + @Override + public ClusterEntity load(Long key) throws Exception { + return clusterDAO.findByResourceId(key); + } + }; + return CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.SECONDS).build(loader); + } + }; + + private ThreadLocal<LoadingCache<Long, ViewInstanceEntity>> viewInstanceCache = + new ThreadLocal<LoadingCache<Long, ViewInstanceEntity>>(){ + @Override + protected LoadingCache<Long, ViewInstanceEntity> initialValue() { + CacheLoader<Long, ViewInstanceEntity> loader = new CacheLoader<Long, ViewInstanceEntity>() { + @Override + public ViewInstanceEntity load(Long key) throws Exception { + return viewInstanceDAO.findByResourceId(key); + } + }; + return CacheBuilder.newBuilder().expireAfterWrite(20, TimeUnit.SECONDS).build(loader); + } + }; + + private ThreadLocal<LoadingCache<String, UserEntity>> usersCache = + new ThreadLocal<LoadingCache<String, UserEntity>>(){ + @Override + protected LoadingCache<String, UserEntity> initialValue() { + CacheLoader<String, UserEntity> loader = new CacheLoader<String, UserEntity>() { + @Override + public UserEntity load(String key) throws Exception { + //fallback mechanism, mostly for unit tests + UserEntity userEntity = userDAO.findLocalUserByName(key); + if (userEntity == null) { + userEntity = userDAO.findLdapUserByName(key); + } + if (userEntity == null) { + userEntity = userDAO.findUserByNameAndType(key, UserType.JWT); + } + return userEntity; + } + }; + + return CacheBuilder.newBuilder() + .expireAfterWrite(20, TimeUnit.SECONDS) + .build(loader); + } + }; + + private ThreadLocal<LoadingCache<PrincipalEntity, GroupEntity>> groupsCache = + new ThreadLocal<LoadingCache<PrincipalEntity, GroupEntity>>(){ + @Override + protected LoadingCache<PrincipalEntity, GroupEntity> initialValue() { + CacheLoader<PrincipalEntity, GroupEntity> loader = new CacheLoader<PrincipalEntity, GroupEntity>() { + @Override + public GroupEntity load(PrincipalEntity key) throws Exception { + return groupDAO.findGroupByPrincipal(key); + } + }; + + return CacheBuilder.newBuilder() + .expireAfterWrite(20, TimeUnit.SECONDS) + .build(loader); + } + }; + + private GroupEntity getCachedGroupByPrincipal(PrincipalEntity principalEntity) { + GroupEntity entity = groupsCache.get().getIfPresent(principalEntity); + if (entity == null) { + for (GroupEntity groupEntity : groupDAO.findAll()) { + groupsCache.get().put(groupEntity.getPrincipal(), groupEntity); + } + entity = groupsCache.get().getUnchecked(principalEntity); + } + return entity; + } + /** * Constructor. @@ -183,13 +270,24 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { } if (userName != null) { - UserEntity userEntity = userDAO.findLocalUserByName(userName); - if (userEntity == null) { - userEntity = userDAO.findLdapUserByName(userName); - } + + UserEntity userEntity = usersCache.get().getIfPresent(userName); if (userEntity == null) { - userEntity = userDAO.findUserByNameAndType(userName, UserType.JWT); + //temporary tradeoff, add ~200ms for single user call, but start saving time for 100+ subsequent calls + //usual case for management page is to populate subresources for all users + Map<String, UserEntity> userNames = new TreeMap<>(); + for (UserEntity entity : userDAO.findAll()) { + UserEntity existing = userNames.get(entity.getUserName()); + if (existing == null || + entity.getUserType() == UserType.LOCAL || + existing.getUserType() == UserType.JWT) { + userNames.put(entity.getUserName(), entity); + } + } + usersCache.get().putAll(userNames); + userEntity = usersCache.get().getUnchecked(userName); } + if (userEntity == null) { throw new SystemException("User " + userName + " was not found"); } @@ -213,7 +311,7 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { * @param requestedIds the relevant request ids * @return a resource */ - protected Resource toResource(PrivilegeEntity privilegeEntity, Object userName, Set<String> requestedIds) { + protected Resource toResource(PrivilegeEntity privilegeEntity, Object userName, Set<String> requestedIds){ final ResourceImpl resource = new ResourceImpl(Resource.Type.UserPrivilege); setResourceProperty(resource, PRIVILEGE_USER_NAME_PROPERTY_ID, userName, requestedIds); @@ -227,7 +325,7 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { final UserEntity user = userDAO.findUserByPrincipal(privilegeEntity.getPrincipal()); setResourceProperty(resource, PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID, user.getUserName(), requestedIds); } else if (principalTypeName.equals(PrincipalTypeEntity.GROUP_PRINCIPAL_TYPE_NAME)) { - final GroupEntity groupEntity = groupDAO.findGroupByPrincipal(privilegeEntity.getPrincipal()); + final GroupEntity groupEntity = getCachedGroupByPrincipal(privilegeEntity.getPrincipal()); setResourceProperty(resource, PRIVILEGE_PRINCIPAL_NAME_PROPERTY_ID, groupEntity.getGroupName(), requestedIds); } @@ -239,11 +337,11 @@ public class UserPrivilegeResourceProvider extends ReadOnlyResourceProvider { // there is nothing special to add for this case break; case CLUSTER: - final ClusterEntity clusterEntity = clusterDAO.findByResourceId(privilegeEntity.getResource().getId()); + final ClusterEntity clusterEntity = clusterCache.get().getUnchecked(privilegeEntity.getResource().getId()); setResourceProperty(resource, PRIVILEGE_CLUSTER_NAME_PROPERTY_ID, clusterEntity.getClusterName(), requestedIds); break; case VIEW: - final ViewInstanceEntity viewInstanceEntity = viewInstanceDAO.findByResourceId(privilegeEntity.getResource().getId()); + final ViewInstanceEntity viewInstanceEntity = viewInstanceCache.get().getUnchecked(privilegeEntity.getResource().getId()); final ViewEntity viewEntity = viewInstanceEntity.getViewEntity(); setResourceProperty(resource, PRIVILEGE_VIEW_NAME_PROPERTY_ID, viewEntity.getCommonName(), requestedIds); diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProviderTest.java index ddb510dc46..ce2d8e10ec 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProviderTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UserPrivilegeResourceProviderTest.java @@ -385,6 +385,7 @@ public class UserPrivilegeResourceProviderTest extends AbstractPrivilegeResource final UserDAO userDAO = createNiceMock(UserDAO.class); expect(userDAO.findLocalUserByName("jdoe")).andReturn(userEntity).anyTimes(); expect(userDAO.findUserByPrincipal(anyObject(PrincipalEntity.class))).andReturn(userEntity).anyTimes(); + expect(userDAO.findAll()).andReturn(Collections.<UserEntity>emptyList()).anyTimes(); final PrivilegeDAO privilegeDAO = createMock(PrivilegeDAO.class); final MemberDAO memberDAO = createMock(MemberDAO.class); @@ -465,6 +466,7 @@ public class UserPrivilegeResourceProviderTest extends AbstractPrivilegeResource andReturn(Collections.<MemberEntity>emptyList()) .atLeastOnce(); expect(userDAO.findLocalUserByName(requestedUsername)).andReturn(userEntity).anyTimes(); + expect(userDAO.findAll()).andReturn(Collections.<UserEntity>emptyList()).anyTimes(); expect(userEntity.getPrincipal()).andReturn(principalEntity).anyTimes(); expect(userEntity.getMemberEntities()).andReturn(Collections.<MemberEntity>emptySet()).anyTimes(); expect(privilegeEntity.getPermission()).andReturn(permissionEntity).anyTimes(); |