package org.opencb.opencga.storage.core.cache; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.lang3.StringUtils; import org.opencb.commons.datastore.core.Query; import org.opencb.commons.datastore.core.QueryOptions; import org.opencb.commons.datastore.core.QueryResult; import org.opencb.opencga.storage.core.config.CacheConfiguration; import org.opencb.opencga.storage.core.config.StorageConfiguration; import org.redisson.Config; import org.redisson.Redisson; import org.redisson.RedissonClient; import org.redisson.client.RedisConnectionException; import org.redisson.codec.JsonJacksonCodec; import org.redisson.codec.KryoCodec; import org.redisson.core.RKeys; import org.redisson.core.RMap; import java.util.*; import java.util.regex.Pattern; /** * Created by wasim on 26/10/16. */ public class CacheManager { private StorageConfiguration storageConfiguration; private Config redissonConfig; private Set<String> allowedTypesSet; private RedissonClient redissonClient; private boolean redisState; private static final String PREFIX_DATABASE_KEY = "ocga:"; public CacheManager() { } public CacheManager(StorageConfiguration configuration) { CacheConfiguration cache; if (configuration != null && configuration.getCache() != null) { this.storageConfiguration = configuration; cache = configuration.getCache(); redissonConfig = new Config(); String host = (StringUtils.isNotEmpty(cache.getHost())) ? cache.getHost() : CacheConfiguration.DEFAULT_HOST; redissonConfig.useSingleServer().setAddress(host); String codec = (StringUtils.isNotEmpty(cache.getSerialization())) ? cache.getSerialization() : CacheConfiguration.DEFAULT_SERIALIZATION; this.allowedTypesSet = new HashSet<>(Arrays.asList(cache.getAllowedTypes().split(","))); if (StringUtils.isNotEmpty(cache.getPassword())) { redissonConfig.useSingleServer().setPassword(cache.getPassword()); } if ("KRYO".equalsIgnoreCase(codec)) { redissonConfig.setCodec(new KryoCodec()); } else { redissonConfig.setCodec(new JsonJacksonCodec()); } redisState = true; // redissonClient = Redisson.create(redissonConfig); redissonClient = null; } } public <T> QueryResult<T> get(String key) { QueryResult<T> queryResult = new QueryResult<>(); if (isActive()) { long start = System.currentTimeMillis(); RMap<Integer, Map<String, Object>> map = getRedissonClient().getMap(key); try { // We only retrieve the first field of the HASH, which is the only one that exist. Map<Integer, Map<String, Object>> result = map.getAll(new HashSet<>(Collections.singletonList(0))); if (result != null && !result.isEmpty()) { Object resultMap = result.get(0).get("result"); queryResult = (QueryResult<T>) resultMap; queryResult.setDbTime((int) (System.currentTimeMillis() - start)); } } catch (RedisConnectionException e) { redisState = false; queryResult.setWarningMsg("Unable to connect to Redis Cache, Please query WITHOUT Cache (Falling back to Database)"); return queryResult; } } return queryResult; } public void set(String key, Query query, QueryResult queryResult) { if (isActive()) { if (queryResult.getDbTime() >= storageConfiguration.getCache().getSlowThreshold() && queryResult.getResult().size() >= storageConfiguration.getCache().getMaxResultSize()) { RMap<Integer, Map<String, Object>> map = getRedissonClient().getMap(key); Map<String, Object> record = new HashMap<>(); record.put("query", query); record.put("result", queryResult); try { map.fastPut(0, record); } catch (RedisConnectionException e) { redisState = false; queryResult.setWarningMsg("Unable to connect to Redis Cache, Please query WITHOUT Cache (Falling back to Database)"); } } } } public String createKey(String studyId, String allowedType, Query query, QueryOptions queryOptions) { queryOptions.remove("cache"); queryOptions.remove("sId"); StringBuilder key = new StringBuilder(PREFIX_DATABASE_KEY); key.append(studyId).append(":").append(allowedType); SortedMap<String, SortedSet<Object>> map = new TreeMap<>(); for (String item : query.keySet()) { map.put(item.toLowerCase(), new TreeSet<>(query.getAsStringList(item))); } for (String item : queryOptions.keySet()) { map.put(item.toLowerCase(), new TreeSet<>(queryOptions.getAsStringList(item))); } String sha1 = DigestUtils.sha1Hex(map.toString()); key.append(":").append(sha1); queryOptions.add("cache", "true"); return key.toString(); } public boolean isActive() { return storageConfiguration.getCache().isActive() && redisState; } public boolean isTypeAllowed(String type) { return allowedTypesSet.contains(type); } public void clear() { RKeys redisKeys = getRedissonClient().getKeys(); redisKeys.deleteByPattern(PREFIX_DATABASE_KEY + "*"); } public void clear(Pattern pattern) { RKeys redisKeys = getRedissonClient().getKeys(); redisKeys.deleteByPattern(pattern.toString()); } public void close() { if (redissonClient != null) { redissonClient.shutdown(); redissonClient = null; } } private synchronized RedissonClient getRedissonClient() { if (redissonClient == null) { redissonClient = Redisson.create(redissonConfig); } return redissonClient; } }