Index: api/src/main/java/org/sakaiproject/authz/api/AuthzGroupService.java
===================================================================
--- api/src/main/java/org/sakaiproject/authz/api/AuthzGroupService.java (revision 131858)
+++ api/src/main/java/org/sakaiproject/authz/api/AuthzGroupService.java (working copy)
@@ -93,6 +93,7 @@
/**
* Access a list of AuthzGroups which contain a specified userid
+ * NOTE: This call is backed by a cache.
*
* @param authzGroupIds
* AuthzGroup selection criteria (list of AuthzGroup ids)
Index: kernel-component/src/main/webapp/WEB-INF/authz-components.xml
===================================================================
--- kernel-component/src/main/webapp/WEB-INF/authz-components.xml (revision 131858)
+++ kernel-component/src/main/webapp/WEB-INF/authz-components.xml (working copy)
@@ -13,7 +13,8 @@
init-method="init"
destroy-method="destroy"
singleton="true"
- depends-on="org.sakaiproject.authz.impl.DbAuthzGroupService.realmRoleGroupCache">
+ depends-on="org.sakaiproject.authz.impl.DbAuthzGroupService.realmRoleGroupCache
+ org.sakaiproject.authz.impl.DbAuthzGroupService.authzUserGroupIdsCache">
@@ -76,4 +77,14 @@
+
+
+
+
+
+
+
+
+
Index: kernel-impl/src/main/java/org/sakaiproject/authz/impl/DbAuthzGroupService.java
===================================================================
--- kernel-impl/src/main/java/org/sakaiproject/authz/impl/DbAuthzGroupService.java (revision 131858)
+++ kernel-impl/src/main/java/org/sakaiproject/authz/impl/DbAuthzGroupService.java (working copy)
@@ -27,6 +27,7 @@
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
@@ -144,6 +145,7 @@
// KNL-600 CACHING for the realm role groups
private Cache m_realmRoleGRCache;
+ private Cache authzUserGroupIdsCache;
/**
* @return the ServerConfigurationService collaborator.
@@ -229,6 +231,8 @@
cacheFunctionNames();
m_realmRoleGRCache = m_memoryService.newCache("org.sakaiproject.authz.impl.DbAuthzGroupService.realmRoleGroupCache");
M_log.info("init(): table: " + m_realmTableName + " external locks: " + m_useExternalLocks);
+
+ authzUserGroupIdsCache = m_memoryService.newCache("org.sakaiproject.authz.impl.DbAuthzGroupService.authzUserGroupIdsCache");
}
catch (Exception t)
{
@@ -241,6 +245,8 @@
*/
public void destroy()
{
+ authzUserGroupIdsCache.destroy();
+
// done with event watching
eventTrackingService().deleteObserver(this);
@@ -750,6 +756,20 @@
if (authzGroupIds == null || userid == null || authzGroupIds.size() < 1)
return new ArrayList(); // empty list
+ UserAndGroups uag = null;
+ // first consult the cache
+ if (authzUserGroupIdsCache.containsKey(userid)) {
+ uag = (UserAndGroups) authzUserGroupIdsCache.get(userid);
+ List result = uag.getRealmQuery(new HashSet(authzGroupIds));
+ if (M_log.isDebugEnabled()) M_log.debug(uag);
+ if (result != null) {
+ // hit
+ return result;
+ }
+ // miss
+ }
+
+ // not in the cache
String inClause = orInClause( authzGroupIds.size(), "SAKAI_REALM.REALM_ID" );
String statement = dbAuthzGroupSql.getSelectRealmUserGroupSql( inClause );
Object[] fields = new Object[authzGroupIds.size()+1];
@@ -758,8 +778,18 @@
fields[i] = authzGroupIds.get(i);
}
fields[authzGroupIds.size()] = userid;
-
- return sqlService().dbRead(statement, fields, null );
+
+ List dbResult = sqlService().dbRead(statement, fields, null );
+
+ // no cache for user so create
+ if (uag == null) {
+ uag = new UserAndGroups(userid);
+ }
+ // add to the users cache
+ uag.addRealmQuery(new HashSet(authzGroupIds), dbResult);
+ authzUserGroupIdsCache.put(userid, uag);
+
+ return dbResult;
}
/**
@@ -2536,6 +2566,86 @@
return grants;
}
+ private class UserAndGroups
+ {
+ String user;
+ long total;
+ long hit;
+ Map> realmsQuery;
+
+ public UserAndGroups(String userid) {
+ this.user = userid;
+ this.total = 0;
+ this.hit = 0;
+ this.realmsQuery = new HashMap>();
+ }
+
+ void addRealmQuery(Set query, List result) {
+ if (query == null || query.size() < 1) return;
+ total++;
+ Long queryHash = computeRealmQueryHash(query);
+
+ if (queryHash != null) {
+ if (result == null) result = Collections.emptyList();
+ realmsQuery.put(queryHash, result);
+ }
+ }
+
+ List getRealmQuery(Set query) {
+ if (query == null || query.size() < 1) return null;
+ List result = null;
+
+ total++;
+ Long queryHash = computeRealmQueryHash(query);
+
+ if (queryHash != null) {
+ if (realmsQuery.containsKey(queryHash)) {
+ result = realmsQuery.get(queryHash);
+ hit++;
+ }
+ }
+ return result;
+ }
+
+ Long computeRealmQueryHash(Set query) {
+
+ if (query == null || query.size() == 0) return null;
+
+ long hash = 0;
+ for (String q : query) {
+ hash += q.hashCode();
+ }
+
+ return Long.valueOf(hash);
+ }
+
+ @Override
+ public int hashCode() {
+ return user.hashCode();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == null) return false;
+ if (this == obj) return true;
+ if (getClass() != obj.getClass())
+ return false;
+ UserAndGroups other = (UserAndGroups) obj;
+ if (user == null) {
+ if (other.user != null)
+ return false;
+ } else if (!user.equals(other.user))
+ return false;
+ return true;
+ }
+
+ @Override
+ public String toString() {
+ return "UserAndGroups [" + (user != null ? "user=" + user : "") + "]" +
+ " size=" + realmsQuery.size() + ", total=" + total + ", hits=" + hit + ", hit ratio=" + (hit * 100) / (float) total;
+ }
+ }
+
public class RealmAndProvider
{
public Integer realmId;
@@ -2957,8 +3067,7 @@
public void update(Observable arg0, Object arg) {
- // No need to listen for events if we are not caching the authz grants
- if (arg == null || !(arg instanceof Event) || !serverConfigurationService().getBoolean("authz.cacheGrants", true))
+ if (arg == null || !(arg instanceof Event))
return;
Event event = (Event) arg;
@@ -2974,10 +3083,16 @@
String realmId = extractEntityId(event.getResource());
if (realmId != null) {
- if (M_log.isDebugEnabled()) {
- M_log.debug("DbAuthzGroupService update(): clear realm role cache for " + realmId);
+ for (String user : getAuthzUsersInGroups(new HashSet(Arrays.asList(realmId)))) {
+ authzUserGroupIdsCache.remove(user);
}
- m_realmRoleGRCache.remove(realmId);
+ if (serverConfigurationService().getBoolean("authz.cacheGrants", true)) {
+ if (M_log.isDebugEnabled()) {
+ M_log.debug("DbAuthzGroupService update(): clear realm role cache for " + realmId);
+ }
+
+ m_realmRoleGRCache.remove(realmId);
+ }
} else {
// This should never happen as the events we generate should always have
// a /realm/ prefix on the resource.