Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Performance: Cache RoleMaps produced by RoleMap#newMatchingRoleMap() #81

Merged
merged 6 commits into from
Jun 27, 2019

Conversation

AbhyudayaSharma
Copy link
Member

From our discussion yesterday on the Gitter chat and since the newMatchingRoleMap is called for every ACL request and a lot of permissions are checked for a project when the home page loads, having a cache avoids checking regular expressions again for every time permission checks are called for a given Item.

Since the newMatchingRoleMap is called for every ACL request and a lot
of permissions are checked for a project when the home page loads, having
a cache avoids checking regular expressions again for every time permission
checks are called for a given Item.
@AbhyudayaSharma AbhyudayaSharma requested a review from a team June 20, 2019 07:01
@AbhyudayaSharma AbhyudayaSharma self-assigned this Jun 20, 2019
@AbhyudayaSharma AbhyudayaSharma changed the title Add a cache for RoleMap#newMatchingRoleMap() Cache RoleMaps produced by RoleMap#newMatchingRoleMap() Jun 20, 2019
Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to think about cache invalidation.
I would guess that ItemListener would be enough for it (so that we invalidate the cache on item creation or deletion). WDYT?

Also, would be great to have some performance metrics before and after the change from our benchmarks

* for different permissions for the same {@code itemName}, so cache them and avoid wasting time
* matching regular expressions.
*/
private final Cache<String, RoleMap> matchingRoleMapCache = CacheBuilder.newBuilder()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we continue with the Cache from Guava (avoid adding any dependencies) or use something else like JCS, Ehcache or something else?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine to use Guava as long as we do not needed newer versions.
Maybe we could have a research task for comparing cache implementations with benchmarks in later phases

@AbhyudayaSharma
Copy link
Member Author

AbhyudayaSharma commented Jun 20, 2019

Results compared to the benchmarks carried out in #80 :

image

* cleanup JavaDoc for newMatchingRoleMap
* remove unused private method
* cleanup unAssignRole
*/
public RoleMap newMatchingRoleMap(String namePattern) {
RoleMap cachedRoleMap = matchingRoleMapCache.getIfPresent(namePattern);
public RoleMap newMatchingRoleMap(String itemName) {
Copy link
Member Author

@AbhyudayaSharma AbhyudayaSharma Jun 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed the JavaDoc because I don't think it was correctly telling what was going on.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that the original Javadoc was wrong,
Maybe we could rename it to itemNamePrefix or so, because actually it does some kind of "pattern matching", just not the regexp/wildcard/whatever. It just takes a string and verifies it against regexp, but it does not really have to be a full item name. For better or worse...

* @param permission The permission you want to check
* @return A Set of Roles holding the given permission
*/
private Set<Role> getRolesHavingPermission(final Permission permission) {
Copy link
Member Author

@AbhyudayaSharma AbhyudayaSharma Jun 20, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've removed it because it was not being used anywhere.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was refactored out in #54 and has no references to it now.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a good API method, but we can always recover it later if somebody needs it

@@ -203,8 +203,8 @@ public SidACL getACL(RoleType roleType, AccessControlled controlledItem) {
public void addRole(Role role) {
if (this.getRole(role.getName()) == null) {
this.grantedRoles.put(role, new CopyOnWriteArraySet<>());
matchingRoleMapCache.invalidateAll();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This cache would only be affected when a change to grantedRoles is made because the RoleMap returned contains only those roles applicable for a given name. So it won't be necessary to use ItemListener in this case.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine with me, just minor comments

* for different permissions for the same {@code itemName}, so cache them and avoid wasting time
* matching regular expressions.
*/
private final Cache<String, RoleMap> matchingRoleMapCache = CacheBuilder.newBuilder()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is fine to use Guava as long as we do not needed newer versions.
Maybe we could have a research task for comparing cache implementations with benchmarks in later phases

@@ -203,8 +203,8 @@ public SidACL getACL(RoleType roleType, AccessControlled controlledItem) {
public void addRole(Role role) {
if (this.getRole(role.getName()) == null) {
this.grantedRoles.put(role, new CopyOnWriteArraySet<>());
matchingRoleMapCache.invalidateAll();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

agreed

* @param permission The permission you want to check
* @return A Set of Roles holding the given permission
*/
private Set<Role> getRolesHavingPermission(final Permission permission) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like a good API method, but we can always recover it later if somebody needs it

@oleg-nenashev
Copy link
Member

Test failure is really odd here, and last reopen did not trigger the build. Let me try

*/
private final Cache<String, RoleMap> matchingRoleMapCache = CacheBuilder.newBuilder()
.softValues()
.maximumSize(Settings.USER_DETAILS_CACHE_MAX_SIZE)
Copy link
Member Author

@AbhyudayaSharma AbhyudayaSharma Jun 24, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default value for the max size of the cache is 100. If there are more than 100 objects managed by Role Strategy, the cache would start evicting entries. Also, the max size of the cache cannot be increased after its creation. Should we care about this situation and create a larger cache using an ItemListener and a ComputerListener at the cost of higher memory usage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. The objects are not big, so bumping to 2048 or so would make sense

* for different permissions for the same {@code itemNamePrefix}, so cache them and avoid wasting time
* matching regular expressions.
*/
private final Cache<String, RoleMap> matchingRoleMapCache = CacheBuilder.newBuilder()
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The matching RoleMaps that are created also have a cache in them which is not needed. Do we reorganize the class structure to avoid it or don't care about a little more memory use?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would submit a follow-up ticket for it. Memory usage is not a problem for the plugin ATM

@oleg-nenashev oleg-nenashev changed the title Cache RoleMaps produced by RoleMap#newMatchingRoleMap() Performance: Cache RoleMaps produced by RoleMap#newMatchingRoleMap() Jun 24, 2019
Copy link
Member

@oleg-nenashev oleg-nenashev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-:+1:

@runzexia runzexia added on-hold and removed on-hold labels Jun 27, 2019
@scrwr
Copy link

scrwr commented Jan 28, 2020

Hi. This has some really odd implication as it seems. A typical workflow is:

  1. create a new role
  2. assign the role

But role assignment doesn't invalidate the cache and hence, the user sticks with his old roleset for an hour.

@odilontalk
Copy link

Yeah, facing the same issue mentioned by @scrwr here.

There is any command that i can run to invalidate cache? Maybe in Jenkins Script Console?

@oleg-nenashev
Copy link
Member

oleg-nenashev commented Aug 28, 2020 via email

@grickit
Copy link

grickit commented Aug 28, 2020

@odilontalk I just ran into this myself and confirmed (as implied by @scrwr's comment) that creating a new arbitrary dummy role and hitting "apply" will clear/update the cached permissions. You can then delete that role.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
7 participants