/* * Seldon -- open source prediction engine * ======================================= * * Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/) * * ******************************************************************************************** * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * ******************************************************************************************** */ package io.seldon.memcache; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import net.spy.memcached.AddrUtil; import net.spy.memcached.CASMutation; import net.spy.memcached.CASMutator; import net.spy.memcached.CASValue; import net.spy.memcached.ConnectionFactoryBuilder; import net.spy.memcached.DefaultConnectionFactory; import net.spy.memcached.MemcachedClient; import net.spy.memcached.transcoders.SerializingTranscoder; import net.spy.memcached.transcoders.Transcoder; import org.apache.log4j.Logger; public class MemCachePeer { private static Logger logger = Logger.getLogger( MemCachePeer.class.getName() ); private static final int MAX_CAS_RETRIES = 100; // create a static client as most installs only need // a single instance protected static List<MemcachedClient> clients; protected static MemcachedClient theClient = null; public static void initialise(String serverList,int numClients) { try { if (numClients > 1) { clients = new ArrayList<MemcachedClient>(numClients); logger.info("Creating "+numClients+" memcache clients"); for(int i=0;i<numClients;i++) { ConnectionFactoryBuilder cb = new ConnectionFactoryBuilder(new DefaultConnectionFactory()); cb.setOpTimeout(ExceptionSwallowingMemcachedClient.MEMCACHE_OP_TIMEOUT); clients.add(new MemcachedClient(cb.build(),AddrUtil.getAddresses(serverList))); } } else { logger.info("Creating single memcache client"); ConnectionFactoryBuilder cb = new ConnectionFactoryBuilder(new DefaultConnectionFactory()); cb.setOpTimeout(ExceptionSwallowingMemcachedClient.MEMCACHE_OP_TIMEOUT); theClient = new MemcachedClient(cb.build(),AddrUtil.getAddresses(serverList)); } logger.info(String.format("MemcachedClient initialised using serverList[%s]",serverList+" with "+numClients+" clients")); } catch (IOException e) { logger.error("Can't create memcache connection ",e); } } /* * Get random memcache client */ private static MemcachedClient getClient() { if (theClient != null) return theClient; else return clients.get(ThreadLocalRandom.current().nextInt(clients.size())); } public static void delete(String key) { MemcachedClient client = getClient(); if (client != null) try { client.delete(hashKey(key)); } catch (Exception ex) { logger.error("Memcache delete exeption ",ex); } } public static void put(String key,Object obj) { MemcachedClient client = getClient(); if (client != null) try { client.set(hashKey(key), 0, obj); } catch (Exception ex) { logger.error("Memcache put exeption ",ex); } } /* * Expire in seconds */ public static void put(String key,Object obj,int expireSeconds) { MemcachedClient client = getClient(); if (client != null) try { client.set(hashKey(key), expireSeconds, obj); } catch (Exception ex) { logger.error("Memcache put expire exeption ",ex); } } public static Object get(String key) { MemcachedClient client = getClient(); Object myObj=null; if (client != null) { Future<Object> f=client.asyncGet(hashKey(key)); try { myObj=f.get(ExceptionSwallowingMemcachedClient.MEMCACHE_OP_TIMEOUT, TimeUnit.MILLISECONDS); } catch(TimeoutException e) { logger.error("Timeout exception in get ",e); f.cancel(false); } catch (InterruptedException e) { logger.error("Interrupted in get ",e); f.cancel(false); } catch (ExecutionException e) { logger.error("Execution exception in get ",e); f.cancel(false); } } return myObj; } public static CASValue gets(String key) { MemcachedClient client = getClient(); if (client != null) { try { return client.gets(hashKey(key)); } catch (Exception ex) { logger.error("Memcache get exeption ",ex); return null; } } else return null; } public static <T> T cas(String key,CASMutation<T> mutation,T value) { return cas(key,mutation,value,0); } /** * Method to allow CAS * @param <T> * @param key * @param mutation * @param value * @return */ public static <T> T cas(String key,CASMutation<T> mutation,T value,int expireSecs) { MemcachedClient client = getClient(); if (client != null) { Transcoder transcoder = new SerializingTranscoder(); // The mutator who'll do all the low-level stuff. // Set number of retries to limit time taken..its not essential this succeeds CASMutator<T> mutator = new CASMutator<>(client, transcoder,MAX_CAS_RETRIES); // This returns whatever value was successfully stored within the // cache -- either the initial list as above, or a mutated existing // one try { return mutator.cas(hashKey(key), value, expireSecs, mutation); } catch (Exception e) { logger.error("Failed up update hits in cache ",e); return null; } } else return null; } private static String hashKey(String key) { return SecurityHashPeer.md5digest(key); } }