Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NEW: Hazelcast supports near cache #19

Open
wants to merge 1 commit 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
61 changes: 50 additions & 11 deletions src/main/java/io/ebean/hazelcast/HzCacheFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import com.hazelcast.topic.ITopic;

import io.ebean.BackgroundExecutor;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheFactory;
import io.ebean.cache.ServerCacheNotification;
import io.ebean.cache.ServerCacheNotify;
import io.ebean.config.DatabaseConfig;
import io.ebeaninternal.server.cache.DefaultServerCache;
import io.ebeaninternal.server.cache.DefaultServerCacheConfig;
import io.ebeaninternal.server.cache.DefaultServerQueryCache;
import org.slf4j.Logger;
Expand All @@ -36,7 +36,7 @@ public final class HzCacheFactory implements ServerCacheFactory {
*/
private static final Logger logger = LoggerFactory.getLogger("io.ebean.cache.HzCacheFactory");

private final ConcurrentHashMap<String, HzQueryCache> queryCaches;
private final ConcurrentHashMap<String, HzLocalCache> localCaches;
private final HazelcastInstance instance;
/**
* Topic used to broadcast query cache invalidation.
Expand All @@ -51,7 +51,7 @@ public final class HzCacheFactory implements ServerCacheFactory {

public HzCacheFactory(DatabaseConfig config, BackgroundExecutor executor) {
this.executor = executor;
this.queryCaches = new ConcurrentHashMap<>();
this.localCaches = new ConcurrentHashMap<>();
if (System.getProperty("hazelcast.logging.type") == null) {
System.setProperty("hazelcast.logging.type", "slf4j");
}
Expand Down Expand Up @@ -107,7 +107,9 @@ public ServerCacheNotify createCacheNotify(ServerCacheNotify listener) {
@Override
public ServerCache createCache(ServerCacheConfig config) {
if (config.isQueryCache()) {
return createQueryCache(config);
return createLocalCache(config, true);
} else if (config.getCacheOptions().isNearCache()) {
return createLocalCache(config, false);
} else {
return createNormalCache(config);
}
Expand All @@ -120,23 +122,38 @@ private ServerCache createNormalCache(ServerCacheConfig config) {
return config.tenantAware(new HzCache(map));
}

private ServerCache createQueryCache(ServerCacheConfig config) {
private ServerCache createLocalCache(ServerCacheConfig config, boolean queryCache) {
synchronized (this) {
HzQueryCache cache = queryCaches.get(config.getCacheKey());
HzLocalCache cache = localCaches.get(config.getCacheKey());
if (cache == null) {
logger.debug("create query cache [{}]", config.getCacheKey());
cache = new HzQueryCache(new DefaultServerCacheConfig(config));
if (queryCache) {
cache = new HzQueryCache(new DefaultServerCacheConfig(config));
} else {
cache = new HzNearCache(new DefaultServerCacheConfig(config));
}
cache.periodicTrim(executor);
queryCaches.put(config.getCacheKey(), cache);
localCaches.put(config.getCacheKey(), cache);
}
assert cache instanceof HzQueryCache == queryCache : "Got wrong cache type: " + cache.getClass().getName() + ", queyCache: " + queryCache;
return config.tenantAware(cache);
}
}

/**
* Either a local QueryCache or a near beanCache.
*/
interface HzLocalCache extends ServerCache {

void periodicTrim(BackgroundExecutor executor);

void invalidate();
}

/**
* Extends normal default implementation with notification of clear() to cluster.
*/
private class HzQueryCache extends DefaultServerQueryCache {
class HzQueryCache extends DefaultServerQueryCache implements HzLocalCache {

HzQueryCache(DefaultServerCacheConfig cacheConfig) {
super(cacheConfig);
Expand All @@ -151,7 +168,29 @@ public void clear() {
/**
* Process the invalidation message coming from the cluster.
*/
private void invalidate() {
@Override
public void invalidate() {
super.clear();
}
}

/**
* Extends normal default implementation with notification of clear() to cluster.
*/
class HzNearCache extends DefaultServerCache implements HzLocalCache {

public HzNearCache(DefaultServerCacheConfig config) {
super(config);
}

@Override
public void clear() {
super.clear();
sendInvalidation(name);
}

@Override
public void invalidate() {
super.clear();
}
}
Expand All @@ -167,7 +206,7 @@ private void sendInvalidation(String key) {
* Process a remote query cache invalidation.
*/
private void processInvalidation(String cacheName) {
HzQueryCache cache = queryCaches.get(cacheName);
HzLocalCache cache = localCaches.get(cacheName);
if (cache != null) {
cache.invalidate();
}
Expand Down
19 changes: 16 additions & 3 deletions src/test/java/io/ebean/hazelcast/HzCacheFactoryTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
import io.ebean.DB;
import io.ebean.Database;
import io.ebean.Ebean;
import io.ebean.EbeanServer;
import io.ebean.cache.ServerCache;
import io.ebean.cache.ServerCacheManager;
import io.ebean.cache.TenantAwareCache;
import io.ebean.cache.TenantAwareKey;
import io.ebeaninternal.server.cache.DefaultServerQueryCache;
import org.example.domain.EAddress;
import org.example.domain.EConfig;
import org.example.domain.ECustomer;
import org.example.domain.EOrder;
import org.junit.Test;
Expand Down Expand Up @@ -94,7 +94,20 @@ public void integration() throws InterruptedException {
ServerCache beanCache = cacheManager.beanCache(ECustomer.class);

assertThat(beanCache).isInstanceOf(TenantAwareCache.class);
assertThat(beanCache.unwrap(HzCache.class)).isInstanceOf(HzCache.class);
assertThat(beanCache.unwrap(ServerCache.class)).isInstanceOf(HzCache.class);

ServerCache queryCache = cacheManager.queryCache(ECustomer.class);
assertThat(queryCache).isInstanceOf(TenantAwareCache.class);
assertThat(queryCache.unwrap(ServerCache.class)).isInstanceOf(HzCacheFactory.HzQueryCache.class);

beanCache = cacheManager.beanCache(EConfig.class);

assertThat(beanCache).isInstanceOf(TenantAwareCache.class);
assertThat(beanCache.unwrap(ServerCache.class)).isInstanceOf(HzCacheFactory.HzNearCache.class);

queryCache = cacheManager.queryCache(EConfig.class);
assertThat(queryCache).isInstanceOf(TenantAwareCache.class);
assertThat(queryCache.unwrap(ServerCache.class)).isInstanceOf(HzCacheFactory.HzQueryCache.class);

// Ebean.update(ECustomer.class)
// .setRaw("name = 'x'")
Expand Down
19 changes: 19 additions & 0 deletions src/test/java/org/example/domain/EConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.example.domain;

import io.ebean.annotation.Cache;

import javax.persistence.Entity;

@Cache(enableQueryCache = true, nearCache = true)
@Entity
public class EConfig extends EBase {
private boolean option1;

public boolean isOption1() {
return option1;
}

public void setOption1(boolean option1) {
this.option1 = option1;
}
}