Improve application credential validation speed
Validating an application credential token is very slow, taking at least
400ms+ in a simple devstack environment, 5-10x longer than validating a
user/password project token.
The primary bottleneck during a token validation request
(/v3/auth/tokens) is that token.roles is evaluated at least 5 times.
validate_token is called twice, first during RBAC to populate the
subject token context and again to actually validate the token. Each
call to validate_token then called token.roles twice because it first
checks if it is None, before calling it again to use the result. Lastly
token.roles is evaluated a fifth time during
render_token_response_from_model.
Each evaluation of token.roles calls through
_get_application_credential_roles into list_role_assignments which then
makes multiple round-trip SQL queries to the database.
Unlike the related get_roles_for_user_and_project function, none of
these calls are currently cached/memoized. We memoize
list_role_assignments to get the same-speedup.
Reduce the number of token.roles calls to only 3 by storing and re-using
the token.roles result in validate_token, then memoize
list_role_assignments so the 2nd and 3rd call fetch from the cache
instead of repeating many SQL queries.
This provides a substantial performance improvement bringing validation
time in-line with user/password tokens.
Change-Id: I8c45131b298ceae7b43b42e2c5df167607d18c48
(cherry picked from commit 67b5cca032
)
This commit is contained in:
parent
5a55e9de15
commit
932863b6db
|
@ -938,6 +938,7 @@ class Manager(manager.Manager):
|
|||
|
||||
return assignments
|
||||
|
||||
@MEMOIZE_COMPUTED_ASSIGNMENTS
|
||||
def list_role_assignments(self, role_id=None, user_id=None, group_id=None,
|
||||
system=None, domain_id=None, project_id=None,
|
||||
include_subtree=False, inherited=None,
|
||||
|
@ -1080,6 +1081,7 @@ class Manager(manager.Manager):
|
|||
system_assignments = self.list_system_grants_for_group(group_id)
|
||||
for assignment in system_assignments:
|
||||
self.delete_system_grant_for_group(group_id, assignment['id'])
|
||||
COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
def delete_user_assignments(self, user_id):
|
||||
# FIXME(lbragstad): This should be refactored in the Rocky release so
|
||||
|
@ -1091,6 +1093,7 @@ class Manager(manager.Manager):
|
|||
system_assignments = self.list_system_grants_for_user(user_id)
|
||||
for assignment in system_assignments:
|
||||
self.delete_system_grant_for_user(user_id, assignment['id'])
|
||||
COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
def check_system_grant_for_user(self, user_id, role_id):
|
||||
"""Check if a user has a specific role on the system.
|
||||
|
@ -1163,6 +1166,7 @@ class Manager(manager.Manager):
|
|||
target_id = self._SYSTEM_SCOPE_TOKEN
|
||||
inherited = False
|
||||
self.driver.delete_system_grant(role_id, user_id, target_id, inherited)
|
||||
COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
def check_system_grant_for_group(self, group_id, role_id):
|
||||
"""Check if a group has a specific role on the system.
|
||||
|
@ -1237,6 +1241,7 @@ class Manager(manager.Manager):
|
|||
self.driver.delete_system_grant(
|
||||
role_id, group_id, target_id, inherited
|
||||
)
|
||||
COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
def list_all_system_grants(self):
|
||||
"""Return a list of all system grants."""
|
||||
|
|
|
@ -242,8 +242,9 @@ def build_token_values(token):
|
|||
token_values['assignment_domain_id'] = None
|
||||
|
||||
role_list = []
|
||||
if token.roles is not None:
|
||||
for role in token.roles:
|
||||
token_roles = token.roles
|
||||
if token_roles is not None:
|
||||
for role in token_roles:
|
||||
role_list.append(role['id'])
|
||||
token_values['roles'] = role_list
|
||||
|
||||
|
|
|
@ -643,6 +643,9 @@ class AssignmentTests(AssignmentTestHelperMixin):
|
|||
# attempts to lookup a group that has been deleted in the backend
|
||||
with mock.patch.object(PROVIDERS.identity_api, 'get_group',
|
||||
_group_not_found):
|
||||
# Mocking a dependent function makes the cache invalid
|
||||
keystone.assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
assignment_list = PROVIDERS.assignment_api.list_role_assignments(
|
||||
include_names=True
|
||||
)
|
||||
|
@ -669,6 +672,9 @@ class AssignmentTests(AssignmentTestHelperMixin):
|
|||
# in the backend
|
||||
with mock.patch.object(PROVIDERS.identity_api, 'list_users_in_group',
|
||||
_group_not_found):
|
||||
# Mocking a dependent function makes the cache invalid
|
||||
keystone.assignment.COMPUTED_ASSIGNMENTS_REGION.invalidate()
|
||||
|
||||
assignment_list = PROVIDERS.assignment_api.list_role_assignments(
|
||||
effective=True
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue