Optimize lookup of org membership on prototype and perms APIs

Fixes a major slowdown when working with permissions under organizations with a lot of members

Fixes https://www.pivotaltracker.com/story/show/144076113
This commit is contained in:
Joseph Schorr 2017-05-08 13:31:26 -04:00
parent f5e4380a57
commit db767b3610
3 changed files with 37 additions and 17 deletions

View file

@ -106,15 +106,28 @@ def remove_organization_member(org, user_obj):
TeamMember.delete().where(TeamMember.id << members).execute() TeamMember.delete().where(TeamMember.id << members).execute()
def get_organization_member_set(orgname): def get_organization_member_set(org, include_robots=False, users_filter=None):
""" Returns the set of all member usernames under the given organization, with optional
filtering by robots and/or by a specific set of User objects.
"""
Org = User.alias() Org = User.alias()
org_users = (User org_users = (User
.select(User.username) .select(User.username)
.join(TeamMember) .join(TeamMember)
.join(Team) .join(Team)
.join(Org, on=(Org.id == Team.organization)) .where(Team.organization == org)
.where(Org.username == orgname)
.distinct()) .distinct())
if not include_robots:
org_users = org_users.where(User.robot == False)
if users_filter is not None:
ids_list = [u.id for u in users_filter if u is not None]
if not ids_list:
return set()
org_users = org_users.where(User.id << ids_list)
return {user.username for user in org_users} return {user.username for user in org_users}

View file

@ -73,6 +73,9 @@ class RepositoryUserPermissionList(RepositoryParamResource):
# This repository isn't under an org # This repository isn't under an org
pass pass
# Load the permissions.
repo_perms = model.user.get_all_repo_users(namespace, repository)
# Determine how to wrap the role(s). # Determine how to wrap the role(s).
def wrapped_role_view(repo_perm): def wrapped_role_view(repo_perm):
return wrap_role_view_user(role_view(repo_perm), repo_perm.user) return wrap_role_view_user(role_view(repo_perm), repo_perm.user)
@ -80,20 +83,17 @@ class RepositoryUserPermissionList(RepositoryParamResource):
role_view_func = wrapped_role_view role_view_func = wrapped_role_view
if org: if org:
org_members = model.organization.get_organization_member_set(namespace) users_filter = {perm.user for perm in repo_perms}
org_members = model.organization.get_organization_member_set(org, users_filter=users_filter)
current_func = role_view_func current_func = role_view_func
def wrapped_role_org_view(repo_perm): def wrapped_role_org_view(repo_perm):
return wrap_role_view_org(current_func(repo_perm), repo_perm.user, return wrap_role_view_org(current_func(repo_perm), repo_perm.user, org_members)
org_members)
role_view_func = wrapped_role_org_view role_view_func = wrapped_role_org_view
# Load and return the permissions.
repo_perms = model.user.get_all_repo_users(namespace, repository)
return { return {
'permissions': {perm.user.username: role_view_func(perm) 'permissions': {perm.user.username: role_view_func(perm) for perm in repo_perms}
for perm in repo_perms}
} }
@ -156,8 +156,8 @@ class RepositoryUserPermission(RepositoryParamResource):
perm_view = wrap_role_view_user(role_view(perm), perm.user) perm_view = wrap_role_view_user(role_view(perm), perm.user)
try: try:
model.organization.get_organization(namespace) org = model.organization.get_organization(namespace)
org_members = model.organization.get_organization_member_set(namespace) org_members = model.organization.get_organization_member_set(org, users_filter={perm.user})
perm_view = wrap_role_view_org(perm_view, perm.user, org_members) perm_view = wrap_role_view_org(perm_view, perm.user, org_members)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
# This repository is not part of an organization # This repository is not part of an organization
@ -183,8 +183,8 @@ class RepositoryUserPermission(RepositoryParamResource):
perm_view = wrap_role_view_user(role_view(perm), perm.user) perm_view = wrap_role_view_user(role_view(perm), perm.user)
try: try:
model.organization.get_organization(namespace) org = model.organization.get_organization(namespace)
org_members = model.organization.get_organization_member_set(namespace) org_members = model.organization.get_organization_member_set(org, users_filter={perm.user})
perm_view = wrap_role_view_org(perm_view, perm.user, org_members) perm_view = wrap_role_view_org(perm_view, perm.user, org_members)
except model.InvalidOrganizationException: except model.InvalidOrganizationException:
# This repository is not part of an organization # This repository is not part of an organization

View file

@ -133,7 +133,10 @@ class PermissionPrototypeList(ApiResource):
raise NotFound() raise NotFound()
permissions = model.permission.get_prototype_permissions(org) permissions = model.permission.get_prototype_permissions(org)
org_members = model.organization.get_organization_member_set(orgname)
users_filter = ({p.activating_user for p in permissions} |
{p.delegate_user for p in permissions})
org_members = model.organization.get_organization_member_set(org, users_filter=users_filter)
return {'prototypes': [prototype_view(p, org_members) for p in permissions]} return {'prototypes': [prototype_view(p, org_members) for p in permissions]}
raise Unauthorized() raise Unauthorized()
@ -180,7 +183,9 @@ class PermissionPrototypeList(ApiResource):
prototype = model.permission.add_prototype_permission(org, role_name, activating_user, prototype = model.permission.add_prototype_permission(org, role_name, activating_user,
delegate_user, delegate_team) delegate_user, delegate_team)
log_prototype_action('create_prototype_permission', orgname, prototype) log_prototype_action('create_prototype_permission', orgname, prototype)
org_members = model.organization.get_organization_member_set(orgname)
users_filter = {prototype.activating_user, prototype.delegate_user}
org_members = model.organization.get_organization_member_set(org, users_filter=users_filter)
return prototype_view(prototype, org_members) return prototype_view(prototype, org_members)
raise Unauthorized() raise Unauthorized()
@ -257,7 +262,9 @@ class PermissionPrototype(ApiResource):
log_prototype_action('modify_prototype_permission', orgname, prototype, log_prototype_action('modify_prototype_permission', orgname, prototype,
original_role=existing.role.name) original_role=existing.role.name)
org_members = model.organization.get_organization_member_set(orgname)
users_filter = {prototype.activating_user, prototype.delegate_user}
org_members = model.organization.get_organization_member_set(org, users_filter=users_filter)
return prototype_view(prototype, org_members) return prototype_view(prototype, org_members)
raise Unauthorized() raise Unauthorized()