
org.glassfish.grizzly.memcached.GrizzlyMemcachedCache Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2012-2014 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package org.glassfish.grizzly.memcached;
import org.glassfish.grizzly.Buffer;
import org.glassfish.grizzly.CompletionHandler;
import org.glassfish.grizzly.Connection;
import org.glassfish.grizzly.ConnectorHandler;
import org.glassfish.grizzly.Grizzly;
import org.glassfish.grizzly.GrizzlyFuture;
import org.glassfish.grizzly.Processor;
import org.glassfish.grizzly.WriteResult;
import org.glassfish.grizzly.attributes.Attribute;
import org.glassfish.grizzly.attributes.AttributeHolder;
import org.glassfish.grizzly.filterchain.FilterChain;
import org.glassfish.grizzly.memcached.pool.BaseObjectPool;
import org.glassfish.grizzly.memcached.pool.NoValidObjectException;
import org.glassfish.grizzly.memcached.pool.ObjectPool;
import org.glassfish.grizzly.memcached.pool.PoolExhaustedException;
import org.glassfish.grizzly.memcached.pool.PoolableObjectFactory;
import org.glassfish.grizzly.memcached.zookeeper.BarrierListener;
import org.glassfish.grizzly.memcached.zookeeper.CacheServerListBarrierListener;
import org.glassfish.grizzly.memcached.zookeeper.PreferRemoteConfigBarrierListener;
import org.glassfish.grizzly.memcached.zookeeper.ZKClient;
import org.glassfish.grizzly.memcached.zookeeper.ZooKeeperSupportCache;
import org.glassfish.grizzly.nio.transport.TCPNIOConnectorHandler;
import org.glassfish.grizzly.nio.transport.TCPNIOTransport;
import org.glassfish.grizzly.utils.DataStructures;
import java.io.UnsupportedEncodingException;
import java.net.SocketAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* The implementation of the {@link MemcachedCache} based on Grizzly
*
* Basically, this class use {@link BaseObjectPool} for pooling connections of the memcached server
* and {@link ConsistentHashStore} for selecting the memcached server corresponding to the given key.
*
* When a Cache operation is called,
* 1. finding the correct server by consistent hashing
* 2. borrowing the connection from the connection pool
* 3. queueing request and sending packets to the memcached server and waiting for notification
* 4. being waken by Grizzly filter when the response is arrived
* 5. returning the connection to the pool
*
* For the failback of the memcached server, {@link HealthMonitorTask} will be scheduled by {@code healthMonitorIntervalInSecs}.
* If connecting and writing are failed, this cache retries failure operations by {@code retryCount}.
* If retrials also failed, the server will be regarded as not valid and removed in {@link ConsistentHashStore}.
* Sometimes, automatical changes of the server list can cause stale cache data at runtime.
* So this cache provides {@code failover} flag which can turn off the failover/failback.
*
* This cache also supports bulk operations such as {@link #setMulti} as well as {@link #getMulti}.
*
* Example of use:
* {@code
* // creates a CacheManager
* final GrizzlyMemcachedCacheManager manager = new GrizzlyMemcachedCacheManager.Builder().build();
*
* // creates a CacheBuilder
* final GrizzlyMemcachedCache.Builder builder = manager.createCacheBuilder("USER");
* // sets initial servers
* builder.servers(initServerSet);
* // creates the specific Cache
* final MemcachedCache userCache = builder.build();
*
* // if you need to add another server
* userCache.addServer(anotherServerAddress);
*
* // cache operations
* boolean result = userCache.set("name", "foo", expirationTimeoutInSec, false);
* String value = userCache.get("name", false);
* // ...
*
* // shuts down
* manager.shutdown();
* }
*
* @author Bongjae Chang
*/
public class GrizzlyMemcachedCache implements MemcachedCache, ZooKeeperSupportCache {
private static final Logger logger = Grizzly.logger(GrizzlyMemcachedCache.class);
private static final AtomicInteger opaqueIndex = new AtomicInteger();
private final String cacheName;
private final TCPNIOTransport transport;
private final long connectTimeoutInMillis;
private final long writeTimeoutInMillis;
private final long responseTimeoutInMillis;
public static final String CONNECTION_POOL_ATTRIBUTE_NAME = "GrizzlyMemcachedCache.ConnectionPool";
private final Attribute>> connectionPoolAttribute =
Grizzly.DEFAULT_ATTRIBUTE_BUILDER.createAttribute(CONNECTION_POOL_ATTRIBUTE_NAME);
private final ObjectPool> connectionPool;
private final Set servers;
private final long healthMonitorIntervalInSecs;
private final ScheduledFuture> scheduledFuture;
private final HealthMonitorTask healthMonitorTask;
private final ScheduledExecutorService scheduledExecutor;
private final boolean failover;
private final boolean preferRemoteConfig;
private final ConsistentHashStore consistentHash = new ConsistentHashStore();
private final ZKClient zkClient;
private final CacheServerListBarrierListener zkListener;
private String zooKeeperServerListPath;
private MemcachedClientFilter clientFilter;
private GrizzlyMemcachedCache(Builder builder) {
this.cacheName = builder.cacheName;
this.transport = builder.transport;
this.connectTimeoutInMillis = builder.connectTimeoutInMillis;
this.writeTimeoutInMillis = builder.writeTimeoutInMillis;
this.responseTimeoutInMillis = builder.responseTimeoutInMillis;
this.healthMonitorIntervalInSecs = builder.healthMonitorIntervalInSecs;
@SuppressWarnings("unchecked")
final BaseObjectPool.Builder> connectionPoolBuilder =
new BaseObjectPool.Builder>(new PoolableObjectFactory>() {
@Override
public Connection createObject(final SocketAddress key) throws Exception {
final ConnectorHandler connectorHandler =
TCPNIOConnectorHandler.builder(transport).setReuseAddress(true).build();
final Future future = connectorHandler.connect(key);
final Connection connection;
try {
if (connectTimeoutInMillis < 0) {
connection = future.get();
} else {
connection = future.get(connectTimeoutInMillis, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException ie) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + key, ie);
}
throw ie;
} catch (ExecutionException ee) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + key, ee);
}
throw ee;
} catch (TimeoutException te) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + key, te);
}
throw te;
}
if (connection != null) {
connectionPoolAttribute.set(connection, connectionPool);
return connection;
} else {
throw new IllegalStateException("connection must not be null");
}
}
@Override
public void destroyObject(final SocketAddress key, final Connection value) throws Exception {
if (value != null) {
if (value.isOpen()) {
value.closeSilently();
}
final AttributeHolder attributeHolder = value.getAttributes();
if (attributeHolder != null) {
attributeHolder.removeAttribute(CONNECTION_POOL_ATTRIBUTE_NAME);
}
if (logger.isLoggable(Level.FINEST)) {
logger.log(Level.FINEST, "the connection has been destroyed. key={0}, value={1}", new Object[]{key, value});
}
}
}
@Override
public boolean validateObject(final SocketAddress key, final Connection value) throws Exception {
return GrizzlyMemcachedCache.this.validateConnectionWithNoopCommand(value);
// or return GrizzlyMemcachedCache.this.validateConnectionWithVersionCommand(value);
}
});
connectionPoolBuilder.min(builder.minConnectionPerServer);
connectionPoolBuilder.max(builder.maxConnectionPerServer);
connectionPoolBuilder.keepAliveTimeoutInSecs(builder.keepAliveTimeoutInSecs);
connectionPoolBuilder.disposable(builder.allowDisposableConnection);
connectionPoolBuilder.borrowValidation(builder.borrowValidation);
connectionPoolBuilder.returnValidation(builder.returnValidation);
connectionPool = connectionPoolBuilder.build();
this.failover = builder.failover;
this.servers = builder.servers;
if (failover && healthMonitorIntervalInSecs > 0) {
healthMonitorTask = new HealthMonitorTask();
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
scheduledFuture = scheduledExecutor.scheduleWithFixedDelay(healthMonitorTask, healthMonitorIntervalInSecs, healthMonitorIntervalInSecs, TimeUnit.SECONDS);
} else {
healthMonitorTask = null;
scheduledExecutor = null;
scheduledFuture = null;
}
this.preferRemoteConfig = builder.preferRemoteConfig;
if (this.preferRemoteConfig) {
this.zkListener = new PreferRemoteConfigBarrierListener(this, servers);
} else {
this.zkListener = new CacheServerListBarrierListener(this, servers);
}
this.zkClient = builder.zkClient;
}
/**
* {@inheritDoc}
*/
@Override
public void start() {
final Processor processor = transport.getProcessor();
if (!(processor instanceof FilterChain)) {
throw new IllegalStateException("transport's processor has to be a FilterChain");
}
final FilterChain filterChain = (FilterChain) processor;
final int idx = filterChain.indexOfType(MemcachedClientFilter.class);
if (idx == -1) {
throw new IllegalStateException("transport has to have MemcachedClientFilter in the FilterChain");
}
clientFilter = (MemcachedClientFilter) filterChain.get(idx);
if (clientFilter == null) {
throw new IllegalStateException("MemcachedClientFilter should not be null");
}
if (zkClient != null) {
if (!preferRemoteConfig) {
for (final SocketAddress address : servers) {
addServer(address);
}
} else {
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, "local config has been ignored because preferRemoteConfig is true. servers={0}", servers);
}
}
// need to initialize the remote server with local initalServers if the remote server data is empty?
// currently, do nothing
zooKeeperServerListPath = zkClient.registerBarrier(cacheName, zkListener, null);
} else {
for (final SocketAddress address : servers) {
addServer(address);
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void stop() {
if (scheduledFuture != null) {
scheduledFuture.cancel(true);
}
if (scheduledExecutor != null) {
scheduledExecutor.shutdown();
}
servers.clear();
consistentHash.clear();
if (connectionPool != null) {
connectionPool.destroy();
}
if (zkClient != null) {
zkClient.unregisterBarrier(cacheName);
}
}
/**
* {@inheritDoc}
*/
@SuppressWarnings("unchecked")
@Override
public boolean addServer(final SocketAddress serverAddress) {
return addServer(serverAddress, true);
}
@SuppressWarnings("unchecked")
private boolean addServer(final SocketAddress serverAddress, boolean initial) {
if (serverAddress == null) {
return true;
}
if (connectionPool != null) {
try {
connectionPool.createAllMinObjects(serverAddress);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to create min connections in the pool. address=" + serverAddress, e);
}
try {
connectionPool.destroy(serverAddress);
} catch (Exception ignore) {
}
if (!initial) {
return false;
}
}
}
consistentHash.add(serverAddress);
servers.add(serverAddress);
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, "added the server to the consistent hash successfully. address={0}", serverAddress);
}
return true;
}
/**
* {@inheritDoc}
*/
@Override
public void removeServer(final SocketAddress serverAddress) {
removeServer(serverAddress, true);
}
private void removeServer(final SocketAddress serverAddress, final boolean forcibly) {
if (serverAddress == null) {
return;
}
if (!forcibly) {
if (healthMonitorTask != null && healthMonitorTask.failure(serverAddress)) {
consistentHash.remove(serverAddress);
servers.remove(serverAddress);
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, "removed the server from the consistent hash successfully. address={0}", serverAddress);
}
}
} else {
consistentHash.remove(serverAddress);
servers.remove(serverAddress);
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, "removed the server from the consistent hash successfully. address={0}", serverAddress);
}
}
if (connectionPool != null) {
try {
connectionPool.destroy(serverAddress);
if (logger.isLoggable(Level.INFO)) {
logger.log(Level.INFO, "removed the server in the pool successfully. address={0}", serverAddress);
}
} catch (Exception e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to remove connections in the pool", e);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isInServerList(final SocketAddress serverAddress) {
return consistentHash.hasValue(serverAddress);
}
/**
* {@inheritDoc}
*/
@Override
public List getCurrentServerList() {
if (!servers.isEmpty()) {
return new ArrayList(servers);
} else {
return Collections.emptyList();
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean isZooKeeperSupported() {
return zkClient != null;
}
/**
* {@inheritDoc}
*/
@Override
public String getZooKeeperServerListPath() {
if (!isZooKeeperSupported()) {
return null;
}
return zooKeeperServerListPath;
}
/**
* {@inheritDoc}
*/
@Override
public String getCurrentServerListFromZooKeeper() {
if (!isZooKeeperSupported()) {
return null;
}
final byte[] serverListBytes = zkClient.getData(zooKeeperServerListPath, null);
if (serverListBytes == null) {
return null;
}
final String serverListString;
try {
serverListString = new String(serverListBytes, CacheServerListBarrierListener.DEFAULT_SERVER_LIST_CHARSET);
} catch (UnsupportedEncodingException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to decode the server list bytes");
}
return null;
}
return serverListString;
}
/**
* {@inheritDoc}
*/
@Override
public boolean setCurrentServerListOfZooKeeper(final String cacheServerList) {
if (!isZooKeeperSupported()) {
return false;
}
if (cacheServerList == null) {
return false;
}
final byte[] serverListBytes;
try {
serverListBytes = cacheServerList.getBytes(CacheServerListBarrierListener.DEFAULT_SERVER_LIST_CHARSET);
} catch (UnsupportedEncodingException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to eecode the server list");
}
return false;
}
return zkClient.setData(zooKeeperServerListPath, serverListBytes, -1) != null;
}
/**
* {@inheritDoc}
*/
@Override
public void addZooKeeperListener(final BarrierListener listener) {
if (!isZooKeeperSupported()) {
return;
}
zkListener.addCustomListener(listener);
}
/**
* {@inheritDoc}
*/
@Override
public void removeZooKeeperListener(final BarrierListener listener) {
if (!isZooKeeperSupported()) {
return;
}
zkListener.removeCustomListener(listener);
}
/**
* {@inheritDoc}
*/
@Override
public String getName() {
return cacheName;
}
@Override
public boolean set(final K key, final V value, final int expirationInSecs, final boolean noReply) {
return set(key, value, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean set(final K key, final V value, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
builder.op(noReply ? CommandOpcodes.SetQ : CommandOpcodes.Set);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to set. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to set. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public Map setMulti(final Map map, final int expirationInSecs) {
return setMulti(map, expirationInSecs, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map setMulti(final Map map, final int expirationInSecs, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
final Map result = new HashMap();
if (map == null || map.isEmpty()) {
return result;
}
// categorize keys by address
final Map>> categorizedMap = new HashMap>>();
for (K key : map.keySet()) {
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to get the address from the consistent hash in setMulti(). key buffer={0}", keyBuffer);
}
keyWrapper.recycle();
continue;
}
List> keyList = categorizedMap.get(address);
if (keyList == null) {
keyList = new ArrayList>();
categorizedMap.put(address, keyList);
}
keyList.add(keyWrapper);
}
// set multi from server
for (Map.Entry>> entry : categorizedMap.entrySet()) {
final SocketAddress address = entry.getKey();
final List> keyList = entry.getValue();
try {
sendSetMulti(entry.getKey(), keyList, map, expirationInSecs, writeTimeoutInMillis, responseTimeoutInMillis, result);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute setMulti(). address=" + address + ", keySize=" + keyList.size(), ie);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute setMulti(). address=" + address + ", keyList=" + keyList, ie);
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute setMulti(). address=" + address + ", keySize=" + keyList.size(), e);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute setMulti(). address=" + address + ", keyList=" + keyList, e);
}
} finally {
recycleBufferWrappers(keyList);
}
}
return result;
}
@Override
public boolean add(final K key, final V value, final int expirationInSecs, final boolean noReply) {
return add(key, value, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean add(final K key, final V value, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
builder.op(noReply ? CommandOpcodes.AddQ : CommandOpcodes.Add);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to add. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to add. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean replace(final K key, final V value, final int expirationInSecs, final boolean noReply) {
return replace(key, value, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean replace(final K key, final V value, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
builder.op(noReply ? CommandOpcodes.ReplaceQ : CommandOpcodes.Replace);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to replace. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to replace. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean cas(final K key, final V value, final int expirationInSecs, final long cas, final boolean noReply) {
return cas(key, value, expirationInSecs, cas, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean cas(final K key, final V value, final int expirationInSecs, final long cas, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
builder.op(noReply ? CommandOpcodes.SetQ : CommandOpcodes.Set);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.cas(cas);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to set with cas. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to set with cas. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public Map casMulti(final Map> map, final int expirationInSecs) {
return casMulti(map, expirationInSecs, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map casMulti(final Map> map, final int expirationInSecs, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
final Map result = new HashMap();
if (map == null || map.isEmpty()) {
return result;
}
// categorize keys by address
final Map>> categorizedMap = new HashMap>>();
for (K key : map.keySet()) {
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to get the address from the consistent hash in casMulti(). key buffer={0}", keyBuffer);
}
keyWrapper.recycle();
continue;
}
List> keyList = categorizedMap.get(address);
if (keyList == null) {
keyList = new ArrayList>();
categorizedMap.put(address, keyList);
}
keyList.add(keyWrapper);
}
// cas multi from server
for (Map.Entry>> entry : categorizedMap.entrySet()) {
final SocketAddress address = entry.getKey();
final List> keyList = entry.getValue();
try {
sendCasMulti(entry.getKey(), keyList, map, expirationInSecs, writeTimeoutInMillis, responseTimeoutInMillis, result);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute casMulti(). address=" + address + ", keySize=" + keyList.size(), ie);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute casMulti(). address=" + address + ", keyList=" + keyList, ie);
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute casMulti(). address=" + address + ", keySize=" + keyList.size(), e);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute casMulti(). address=" + address + ", keyList=" + keyList, e);
}
} finally {
recycleBufferWrappers(keyList);
}
}
return result;
}
@Override
public boolean append(final K key, final V value, final boolean noReply) {
return append(key, value, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean append(final K key, final V value, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, true);
builder.op(noReply ? CommandOpcodes.AppendQ : CommandOpcodes.Append);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
valueWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to append. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to append. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean prepend(final K key, final V value, final boolean noReply) {
return prepend(key, value, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean prepend(final K key, final V value, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null || value == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, true);
builder.op(noReply ? CommandOpcodes.PrependQ : CommandOpcodes.Prepend);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final BufferWrapper valueWrapper = BufferWrapper.wrap(value, transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
valueWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to prepend. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to prepend. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public Map getMulti(final Set keys) {
return getMulti(keys, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map getMulti(final Set keys, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
final Map result = new HashMap();
if (keys == null || keys.isEmpty()) {
return result;
}
// categorize keys by address
final Map>> categorizedMap = new HashMap>>();
for (K key : keys) {
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to get the address from the consistent hash in getMulti(). key buffer={0}", keyBuffer);
}
keyWrapper.recycle();
continue;
}
List> keyList = categorizedMap.get(address);
if (keyList == null) {
keyList = new ArrayList>();
categorizedMap.put(address, keyList);
}
keyList.add(keyWrapper);
}
// get multi from server
for (Map.Entry>> entry : categorizedMap.entrySet()) {
final SocketAddress address = entry.getKey();
final List> keyList = entry.getValue();
try {
sendGetMulti(entry.getKey(), keyList, writeTimeoutInMillis, responseTimeoutInMillis, result);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute getMulti(). address=" + address + ", keySize=" + keyList.size(), ie);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute getMulti(). address=" + address + ", keyList=" + keyList, ie);
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute getMulti(). address=" + address + ", keySize=" + keyList.size(), e);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute getMulti(). address=" + address + ", keyList=" + keyList, e);
}
} finally {
recycleBufferWrappers(keyList);
}
}
return result;
}
@Override
public V get(final K key, final boolean noReply) {
return get(key, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@SuppressWarnings("unchecked")
@Override
public V get(final K key, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return null;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.op(noReply ? CommandOpcodes.GetQ : CommandOpcodes.Get);
builder.noReply(false);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return null;
}
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (V) result;
} else {
return null;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get. address=" + address + ", request=" + request, ie);
}
return null;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get. address=" + address + ", request=" + request, e);
}
return null;
} finally {
builder.recycle();
}
}
@Override
public ValueWithKey getKey(final K key, final boolean noReply) {
return getKey(key, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@SuppressWarnings("unchecked")
@Override
public ValueWithKey getKey(final K key, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return null;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.op(noReply ? CommandOpcodes.GetKQ : CommandOpcodes.GetK);
builder.noReply(false);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return null;
}
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (ValueWithKey) result;
} else {
return null;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get with key. address=" + address + ", request=" + request, ie);
}
return null;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get with key. address=" + address + ", request=" + request, e);
}
return null;
} finally {
builder.recycle();
}
}
@Override
public ValueWithCas gets(final K key, final boolean noReply) {
return gets(key, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@SuppressWarnings("unchecked")
@Override
public ValueWithCas gets(final K key, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return null;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.op(noReply ? CommandOpcodes.GetsQ : CommandOpcodes.Gets);
builder.noReply(false);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return null;
}
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (ValueWithCas) result;
} else {
return null;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get with cas. address=" + address + ", request=" + request, ie);
}
return null;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get with cas. address=" + address + ", request=" + request, e);
}
return null;
} finally {
builder.recycle();
}
}
@Override
public Map> getsMulti(final Set keys) {
return getsMulti(keys, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map> getsMulti(final Set keys, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
final Map> result = new HashMap>();
if (keys == null || keys.isEmpty()) {
return result;
}
// categorize keys by address
final Map>> categorizedMap = new HashMap>>();
for (K key : keys) {
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to get the address from the consistent hash in getsMulti(). key buffer={0}", keyBuffer);
}
keyWrapper.recycle();
continue;
}
List> keyList = categorizedMap.get(address);
if (keyList == null) {
keyList = new ArrayList>();
categorizedMap.put(address, keyList);
}
keyList.add(keyWrapper);
}
// get multi from server
for (Map.Entry>> entry : categorizedMap.entrySet()) {
final SocketAddress address = entry.getKey();
final List> keyList = entry.getValue();
try {
sendGetsMulti(entry.getKey(), keyList, writeTimeoutInMillis, responseTimeoutInMillis, result);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute getsMulti(). address=" + address + ", keySize=" + keyList.size(), ie);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute getsMulti(). address=" + address + ", keyList=" + keyList, ie);
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute getsMulti(). address=" + address + ", keySize=" + keyList.size(), e);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute getsMulti(). address=" + address + ", keyList=" + keyList, e);
}
} finally {
recycleBufferWrappers(keyList);
}
}
return result;
}
@Override
public V gat(final K key, final int expirationInSecs, final boolean noReply) {
return gat(key, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@SuppressWarnings("unchecked")
@Override
public V gat(final K key, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return null;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, false);
builder.op(noReply ? CommandOpcodes.GATQ : CommandOpcodes.GAT);
builder.noReply(false);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return null;
}
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (V) result;
} else {
return null;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get and touch. address=" + address + ", request=" + request, ie);
}
return null;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get and touch. address=" + address + ", request=" + request, e);
}
return null;
} finally {
builder.recycle();
}
}
@Override
public boolean delete(final K key, final boolean noReply) {
return delete(key, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean delete(final K key, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.op(noReply ? CommandOpcodes.DeleteQ : CommandOpcodes.Delete);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to delete. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to delete. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public Map deleteMulti(final Set keys) {
return deleteMulti(keys, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map deleteMulti(final Set keys, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
final Map result = new HashMap();
if (keys == null || keys.isEmpty()) {
return result;
}
// categorize keys by address
final Map>> categorizedMap = new HashMap>>();
for (K key : keys) {
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "failed to get the address from the consistent hash in deleteMulti(). key buffer={0}", keyBuffer);
}
keyWrapper.recycle();
continue;
}
List> keyList = categorizedMap.get(address);
if (keyList == null) {
keyList = new ArrayList>();
categorizedMap.put(address, keyList);
}
keyList.add(keyWrapper);
}
// delete multi from server
for (Map.Entry>> entry : categorizedMap.entrySet()) {
final SocketAddress address = entry.getKey();
final List> keyList = entry.getValue();
try {
sendDeleteMulti(entry.getKey(), keyList, writeTimeoutInMillis, responseTimeoutInMillis, result);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute deleteMulti(). address=" + address + ", keySize=" + keyList.size(), ie);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute deleteMulti(). address=" + address + ", keyList=" + keyList, ie);
}
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute deleteMulti(). address=" + address + ", keySize=" + keyList.size(), e);
} else if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to execute deleteMulti(). address=" + address + ", keyList=" + keyList, e);
}
} finally {
recycleBufferWrappers(keyList);
}
}
return result;
}
@Override
public long incr(final K key, final long delta, final long initial, final int expirationInSecs, final boolean noReply) {
return incr(key, delta, initial, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public long incr(final K key, final long delta, final long initial, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return -1;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, false);
builder.op(noReply ? CommandOpcodes.IncrementQ : CommandOpcodes.Increment);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
builder.delta(delta);
builder.initial(initial);
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return -1;
}
try {
if (noReply) {
sendNoReply(address, request);
return -1;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (Long) result;
} else {
return -1;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to increase. address=" + address + ", request=" + request, ie);
}
return -1;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to increase. address=" + address + ", request=" + request, e);
}
return -1;
} finally {
builder.recycle();
}
}
@Override
public long decr(final K key, final long delta, final long initial, final int expirationInSecs, final boolean noReply) {
return decr(key, delta, initial, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public long decr(final K key, final long delta, final long initial, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return -1;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, false);
builder.op(noReply ? CommandOpcodes.DecrementQ : CommandOpcodes.Decrement);
builder.noReply(noReply);
builder.opaque(noReply ? generateOpaque() : 0);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
builder.delta(delta);
builder.initial(initial);
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return -1;
}
try {
if (noReply) {
sendNoReply(address, request);
return -1;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result != null) {
return (Long) result;
} else {
return -1;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to decrease. address=" + address + ", request=" + request, ie);
}
return -1;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to decrease. address=" + address + ", request=" + request, e);
}
return -1;
} finally {
builder.recycle();
}
}
@Override
public String saslAuth(final SocketAddress address, final String mechanism, final byte[] data) {
return saslAuth(address, mechanism, data, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public String saslAuth(final SocketAddress address, final String mechanism, final byte[] data, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
throw new UnsupportedOperationException();
}
@Override
public String saslStep(final SocketAddress address, final String mechanism, final byte[] data) {
return saslStep(address, mechanism, data, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public String saslStep(final SocketAddress address, final String mechanism, final byte[] data, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
throw new UnsupportedOperationException();
}
@Override
public String saslList(final SocketAddress address) {
return saslList(address, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public String saslList(final SocketAddress address, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
throw new UnsupportedOperationException();
}
@Override
public Map stats(final SocketAddress address) {
return stats(address, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map stats(final SocketAddress address, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
return statsItems(address, null, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public Map statsItems(final SocketAddress address, final String item) {
return statsItems(address, item, writeTimeoutInMillis, responseTimeoutInMillis);
}
@SuppressWarnings("unchecked")
@Override
public Map statsItems(final SocketAddress address, final String item, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return null;
}
if (connectionPool == null) {
throw new IllegalStateException("connection pool must not be null");
}
if (clientFilter == null) {
throw new IllegalStateException("client filter must not be null");
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, item != null, false);
builder.op(CommandOpcodes.Stat);
builder.noReply(false);
if (item != null) {
builder.originKey(item);
final BufferWrapper keyWrapper = BufferWrapper.wrap(item, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
}
final MemcachedRequest request = builder.build();
final Connection connection;
try {
connection = connectionPool.borrowObject(address, connectTimeoutInMillis);
} catch (PoolExhaustedException pee) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", pee);
}
return null;
} catch (NoValidObjectException nvoe) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", nvoe);
}
return null;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", te);
}
return null;
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", ie);
}
return null;
}
try {
final GrizzlyFuture> future = connection.write(new MemcachedRequest[]{request});
try {
if (writeTimeoutInMillis > 0) {
future.get(writeTimeoutInMillis, TimeUnit.MILLISECONDS);
} else {
future.get();
}
} catch (ExecutionException ee) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", request=" + request, ee);
}
return null;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", request=" + request, te);
}
return null;
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. address=" + address + ", request=" + request, ie);
}
return null;
} finally {
builder.recycle();
}
final Map stats = new HashMap();
while (true) {
final ValueWithKey value;
try {
value = clientFilter.getCorrelatedResponse(connection, request, responseTimeoutInMillis);
} catch (TimeoutException te) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. timeout=" + responseTimeoutInMillis + "ms", te);
}
break;
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the stats. timeout=" + responseTimeoutInMillis + "ms", ie);
}
break;
}
if (value != null) {
final String statKey = value.getKey();
final String statValue = value.getValue();
if (statKey != null && statValue != null) {
stats.put(statKey, statValue);
} else {
break;
}
} else {
break;
}
}
return stats;
} finally {
returnConnectionSafely(address, connection);
}
}
@Override
public boolean quit(final SocketAddress address, final boolean noReply) {
return quit(address, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean quit(final SocketAddress address, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, false, false);
builder.op(noReply ? CommandOpcodes.QuitQ : CommandOpcodes.Quit);
builder.noReply(noReply);
final MemcachedRequest request = builder.build();
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to quit. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to quit. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean flushAll(final SocketAddress address, final int expirationInSecs, final boolean noReply) {
return flushAll(address, expirationInSecs, noReply, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean flushAll(final SocketAddress address, final int expirationInSecs, final boolean noReply, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(expirationInSecs > 0, false, false);
builder.op(noReply ? CommandOpcodes.FlushQ : CommandOpcodes.Flush);
builder.noReply(noReply);
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
try {
if (noReply) {
sendNoReply(address, request);
return true;
} else {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to flush. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to flush. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean touch(final K key, final int expirationInSecs) {
return touch(key, expirationInSecs, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean touch(final K key, final int expirationInSecs, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (key == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, false);
builder.op(CommandOpcodes.Touch);
builder.noReply(false);
builder.originKey(key);
final BufferWrapper keyWrapper = BufferWrapper.wrap(key, transport.getMemoryManager());
final Buffer keyBuffer = keyWrapper.getBuffer();
builder.key(keyBuffer);
keyWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
final MemcachedRequest request = builder.build();
final SocketAddress address = consistentHash.get(keyBuffer.toByteBuffer());
if (address == null) {
builder.recycle();
return false;
}
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to touch. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to touch. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean noop(final SocketAddress address) {
return noop(address, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean noop(final SocketAddress address, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, false, false);
builder.op(CommandOpcodes.Noop);
builder.opaque(generateOpaque());
builder.noReply(false);
final MemcachedRequest request = builder.build();
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the noop operation. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the noop operation. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public boolean verbosity(final SocketAddress address, final int verbosity) {
return verbosity(address, verbosity, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public boolean verbosity(final SocketAddress address, final int verbosity, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, false, false);
builder.op(CommandOpcodes.Verbosity);
builder.noReply(false);
builder.verbosity(verbosity);
final MemcachedRequest request = builder.build();
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the vebosity operation. address=" + address + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the vebosity operation. address=" + address + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
@Override
public String version(final SocketAddress address) {
return version(address, writeTimeoutInMillis, responseTimeoutInMillis);
}
@Override
public String version(final SocketAddress address, final long writeTimeoutInMillis, final long responseTimeoutInMillis) {
if (address == null) {
return null;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, false, false);
builder.op(CommandOpcodes.Version);
builder.noReply(false);
final MemcachedRequest request = builder.build();
try {
final Object result = send(address, request, writeTimeoutInMillis, responseTimeoutInMillis);
if (result instanceof String) {
return (String) result;
} else {
return null;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the version operation. address=" + address + ", request=" + request, ie);
}
return null;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the version operation. address=" + address + ", request=" + request, e);
}
return null;
} finally {
builder.recycle();
}
}
private boolean validateConnectionWithNoopCommand(final Connection connection) {
if (connection == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, false, false);
builder.op(CommandOpcodes.Noop);
builder.opaque(generateOpaque());
builder.noReply(false);
final MemcachedRequest request = builder.build();
try {
final GrizzlyFuture> future = connection.write(new MemcachedRequest[]{request});
if (writeTimeoutInMillis > 0) {
future.get(writeTimeoutInMillis, TimeUnit.MILLISECONDS);
} else {
future.get();
}
final Object result = clientFilter.getCorrelatedResponse(connection, request, responseTimeoutInMillis);
if (result instanceof Boolean) {
return (Boolean) result;
} else {
return false;
}
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the noop operation. connection=" + connection + ", request=" + request, ie);
}
return false;
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to execute the noop operation. connection=" + connection + ", request=" + request, e);
}
return false;
} finally {
builder.recycle();
}
}
private boolean validateConnectionWithVersionCommand(final Connection connection) {
if (connection == null) {
return false;
}
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, false, false);
builder.op(CommandOpcodes.Version);
builder.noReply(false);
final MemcachedRequest request = builder.build();
try {
final GrizzlyFuture> future = connection.write(new MemcachedRequest[]{request});
if (writeTimeoutInMillis > 0) {
future.get(writeTimeoutInMillis, TimeUnit.MILLISECONDS);
} else {
future.get();
}
if (clientFilter == null) {
throw new IllegalStateException("client filter must not be null");
}
final Object result = clientFilter.getCorrelatedResponse(connection, request, responseTimeoutInMillis);
return result instanceof String;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, te);
}
return false;
} catch (ExecutionException ee) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, ee);
}
return false;
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, ie);
}
Thread.currentThread().interrupt();
return false;
} finally {
builder.recycle();
}
}
private void sendNoReply(final SocketAddress address, final MemcachedRequest request)
throws PoolExhaustedException, NoValidObjectException, TimeoutException, InterruptedException {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
if (request == null) {
throw new IllegalArgumentException("request must not be null");
}
if (connectionPool == null) {
throw new IllegalStateException("connection pool must not be null");
}
final Connection connection;
try {
connection = connectionPool.borrowObject(address, connectTimeoutInMillis);
} catch (PoolExhaustedException pee) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", pee);
}
throw pee;
} catch (NoValidObjectException nvoe) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", nvoe);
}
removeServer(address, false);
throw nvoe;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", te);
}
throw te;
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", ie);
}
throw ie;
}
if (request.isNoReply()) {
connection.write(new MemcachedRequest[]{request}, new CompletionHandler>() {
@Override
public void cancelled() {
returnConnectionSafely(address, connection);
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to send the request. request={0}, connection={1}", new Object[]{request, connection});
}
}
@Override
public void failed(Throwable t) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to send the request. request=" + request + ", connection=" + connection, t);
}
}
@Override
public void completed(WriteResult result) {
returnConnectionSafely(address, connection);
}
@Override
public void updated(WriteResult result) {
}
});
}
}
private boolean sendNoReplySafely(final Connection connection, final MemcachedRequest request) {
if (connection == null) {
throw new IllegalArgumentException("connection must not be null");
}
if (request == null) {
throw new IllegalArgumentException("request must not be null");
}
if (request.isNoReply()) {
GrizzlyFuture future = connection.write(new MemcachedRequest[]{request});
try {
future.get(writeTimeoutInMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, ie);
}
Thread.currentThread().interrupt();
return false;
} catch (ExecutionException ee) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, ee);
}
return false;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to check the connection. connection=" + connection, te);
}
return false;
}
}
return true;
}
private Object send(final SocketAddress address,
final MemcachedRequest request,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis) throws TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException, ExecutionException {
if (address == null) {
throw new IllegalArgumentException("address must not be null");
}
if (request == null) {
throw new IllegalArgumentException("request must not be null");
}
if (request.isNoReply()) {
throw new IllegalStateException("request type is no reply");
}
return sendInternal(address, new MemcachedRequest[]{request}, writeTimeoutInMillis, responseTimeoutInMillis, null);
}
private void sendGetMulti(final SocketAddress address,
final List> keyList,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map result) throws ExecutionException, TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException {
if (address == null || keyList == null || keyList.isEmpty() || result == null) {
return;
}
// make multi requests based on key list
final MemcachedRequest[] requests = new MemcachedRequest[keyList.size()];
final BufferWrapper.BufferType keyType = !keyList.isEmpty() ? keyList.get(0).getType() : null;
for (int i = 0; i < keyList.size(); i++) {
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.originKeyType(keyType);
builder.originKey(keyList.get(i).getOrigin());
builder.key(keyList.get(i).getBuffer());
if (i == keyList.size() - 1) {
builder.noReply(false);
builder.op(CommandOpcodes.Get);
} else {
builder.noReply(true);
builder.op(CommandOpcodes.GetQ);
builder.opaque(generateOpaque());
}
requests[i] = builder.build();
builder.recycle();
}
sendInternal(address, requests, writeTimeoutInMillis, responseTimeoutInMillis, result);
}
private void sendGetsMulti(final SocketAddress address,
final List> keyList,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map> result) throws ExecutionException, TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException {
if (address == null || keyList == null || keyList.isEmpty() || result == null) {
return;
}
// make multi requests based on key list
final MemcachedRequest[] requests = new MemcachedRequest[keyList.size()];
final BufferWrapper.BufferType keyType = !keyList.isEmpty() ? keyList.get(0).getType() : null;
for (int i = 0; i < keyList.size(); i++) {
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
builder.originKeyType(keyType);
builder.originKey(keyList.get(i).getOrigin());
builder.key(keyList.get(i).getBuffer());
if (i == keyList.size() - 1) {
builder.noReply(false);
builder.op(CommandOpcodes.Gets);
} else {
builder.noReply(true);
builder.op(CommandOpcodes.GetsQ);
builder.opaque(generateOpaque());
}
requests[i] = builder.build();
builder.recycle();
}
sendInternal(address, requests, writeTimeoutInMillis, responseTimeoutInMillis, result);
}
private void sendSetMulti(final SocketAddress address,
final List> keyList,
final Map map,
final int expirationInSecs,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map result) throws ExecutionException, TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException {
if (address == null || keyList == null || keyList.isEmpty() || result == null) {
return;
}
// make multi requests based on key list
final MemcachedRequest[] requests = new MemcachedRequest[keyList.size()];
final BufferWrapper.BufferType keyType = !keyList.isEmpty() ? keyList.get(0).getType() : null;
for (int i = 0; i < keyList.size(); i++) {
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
if (i == keyList.size() - 1) {
builder.op(CommandOpcodes.Set);
builder.noReply(false);
builder.opaque(0);
} else {
builder.op(CommandOpcodes.SetQ);
builder.noReply(true);
builder.opaque(generateOpaque());
}
final K originKey = keyList.get(i).getOrigin();
builder.originKeyType(keyType);
builder.originKey(originKey);
builder.key(keyList.get(i).getBuffer());
final BufferWrapper valueWrapper = BufferWrapper.wrap(map.get(originKey), transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.expirationInSecs(expirationInSecs);
requests[i] = builder.build();
builder.recycle();
}
sendInternal(address, requests, writeTimeoutInMillis, responseTimeoutInMillis, result);
}
private void sendCasMulti(final SocketAddress address,
final List> keyList,
final Map> map,
final int expirationInSecs,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map result) throws ExecutionException, TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException {
if (address == null || keyList == null || keyList.isEmpty() || result == null) {
return;
}
// make multi requests based on key list
final MemcachedRequest[] requests = new MemcachedRequest[keyList.size()];
final BufferWrapper.BufferType keyType = !keyList.isEmpty() ? keyList.get(0).getType() : null;
for (int i = 0; i < keyList.size(); i++) {
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(true, true, true);
if (i == keyList.size() - 1) {
builder.op(CommandOpcodes.Set);
builder.noReply(false);
builder.opaque(0);
} else {
builder.op(CommandOpcodes.SetQ);
builder.noReply(true);
builder.opaque(generateOpaque());
}
final K originKey = keyList.get(i).getOrigin();
builder.originKeyType(keyType);
builder.originKey(originKey);
builder.key(keyList.get(i).getBuffer());
final ValueWithCas vwc = map.get(originKey);
if (vwc != null) {
final BufferWrapper valueWrapper = BufferWrapper.wrap(vwc.getValue(), transport.getMemoryManager());
builder.value(valueWrapper.getBuffer());
builder.flags(valueWrapper.getType().flags);
valueWrapper.recycle();
builder.cas(vwc.getCas());
}
builder.expirationInSecs(expirationInSecs);
requests[i] = builder.build();
builder.recycle();
}
sendInternal(address, requests, writeTimeoutInMillis, responseTimeoutInMillis, result);
}
private void sendDeleteMulti(final SocketAddress address,
final List> keyList,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map result) throws ExecutionException, TimeoutException, InterruptedException, PoolExhaustedException, NoValidObjectException {
if (address == null || keyList == null || keyList.isEmpty() || result == null) {
return;
}
// make multi requests based on key list
final MemcachedRequest[] requests = new MemcachedRequest[keyList.size()];
final BufferWrapper.BufferType keyType = !keyList.isEmpty() ? keyList.get(0).getType() : null;
for (int i = 0; i < keyList.size(); i++) {
final MemcachedRequest.Builder builder = MemcachedRequest.Builder.create(false, true, false);
if (i == keyList.size() - 1) {
builder.op(CommandOpcodes.Delete);
builder.noReply(false);
builder.opaque(0);
} else {
builder.op(CommandOpcodes.DeleteQ);
builder.noReply(true);
builder.opaque(generateOpaque());
}
final K originKey = keyList.get(i).getOrigin();
builder.originKeyType(keyType);
builder.originKey(originKey);
builder.key(keyList.get(i).getBuffer());
requests[i] = builder.build();
builder.recycle();
}
sendInternal(address, requests, writeTimeoutInMillis, responseTimeoutInMillis, result);
}
private Object sendInternal(final SocketAddress address,
final MemcachedRequest[] requests,
final long writeTimeoutInMillis,
final long responseTimeoutInMillis,
final Map result) throws PoolExhaustedException, NoValidObjectException, InterruptedException, TimeoutException, ExecutionException {
if (address == null || requests == null || requests.length == 0) {
return null;
}
if (connectionPool == null) {
throw new IllegalStateException("connection pool must not be null");
}
if (clientFilter == null) {
throw new IllegalStateException("client filter must not be null");
}
final boolean isMulti = (result != null);
final Connection connection;
try {
connection = connectionPool.borrowObject(address, connectTimeoutInMillis);
} catch (PoolExhaustedException pee) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", pee);
}
throw pee;
} catch (NoValidObjectException nvoe) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", nvoe);
}
removeServer(address, false);
throw nvoe;
} catch (TimeoutException te) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", te);
}
throw te;
} catch (InterruptedException ie) {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "failed to get the connection. address=" + address + ", timeout=" + connectTimeoutInMillis + "ms", ie);
}
throw ie;
}
try {
final GrizzlyFuture> future = connection.write(requests);
if (writeTimeoutInMillis > 0) {
future.get(writeTimeoutInMillis, TimeUnit.MILLISECONDS);
} else {
future.get();
}
} catch (ExecutionException ee) {
// invalid connection
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw ee;
} catch (TimeoutException te) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw te;
} catch (InterruptedException ie) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw ie;
} catch (Exception unexpected) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw new ExecutionException(unexpected);
}
final Object response;
try {
if (!isMulti) {
response = clientFilter.getCorrelatedResponse(connection, requests[0], responseTimeoutInMillis);
} else {
response = clientFilter.getMultiResponse(connection, requests, responseTimeoutInMillis, result);
}
} catch (TimeoutException te) {
returnConnectionSafely(address, connection);
throw te;
} catch (InterruptedException ie) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw ie;
} catch (Exception unexpected) {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
throw new ExecutionException(unexpected);
}
returnConnectionSafely(address, connection);
return response;
}
private void returnConnectionSafely(final SocketAddress address, final Connection connection) {
if (address == null || connection == null) {
return;
}
if (connection.isOpen()) {
try {
connectionPool.returnObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to return the connection. address=" + address + ", connection=" + connection, e);
}
}
} else {
try {
connectionPool.removeObject(address, connection);
} catch (Exception e) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to remove the connection. address=" + address + ", connection=" + connection, e);
}
}
}
}
private static void recycleBufferWrappers(List> bufferWrapperList) {
if (bufferWrapperList == null) {
return;
}
for (BufferWrapper wrapper : bufferWrapperList) {
wrapper.recycle();
}
}
private static int generateOpaque() {
return opaqueIndex.getAndIncrement() & 0x7fffffff;
}
private class HealthMonitorTask implements Runnable {
private final Map failures = DataStructures.getConcurrentMap();
private final Map revivals = DataStructures.getConcurrentMap();
private final AtomicBoolean running = new AtomicBoolean();
public boolean failure(final SocketAddress address) {
if (address == null) {
return true;
}
if (failures.get(address) == null && revivals.get(address) == null) {
failures.put(address, Boolean.TRUE);
return true;
} else {
return false;
}
}
@SuppressWarnings("unchecked")
@Override
public void run() {
if (transport == null) {
throw new IllegalStateException("transport must not be null");
}
if (!running.compareAndSet(false, true)) {
return;
}
try {
revivals.clear();
final Set failuresSet = failures.keySet();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "try to check the failures in health monitor. failed list hint={0}, interval={1}secs", new Object[]{failuresSet, healthMonitorIntervalInSecs});
} else if (logger.isLoggable(Level.INFO) && !failuresSet.isEmpty()) {
logger.log(Level.INFO, "try to check the failures in health monitor. failed list hint={0}, interval={1}secs", new Object[]{failuresSet, healthMonitorIntervalInSecs});
}
for (SocketAddress failure : failuresSet) {
try {
// get the temporary connection
final ConnectorHandler connectorHandler = TCPNIOConnectorHandler.builder(transport).setReuseAddress(true).build();
Future future = connectorHandler.connect(failure);
final Connection connection;
try {
if (connectTimeoutInMillis < 0) {
connection = future.get();
} else {
connection = future.get(connectTimeoutInMillis, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException ie) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the connection in health monitor. address=" + failure, ie);
}
continue;
} catch (ExecutionException ee) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the connection in health monitor. address=" + failure, ee);
}
continue;
} catch (TimeoutException te) {
if (!future.cancel(false) && future.isDone()) {
final Connection c = future.get();
if (c != null && c.isOpen()) {
c.closeSilently();
}
}
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "failed to get the connection in health monitor. address=" + failure, te);
}
continue;
}
if (validateConnectionWithVersionCommand(connection)) {
failures.remove(failure);
revivals.put(failure, Boolean.TRUE);
}
if (connection.isOpen()) {
connection.closeSilently();
}
} catch (Throwable t) {
if (logger.isLoggable(Level.SEVERE)) {
logger.log(Level.SEVERE, "unexpected exception thrown", t);
}
}
}
final Set revivalsSet = revivals.keySet();
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "try to restore revivals in health monitor. revival list hint={0}, interval={1}secs", new Object[]{revivalsSet, healthMonitorIntervalInSecs});
} else if (logger.isLoggable(Level.INFO) && !revivalsSet.isEmpty()) {
logger.log(Level.INFO, "try to restore revivals in health monitor. revival list hint={0}, interval={1}secs", new Object[]{revivalsSet, healthMonitorIntervalInSecs});
}
for (SocketAddress revival : revivalsSet) {
if (!addServer(revival, false)) {
if (logger.isLoggable(Level.WARNING)) {
logger.log(Level.WARNING, "the revival was failed again in health monitor. revival={0}", revival);
}
failures.put(revival, Boolean.TRUE);
}
}
} finally {
running.set(false);
}
}
}
public static class Builder implements CacheBuilder {
private final String cacheName;
private final GrizzlyMemcachedCacheManager manager;
private final TCPNIOTransport transport;
private Set servers = Collections.synchronizedSet(new HashSet());
private long connectTimeoutInMillis = 5000; // 5secs
private long writeTimeoutInMillis = 5000; // 5secs
private long responseTimeoutInMillis = 10000; // 10secs
private long healthMonitorIntervalInSecs = 60; // 1 min
private boolean failover = true;
private boolean preferRemoteConfig = false;
// connection pool config
private int minConnectionPerServer = 5;
private int maxConnectionPerServer = Integer.MAX_VALUE;
private long keepAliveTimeoutInSecs = 30 * 60; // 30 min
private boolean allowDisposableConnection = false;
private boolean borrowValidation = false;
private boolean returnValidation = false;
private final ZKClient zkClient;
public Builder(final String cacheName, final GrizzlyMemcachedCacheManager manager, final TCPNIOTransport transport) {
this.cacheName = cacheName;
this.manager = manager;
this.transport = transport;
this.zkClient = manager.getZkClient();
}
/**
* {@inheritDoc}
*/
@Override
public GrizzlyMemcachedCache build() {
final GrizzlyMemcachedCache cache = new GrizzlyMemcachedCache(this);
cache.start();
if (!manager.addCache(cache)) {
cache.stop();
throw new IllegalStateException("failed to add the cache because the CacheManager already stopped or the same cache name existed");
}
return cache;
}
/**
* Set global connect-timeout
*
* If the given param is negative, the timeout is infite.
* Default is 5000.
*
* @param connectTimeoutInMillis connect-timeout in milli-seconds
* @return this builder
*/
public Builder connectTimeoutInMillis(final long connectTimeoutInMillis) {
this.connectTimeoutInMillis = connectTimeoutInMillis;
return this;
}
/**
* Set global write-timeout
*
* If the given param is negative, the timeout is infite.
* Default is 5000.
*
* @param writeTimeoutInMillis write-timeout in milli-seconds
* @return this builder
*/
public Builder writeTimeoutInMillis(final long writeTimeoutInMillis) {
this.writeTimeoutInMillis = writeTimeoutInMillis;
return this;
}
/**
* Set global response-timeout
*
* If the given param is negative, the timeout is infite.
* Default is 10000.
*
* @param responseTimeoutInMillis response-timeout in milli-seconds
* @return this builder
*/
public Builder responseTimeoutInMillis(final long responseTimeoutInMillis) {
this.responseTimeoutInMillis = responseTimeoutInMillis;
return this;
}
/**
* Set connection pool's min
*
* Default is 5.
*
* @param minConnectionPerServer connection pool's min
* @return this builder
* @see BaseObjectPool.Builder#min(int)
*/
public Builder minConnectionPerServer(final int minConnectionPerServer) {
this.minConnectionPerServer = minConnectionPerServer;
return this;
}
/**
* Set connection pool's max
*
* Default is {@link Integer#MAX_VALUE}
*
* @param maxConnectionPerServer connection pool's max
* @return this builder
* @see BaseObjectPool.Builder#max(int)
*/
public Builder maxConnectionPerServer(final int maxConnectionPerServer) {
this.maxConnectionPerServer = maxConnectionPerServer;
return this;
}
/**
* Set connection pool's KeepAliveTimeout
*
* Default is 1800.
*
* @param keepAliveTimeoutInSecs connection pool's KeepAliveTimeout in seconds
* @return this builder
* @see BaseObjectPool.Builder#keepAliveTimeoutInSecs(long)
*/
public Builder keepAliveTimeoutInSecs(final long keepAliveTimeoutInSecs) {
this.keepAliveTimeoutInSecs = keepAliveTimeoutInSecs;
return this;
}
/**
* Set health monitor's interval
*
* This cache will schedule HealthMonitorTask with this interval.
* HealthMonitorTask will check the failure servers periodically and detect the revived server.
* If the given parameter is negative, this cache never schedules HealthMonitorTask
* so this behavior is similar to seting {@code failover} to be false.
* Default is 60.
*
* @param healthMonitorIntervalInSecs interval in seconds
* @return this builder
*/
public Builder healthMonitorIntervalInSecs(final long healthMonitorIntervalInSecs) {
this.healthMonitorIntervalInSecs = healthMonitorIntervalInSecs;
return this;
}
/**
* Allow or disallow disposable connections
*
* Default is false.
*
* @param allowDisposableConnection true if this cache allows disposable connections
* @return this builder
*/
public Builder allowDisposableConnection(final boolean allowDisposableConnection) {
this.allowDisposableConnection = allowDisposableConnection;
return this;
}
/**
* Enable or disable the connection validation when the connection is borrowed from the connection pool
*
* Default is false.
*
* @param borrowValidation true if this cache should make sure the borrowed connection is valid
* @return this builder
*/
public Builder borrowValidation(final boolean borrowValidation) {
this.borrowValidation = borrowValidation;
return this;
}
/**
* Enable or disable the connection validation when the connection is returned to the connection pool
*
* Default is false.
*
* @param returnValidation true if this cache should make sure the returned connection is valid
* @return this builder
*/
public Builder returnValidation(final boolean returnValidation) {
this.returnValidation = returnValidation;
return this;
}
/**
* Set initial servers
*
* @param servers server set
* @return this builder
*/
public Builder servers(final Set servers) {
if (servers != null && !servers.isEmpty()) {
this.servers.addAll(servers);
}
return this;
}
/**
* Enable or disable failover/failback
*
* Default is true.
*
* @param failover true if this cache should support failover/failback when the server is failed or revived
* @return this builder
*/
public Builder failover(final boolean failover) {
this.failover = failover;
return this;
}
/**
* @deprecated not supported anymore
*/
public Builder retryCount(final int retryCount) {
return this;
}
public Builder preferRemoteConfig(final boolean preferRemoteConfig) {
this.preferRemoteConfig = preferRemoteConfig;
return this;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("GrizzlyMemcachedCache{");
sb.append("cacheName='").append(cacheName).append('\'');
sb.append(", transport=").append(transport);
sb.append(", connectTimeoutInMillis=").append(connectTimeoutInMillis);
sb.append(", writeTimeoutInMillis=").append(writeTimeoutInMillis);
sb.append(", responseTimeoutInMillis=").append(responseTimeoutInMillis);
sb.append(", connectionPool=").append(connectionPool);
sb.append(", servers=").append(servers);
sb.append(", healthMonitorIntervalInSecs=").append(healthMonitorIntervalInSecs);
sb.append(", failover=").append(failover);
sb.append(", preferRemoteConfig=").append(preferRemoteConfig);
sb.append(", zkListener=").append(zkListener);
sb.append(", zooKeeperServerListPath='").append(zooKeeperServerListPath).append('\'');
sb.append('}');
return sb.toString();
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy