Skip to content
This repository was archived by the owner on Dec 1, 2022. It is now read-only.

Added support for synchronous writes #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
package com.googlecode.hibernate.memcached;

import com.googlecode.hibernate.memcached.utils.StringUtils;
import java.util.regex.Pattern;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.regex.Pattern;

/**
* KeyStrategy base class that handles concatenation, cleaning, and truncating the final cache key.
* <p/>
Expand All @@ -30,7 +29,8 @@ public String toKey(String regionName, long clearIndex, Object key) {
String keyString = concatenateKey(regionName, clearIndex, transformKeyObject(key));

if (keyString.length() > MAX_KEY_LENGTH) {
throw new IllegalArgumentException("Key is longer than " + MAX_KEY_LENGTH + " characters, try using the Sha1KeyStrategy: " + keyString);
throw new IllegalArgumentException("Key is longer than " + MAX_KEY_LENGTH +
" characters, try using the Sha1KeyStrategy: " + keyString);
}

String finalKey = CLEAN_PATTERN.matcher(keyString).replaceAll("");
Expand All @@ -41,11 +41,6 @@ public String toKey(String regionName, long clearIndex, Object key) {
protected abstract String transformKeyObject(Object key);

protected String concatenateKey(String regionName, long clearIndex, Object key) {
return new StringBuilder()
.append(regionName)
.append(":")
.append(clearIndex)
.append(":")
.append(String.valueOf(key)).toString();
return regionName + ":" + clearIndex + ":" + key;
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package com.googlecode.hibernate.memcached;

import com.googlecode.hibernate.memcached.utils.StringUtils;

/**
* @author Ray Krueger
*/
public abstract class DigestKeyStrategy extends AbstractKeyStrategy {

@Override
protected String transformKeyObject(Object key) {
return key.toString() + ":" + key.hashCode();
}

@Override
protected String concatenateKey(String regionName, long clearIndex, Object key) {
String longKey = super.concatenateKey(regionName, clearIndex, key);
return digest(longKey);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @author Ray Krueger
*/
public class Md5KeyStrategy extends DigestKeyStrategy {
@Override
protected String digest(String key) {
return StringUtils.md5Hex(key);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
*/
package com.googlecode.hibernate.memcached;

import java.util.Map;

import org.hibernate.cache.Cache;
import org.hibernate.cache.CacheException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;

/**
* Wrapper around MemcachedClient instance to provide the bridge between Hiberante and Memcached.
* Uses the regionName given by Hibernate via the {@link com.googlecode.hibernate.memcached.MemcachedCacheProvider}
Expand All @@ -47,7 +47,7 @@
*/
public class MemcachedCache implements Cache {

private final Logger log = LoggerFactory.getLogger(MemcachedCache.class);
private final Logger log = LoggerFactory.getLogger(getClass());

private final String regionName;
private final Memcache memcache;
Expand Down Expand Up @@ -110,22 +110,20 @@ private Object memcacheGet(Object key) {

if (dogpilePreventionEnabled) {
return getUsingDogpilePrevention(objectKey);

} else {
log.debug("Memcache.get({})", objectKey);
return memcache.get(objectKey);
}

log.debug("Memcache.get({})", objectKey);
return memcache.get(objectKey);
}

private Object getUsingDogpilePrevention(String objectKey) {
Map<String, Object> multi;

String dogpileKey = dogpileTokenKey(objectKey);
log.debug("Checking dogpile key: [{}]", dogpileKey);

log.debug("Memcache.getMulti({}, {})", objectKey, dogpileKey);
multi = memcache.getMulti(dogpileKey, objectKey);

Map<String, Object> multi = memcache.getMulti(dogpileKey, objectKey);
if ((multi == null) || (multi.get(dogpileKey) == null)) {
log.debug("Dogpile key ({}) not found updating token and returning null", dogpileKey);
memcache.set(dogpileKey, cacheTimeSeconds, DOGPILE_TOKEN);
Expand Down Expand Up @@ -227,6 +225,7 @@ public Map<?,?> toMap() {
throw new UnsupportedOperationException();
}

@Override
public String toString() {
return "Memcached (" + regionName + ")";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@
*/
package com.googlecode.hibernate.memcached;

import java.lang.reflect.Constructor;
import java.util.Properties;

import org.hibernate.cache.Cache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.CacheProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.Properties;

/**
* Configures an instance of {@link MemcachedCache} for use as a second-level cache in Hibernate.
* To use set the hibernate property <i>hibernate.cache.provider_class</i> to the name of this class.
Expand Down Expand Up @@ -83,7 +83,7 @@
*/
public class MemcachedCacheProvider implements CacheProvider {

private final Logger log = LoggerFactory.getLogger(MemcachedCacheProvider.class);
private final Logger log = LoggerFactory.getLogger(getClass());

private Memcache client;

Expand All @@ -100,13 +100,9 @@ public Cache buildCache(String regionName, Properties properties) throws CacheEx
setKeyStrategy(keyStrategy, cache);
}

cache.setCacheTimeSeconds(
config.getCacheTimeSeconds(regionName)
);
cache.setCacheTimeSeconds(config.getCacheTimeSeconds(regionName));

cache.setClearSupported(
config.isClearSupported(regionName)
);
cache.setClearSupported(config.isClearSupported(regionName));

boolean dogpilePrevention = config.isDogpilePreventionEnabled(regionName);
cache.setDogpilePreventionEnabled(dogpilePrevention);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
* @author Ray Krueger
*/
public class Sha1KeyStrategy extends DigestKeyStrategy {
@Override
protected String digest(String key) {
return StringUtils.sha1Hex(key);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
package com.googlecode.hibernate.memcached.spymemcached;

import com.googlecode.hibernate.memcached.LoggingMemcacheExceptionHandler;
import com.googlecode.hibernate.memcached.Memcache;
import com.googlecode.hibernate.memcached.MemcacheExceptionHandler;
import com.googlecode.hibernate.memcached.utils.StringUtils;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import net.spy.memcached.ConnectionFactory;
import net.spy.memcached.MemcachedClient;
import net.spy.memcached.internal.OperationFuture;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Map;
import com.googlecode.hibernate.memcached.LoggingMemcacheExceptionHandler;
import com.googlecode.hibernate.memcached.Memcache;
import com.googlecode.hibernate.memcached.MemcacheExceptionHandler;
import com.googlecode.hibernate.memcached.utils.StringUtils;

/**
* DOCUMENT ME!
Expand All @@ -21,9 +28,29 @@ public class SpyMemcache implements Memcache {
private MemcacheExceptionHandler exceptionHandler = new LoggingMemcacheExceptionHandler();

private final MemcachedClient memcachedClient;
private final ConnectionFactory connectionFactory;
private final Long operationTimeout;
private final boolean asyncWrites;

public SpyMemcache(MemcachedClient memcachedClient) {
this(memcachedClient, null, true);
}

public SpyMemcache(MemcachedClient memcachedClient, ConnectionFactory connectionFactory, boolean asyncWrites) {
this.memcachedClient = memcachedClient;
this.connectionFactory = connectionFactory;
this.asyncWrites = asyncWrites;
if (connectionFactory == null) {
operationTimeout = null;
}
else {
operationTimeout = connectionFactory.getOperationTimeout();
log.debug("Using operationTimeout {}ms", operationTimeout);
}
}

public ConnectionFactory getConnectionFactory() {
return connectionFactory;
}

public Object get(String key) {
Expand All @@ -48,15 +75,17 @@ public Map<String, Object> getMulti(String... keys) {
public void set(String key, int cacheTimeSeconds, Object o) {
log.debug("MemcachedClient.set({})", key);
try {
memcachedClient.set(key, cacheTimeSeconds, o);
OperationFuture<Boolean> future = memcachedClient.set(key, cacheTimeSeconds, o);
waitIfNecessary(future);
} catch (Exception e) {
exceptionHandler.handleErrorOnSet(key, cacheTimeSeconds, o, e);
}
}

public void delete(String key) {
try {
memcachedClient.delete(key);
OperationFuture<Boolean> future = memcachedClient.delete(key);
waitIfNecessary(future);
} catch (Exception e) {
exceptionHandler.handleErrorOnDelete(key, e);
}
Expand All @@ -78,4 +107,10 @@ public void shutdown() {
public void setExceptionHandler(MemcacheExceptionHandler exceptionHandler) {
this.exceptionHandler = exceptionHandler;
}

protected void waitIfNecessary(OperationFuture<Boolean> future) throws InterruptedException, TimeoutException, ExecutionException {
if (!asyncWrites || operationTimeout == null) {
future.get(operationTimeout, TimeUnit.MILLISECONDS);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import net.spy.memcached.auth.AuthDescriptor;
import net.spy.memcached.auth.PlainCallbackHandler;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.googlecode.hibernate.memcached.Config;
import com.googlecode.hibernate.memcached.Memcache;
import com.googlecode.hibernate.memcached.MemcacheClientFactory;
Expand All @@ -23,6 +26,8 @@
*/
public class SpyMemcacheClientFactory implements MemcacheClientFactory {

private final Logger log = LoggerFactory.getLogger(getClass());

public static final String PROP_SERVERS = Config.PROP_PREFIX + "servers";
public static final String PROP_OPERATION_QUEUE_LENGTH = Config.PROP_PREFIX + "operationQueueLength";
public static final String PROP_READ_BUFFER_SIZE = Config.PROP_PREFIX + "readBufferSize";
Expand All @@ -32,6 +37,7 @@ public class SpyMemcacheClientFactory implements MemcacheClientFactory {
public static final String PROP_DAEMON_MODE = Config.PROP_PREFIX + "daemonMode";
public static final String PROP_USERNAME = Config.PROP_PREFIX + "username";
public static final String PROP_PASSWORD = Config.PROP_PREFIX + "password";
public static final String PROP_ASYNC_WRITES = Config.PROP_PREFIX + "asyncWrites";
private final PropertiesHelper properties;

public SpyMemcacheClientFactory(PropertiesHelper properties) {
Expand All @@ -43,7 +49,13 @@ public Memcache createMemcacheClient() throws Exception {
ConnectionFactory connectionFactory = getConnectionFactory();

MemcachedClient client = new MemcachedClient(connectionFactory, AddrUtil.getAddresses(getServerList()));
return new SpyMemcache(client);

boolean asyncWrites = "true".equals(properties.get(PROP_ASYNC_WRITES, "true"));

log.debug("Creating new SpyMemcache with connectionFactory {} and asyncWrites: {}",
connectionFactory.getClass().getName(), asyncWrites);

return new SpyMemcache(client, connectionFactory, asyncWrites);
}

protected ConnectionFactory getConnectionFactory() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ abstract class AbstractKeyStrategyTestCase extends BaseTestCase {
protected KeyStrategy strategy

protected void setUp() {
super.setUp()
strategy = getKeyStrategy()
}

Expand All @@ -19,11 +20,10 @@ abstract class AbstractKeyStrategyTestCase extends BaseTestCase {
}

void assert_null_key_does_not_validate() {
shouldFailWithCause(IllegalArgumentException.class) {
shouldFail(IllegalArgumentException) {
strategy.toKey(null, 0, null)
}
}

abstract KeyStrategy getKeyStrategy();

abstract KeyStrategy getKeyStrategy()
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package com.googlecode.hibernate.memcached

/**
* DOCUMENT ME!
* @author Ray Krueger
*/
abstract class BaseTestCase extends groovy.util.GroovyTestCase {
abstract class BaseTestCase extends GroovyTestCase {

static {
LoggingConfig.initializeLogging()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
package com.googlecode.hibernate.memcached

/**
* DOCUMENT ME!
* @author Ray Krueger
Expand Down Expand Up @@ -72,7 +73,5 @@ class ConfigTest extends BaseTestCase {

p["hibernate.memcached.memcacheClientFactory"] = "blah"
assertEquals "blah", config.getMemcachedClientFactoryName()


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ package com.googlecode.hibernate.memcached
*/
class HashCodeKeyStrategyTest extends AbstractKeyStrategyTestCase {

public KeyStrategy getKeyStrategy() {
return new HashCodeKeyStrategy()
KeyStrategy getKeyStrategy() {
new HashCodeKeyStrategy()
}

void test() {
Expand All @@ -28,11 +28,10 @@ class HashCodeKeyStrategyTest extends AbstractKeyStrategyTestCase {
}

void test_really_long_key_throws_exception() {
String regionName = ""
250.times {regionName += "x"}
StringBuilder regionName = new StringBuilder()
250.times {regionName << "x"}
shouldFail(IllegalArgumentException) {
getKeyStrategy().toKey(regionName, 0, "blah blah blah")
getKeyStrategy().toKey(regionName.toString(), 0, "blah blah blah")
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ class LoggingConfig {
System.setProperty "net.spy.log.LoggerImpl", "net.spy.log.Log4JLogger"
}

public static void initializeLogging() {
BasicConfigurator.resetConfiguration();
BasicConfigurator.configure();
static void initializeLogging() {
BasicConfigurator.resetConfiguration()
BasicConfigurator.configure()
}

}
}
Loading