/* * Copyright (c) 2008-2012 EMC Corporation * All Rights Reserved */ package com.emc.storageos.volumecontroller.impl.utils; import com.emc.storageos.db.client.model.DataObject; import com.emc.storageos.db.client.DbClient; import java.lang.ref.SoftReference; import java.net.URI; import java.util.*; /** * This class should be used instead of DbClient if activity is expected to query DB for the same set of objects. * This class is NOT thread safe. The cache should be local with a scope of a single thread. * An example where it is appropriate might be in matching and placement activities run by apisvc and also, to a lesser extend by * controllers. * Activities are moved through a net of methods where each method is likely to query DB for the same set of objects. * To avoid OOM, the cache contains SoftReferences to cached DataObjects. Java's GC guarantees to clear them before JVM * can go OOM. * Cache is not kept in sync with DB. Any update to the database is not visible to the cache. * Use methods * clearCache(URI cached) * or * refresh (Class<T> clazz, URI id) * to eliminate stale objects from the cache. */ public class ObjectLocalCache { private int maxHashSize = 50000; /** * Maps URI/String to a Soft Reference to DataObjects * Soft References are guaranteed to get GCed before the process goes OOM */ private final Map<URI, SoftReference<DataObject>> CACHE_MAP = new HashMap<>(); public ObjectLocalCache(DbClient dbClient) { this.dbClient = dbClient; enabled = true; } public ObjectLocalCache(DbClient dbClient, boolean enabled) { this.dbClient = dbClient; this.enabled = enabled; } private boolean enabled; private DbClient dbClient; public void setDbClient(DbClient dbClient) { this.dbClient = dbClient; } public DbClient getDbClient() { return this.dbClient; } public void setMaxHashSize(int max) { this.maxHashSize = max; } public int getMaxHashSize() { return maxHashSize; } public boolean getEnabled() { return enabled; } public void setEnable(boolean flag) { enabled = flag; } /** * Returns thread local map instance * * @return */ private Map<URI, SoftReference<DataObject>> getCached() { return Collections.unmodifiableMap(CACHE_MAP); } public <T extends DataObject> T queryObject(Class<T> clazz, URI id) { T obj = getObject(id, clazz); if (obj == null) { obj = dbClient.queryObject(clazz, id); if (obj != null && !obj.getInactive()) { putObject(id, obj); } } return obj; } public <T extends DataObject> List<T> queryObject(Class<T> clazz, Collection<URI> ids) { List<URI> missed = new ArrayList<>(); List<T> result = new ArrayList<>(); for (URI id : ids) { T obj = getObject(id, clazz); if (obj == null) { missed.add(id); } else { result.add(obj); } } List<T> more = dbClient.queryObject(clazz, missed); for (T obj : more) { if (!obj.getInactive()) { putObject(obj.getId(), obj); } result.add(obj); } return result; } private <T extends DataObject> T getObject(URI id, Class<T> clazz) { SoftReference<DataObject> objRef = CACHE_MAP.get(id); return objRef == null ? null : clazz.cast(objRef.get()); } private <T extends DataObject> void putObject(URI id, T object) { if (enabled && CACHE_MAP.size() < maxHashSize) { CACHE_MAP.put(id, new SoftReference<DataObject>(object)); } } public void clearCache() { CACHE_MAP.clear(); } public void clearCache(URI cached) { CACHE_MAP.remove(cached); } public <T extends DataObject> T refresh(Class<T> clazz, URI id) { T obj = dbClient.queryObject(clazz, id); if (obj == null) { clearCache(id); } else { putObject(id, obj); } return obj; } }