Skip to content

Commit

Permalink
improved repository cache layer
Browse files Browse the repository at this point in the history
  • Loading branch information
1azyman committed Apr 9, 2018
1 parent e1df260 commit 39e395e
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 62 deletions.
Expand Up @@ -63,8 +63,8 @@
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.*;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.LoadingCache;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
Expand All @@ -79,7 +79,6 @@
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.io.Serializable;
import java.util.*;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
Expand Down Expand Up @@ -113,10 +112,10 @@ public class UserProfileServiceImpl implements UserProfileService, UserDetailsSe
private static final long MAX_CACHE_SIZE = 5000;
private static final long MAX_CACHE_TTL = 60 * 1000; // 60 seconds
// caches <user.name, oid>
private LoadingCache<String, String> oidCache;
private Cache<String, String> oidCache;

{
oidCache = (LoadingCache) CacheBuilder.newBuilder()
oidCache = CacheBuilder.newBuilder()
.expireAfterAccess(MAX_CACHE_TTL, TimeUnit.MILLISECONDS)
.concurrencyLevel(20)
.maximumSize(MAX_CACHE_SIZE)
Expand Down Expand Up @@ -194,15 +193,14 @@ public void updateUser(MidPointPrincipal principal) {
private PrismObject<UserType> findByUsername(String username, OperationResult result) throws SchemaException, ObjectNotFoundException {
String oid = null;
try {
oid = oidCache.get(username);
oid = oidCache.getIfPresent(username);
} catch (Exception ex) {
}
if (oid != null) {
Collection<SelectorOptions<GetOperationOptions>> options = Arrays.asList(
SelectorOptions.create(GetOperationOptions.createPointInTimeType(PointInTimeType.CACHED)),
SelectorOptions.create(GetOperationOptions.createStaleness(MAX_CACHE_TTL)));
GetOperationOptions opts = GetOperationOptions.createPointInTimeType(PointInTimeType.CACHED);
opts.setStaleness(MAX_CACHE_TTL);

PrismObject user = repositoryService.getObject(UserType.class, oid, options, result);
PrismObject user = repositoryService.getObject(UserType.class, oid, SelectorOptions.createCollection(opts), result);
String userName = user.getName() != null ? user.getName().getOrig() : null;
if (Objects.equals(username, userName)) {
return user;
Expand Down
Expand Up @@ -73,6 +73,7 @@ public class RepositoryCache implements RepositoryService {
private static final String CONFIGURATION_COMPONENT = "midpoint.repository";
private static final String PROPERTY_CACHE_MAX_TTL = "cacheMaxTTL";

@Deprecated
private static final Set<Class<? extends ObjectType>> GLOBAL_CACHE_SUPPORTED_TYPES;

static {
Expand Down Expand Up @@ -102,12 +103,8 @@ public class RepositoryCache implements RepositoryService {

private Integer modifyRandomDelayRange;

// *****

private CacheManager cacheManager;

// *****

public RepositoryCache() {
}

Expand Down Expand Up @@ -182,70 +179,83 @@ public static String debugDump() {
public <T extends ObjectType> PrismObject<T> getObject(Class<T> type, String oid,
Collection<SelectorOptions<GetOperationOptions>> options, OperationResult parentResult) throws ObjectNotFoundException, SchemaException {

boolean readOnly = GetOperationOptions.isReadOnly(SelectorOptions.findRootOptions(options));
if (!isCacheable(type) || !nullOrHarmlessOptions(options)) {
// local cache not interested in caching this object
log("Cache: PASS {} ({})", oid, type.getSimpleName());

return getObjectTryGlobalCache(type, oid, options, parentResult);
}

if (shouldUseGlobalCache(type, options)) {
// caller is interested in cached value
org.ehcache.Cache<String, CacheObject<T>> gCache = getGlobalCache(type);
CacheObject cacheObject = gCache != null ? gCache.get(oid) : null;
boolean readOnly = GetOperationOptions.isReadOnly(SelectorOptions.findRootOptions(options));

if (cacheObject == null) {
// reload object
return reloadObject(type, oid, options, parentResult, gCache);
Cache cache = getCache();
if (cache == null) {
log("Cache: NULL {} ({})", oid, type.getSimpleName());
} else {
PrismObject<T> object = (PrismObject) cache.getObject(oid);
if (object != null) {
log("Cache: HIT{} {} ({})", readOnly ? "" : "(clone)", oid, type.getSimpleName());
return cloneIfNecessary(object, readOnly);
}
log("Cache: MISS {} ({})", oid, type.getSimpleName());
}

PrismObject<T> object = getObjectTryGlobalCache(type, oid, options, parentResult);
cacheObject(cache, object, readOnly);

return cloneIfNecessary(object, readOnly);
}

private <T extends ObjectType> PrismObject<T> cloneIfNecessary(PrismObject<T> object, boolean readOnly) {
if (readOnly) {
return object;
}

return object.clone();
}

private <T extends ObjectType> PrismObject<T> getObjectTryGlobalCache(Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult parentResult) throws SchemaException, ObjectNotFoundException {
if (!shouldUseGlobalCache(type, options)) {
// caller is not interested in cached value, or global cache doesn't want to cache value
return getObjectInternal(type, oid, options, parentResult);
}

org.ehcache.Cache<String, CacheObject<T>> gCache = getGlobalCache(type);
CacheObject cacheObject = gCache != null ? gCache.get(oid) : null;

PrismObject<T> object;
if (cacheObject == null) {
// reload object
object = reloadObjectInGlobalCache(type, oid, options, parentResult, gCache);
} else {
if (!shouldCheckVersion(cacheObject, options)) {
log("Cache: Global HIT {}-{}", type.getSimpleName(), oid);
return cacheObject.getObject();
log("Cache: Global HIT {} ({})", oid, type.getSimpleName());
object = cacheObject.getObject();
} else {
if (hasVersionChanged(cacheObject, parentResult)) {
// reload object
return reloadObject(type, oid, options, parentResult, gCache);
object = reloadObjectInGlobalCache(type, oid, options, parentResult, gCache);
} else {
// version matches, just update last version check
cacheObject.setLastVersionCheck(System.currentTimeMillis());

log("Cache: Global HIT, version check {}-{}", type.getSimpleName(), oid);
return cacheObject.getObject();
log("Cache: Global HIT, version check {} ({})", oid, type.getSimpleName());
object = cacheObject.getObject();
}
}
}

if (!isCacheable(type) || !nullOrHarmlessOptions(options)) {
log("Cache: PASS {} ({})", oid, type.getSimpleName());
Long startTime = repoOpStart();
try {
return repositoryService.getObject(type, oid, options, parentResult);
} finally {
repoOpEnd(startTime);
}
}
Cache cache = getCache();
if (cache == null) {
log("Cache: NULL {} ({})", oid, type.getSimpleName());
} else {
PrismObject<T> object = (PrismObject) cache.getObject(oid);
if (object != null) {
// TODO: result?
if (readOnly) {
log("Cache: HIT {} ({})", oid, type.getSimpleName());
return object;
} else {
log("Cache: HIT(clone) {} ({})", oid, type.getSimpleName());
return object.clone();
}
}
log("Cache: MISS {} ({})", oid, type.getSimpleName());
}
PrismObject<T> object;
return object;
}

private <T extends ObjectType> PrismObject<T> getObjectInternal(Class<T> type, String oid, Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult parentResult) throws SchemaException, ObjectNotFoundException {
Long startTime = repoOpStart();
try {
object = repositoryService.getObject(type, oid, null, parentResult);
return repositoryService.getObject(type, oid, options, parentResult);
} finally {
repoOpEnd(startTime);
}
cacheObject(cache, object, readOnly);
return object;
}

private <T extends ObjectType> boolean shouldUseGlobalCache(Class<T> type, Collection<SelectorOptions<GetOperationOptions>> options) {
Expand Down Expand Up @@ -857,15 +867,15 @@ private boolean shouldCheckVersion(CacheObject object, Collection<SelectorOption
return object.getLastVersionCheck() + staleness < System.currentTimeMillis();
}

private <T extends ObjectType> PrismObject<T> reloadObject(Class<T> type, String oid,
Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult result, org.ehcache.Cache<String, CacheObject<T>> gCache)
private <T extends ObjectType> PrismObject<T> reloadObjectInGlobalCache(Class<T> type, String oid,
Collection<SelectorOptions<GetOperationOptions>> options,
OperationResult result, org.ehcache.Cache<String, CacheObject<T>> gCache)
throws ObjectNotFoundException, SchemaException {

log("Cache: Global MISS {}-{}", type.getSimpleName(), oid);
log("Cache: Global MISS {} ({})", oid, type.getSimpleName());

try {
PrismObject object = repositoryService.getObject(type, oid, options, result);
PrismObject object = getObjectInternal(type, oid, options, result);
CacheObject<T> cacheObject = new CacheObject<>(object, System.currentTimeMillis());

gCache.put(oid, cacheObject);
Expand Down

0 comments on commit 39e395e

Please sign in to comment.