com.turbospaces.ebean.AbstractEbeanCache Maven / Gradle / Ivy
package com.turbospaces.ebean;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Objects;
import org.apache.commons.io.input.UnsynchronizedByteArrayInputStream;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import com.turbospaces.cache.BlockhoundCacheWrapper;
import com.turbospaces.cfg.ApplicationProperties;
import com.turbospaces.common.PlatformUtil;
import io.ebean.cache.ServerCacheConfig;
import io.ebean.cache.ServerCacheStatistics;
import io.ebean.cache.ServerCacheType;
import io.ebean.cache.TenantAwareKey;
import io.ebean.config.CurrentTenantProvider;
import io.ebeaninternal.server.cache.CachedBeanData;
import io.ebeaninternal.server.cache.CachedManyIds;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class AbstractEbeanCache implements LocalCache {
private final ApplicationProperties props;
private final String cacheKey;
private final BlockhoundCacheWrapper cache;
private final TenantAwareKey tenantAwareKey;
private final ServerCacheConfig config;
protected AbstractEbeanCache(
ApplicationProperties props,
String cacheKey,
BlockhoundCacheWrapper cache,
CurrentTenantProvider tenantProvider,
ServerCacheConfig config) {
this.props = Objects.requireNonNull(props);
this.cacheKey = Objects.requireNonNull(cacheKey);
this.cache = Objects.requireNonNull(cache);
this.tenantAwareKey = new TenantAwareKey(tenantProvider);
this.config = Objects.requireNonNull(config);
}
@Override
public Object get(Object id) {
String key = key(id);
return cache.getIfPresent(key);
}
@Override
public void put(Object id, Object obj) {
String key = key(id);
cache.put(key, obj);
}
@Override
public void remove(Object id) {
String key = key(id);
cache.invalidate(key);
}
@Override
public void clear() {
cache.invalidateAll();
}
@Override
public int size() {
return (int) cache.size();
}
@Override
public void cleanUp() {
cache.cleanUp();
}
@Override
public int hitRatio() {
return (int) (cache.stats().hitRate() * 100);
}
@Override
public ServerCacheStatistics statistics(boolean reset) {
return LocalCache.stats(cacheKey, cache);
}
@Override
public ServerCacheType type() {
return config.getType();
}
@Override
public String cacheKey() {
return cacheKey;
}
@Override
public String toString() {
return cacheKey();
}
@Override
public int hashCode() {
return Objects.hash(cacheKey());
}
@Override
public boolean equals(Object obj) {
return Objects.equals(cacheKey, ((AbstractEbeanCache) obj).cacheKey());
}
@Override
public void onRemove(byte[] data) {
String key = SerializationUtils.deserialize(data);
log.trace("onRemove {} by key: {}", cacheKey, key);
cache.invalidate(key);
}
@Override
public int onClear() {
String idx = props.CLOUD_APP_INSTANCE_INDEX.get();
int size = size();
log.debug("onClear {} items: {} on ({})", cacheKey, size, idx);
cache.invalidateAll();
return size;
}
protected void onPut(byte[] keyData, byte[] valueData) throws IOException {
//
// ~ fast and reusable input stream
//
UnsynchronizedByteArrayInputStream kstream = UnsynchronizedByteArrayInputStream.builder().setByteArray(keyData).get();
UnsynchronizedByteArrayInputStream vstream = UnsynchronizedByteArrayInputStream.builder().setByteArray(valueData).get();
String key = PlatformUtil.deserialize(kstream);
switch (type()) {
case NATURAL_KEY: {
Object read = PlatformUtil.deserialize(vstream);
cache.put(key, read);
break;
}
case BEAN: {
try (ObjectInputStream ois = new ObjectInputStream(vstream)) {
CachedBeanData read = new CachedBeanData();
read.readExternal(ois);
//
// ~ hack under strict concurrency (do not override the data which can be stale on other nodes)
// ~ we will push the data back shortly
//
boolean toPut = true;
CachedBeanData current = (CachedBeanData) cache.getIfPresent(key);
if (Objects.nonNull(current)) {
toPut = read.getVersion() > current.getVersion(); // ~ READ committed
}
if (toPut) {
log.trace("onPut {} by key: {} bean value: {}", cacheKey, key, read);
cache.put(key, read);
}
} catch (ClassNotFoundException | IOException err) {
ExceptionUtils.wrapAndThrow(err);
}
break;
}
case COLLECTION_IDS: {
try (ObjectInputStream ois = new ObjectInputStream(vstream)) {
CachedManyIds read = new CachedManyIds();
read.readExternal(ois);
log.trace("onPut {} by key: {} ids value: {}", cacheKey, key, read);
cache.put(key, read);
} catch (ClassNotFoundException | IOException err) {
ExceptionUtils.wrapAndThrow(err);
}
break;
}
default: {
throw new IllegalArgumentException("unexpected cache type: " + type());
}
}
}
protected String key(Object key) {
return tenantAwareKey.key(key).toString();
}
}