
com.fnklabs.draenei.orm.CacheableDataProvider Maven / Gradle / Ivy
package com.fnklabs.draenei.orm;
import com.codahale.metrics.Timer;
import com.fnklabs.draenei.CassandraClient;
import com.fnklabs.draenei.MetricsFactory;
import com.fnklabs.draenei.orm.exception.CanNotBuildEntryCacheKey;
import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;
import com.google.common.util.concurrent.*;
import com.hazelcast.core.ExecutionCallback;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.core.ICompletableFuture;
import com.hazelcast.core.IMap;
import com.hazelcast.map.EntryProcessor;
import org.apache.commons.lang3.StringUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
public class CacheableDataProvider extends DataProvider {
public static final Logger LOGGER = LoggerFactory.getLogger(CacheableDataProvider.class);
/**
* Distributed object dataGrid
*/
@NotNull
private final IMap dataGrid;
@Nullable
private final EventListener eventListener;
public CacheableDataProvider(Class clazz,
CassandraClient cassandraClient,
HazelcastInstance hazelcastInstance,
MetricsFactory metricsFactory,
@Nullable EventListener eventListener) {
super(clazz, cassandraClient, hazelcastInstance, metricsFactory);
this.eventListener = eventListener;
/**
* Initialize dataGrid
*/
dataGrid = hazelcastInstance.getMap(getMapName(clazz));
}
public CacheableDataProvider(Class clazz,
CassandraClient cassandraClient,
HazelcastInstance hazelcastInstance,
MetricsFactory metricsFactory,
ListeningExecutorService executorService) {
super(clazz, cassandraClient, hazelcastInstance, metricsFactory);
this.eventListener = null;
/**
* Initialize dataGrid
*/
dataGrid = hazelcastInstance.getMap(getMapName(clazz));
}
@Override
public ListenableFuture findOneAsync(Object... keys) {
Timer.Context time = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_PROVIDER_FIND).time();
long cacheKey = buildCacheKey(keys);
ListenableFuture getFromDataGridFuture = getFromDataGrid(cacheKey);
ListenableFuture findEntityFuture = Futures.transform(getFromDataGridFuture, (T result) -> {
if (result != null) {
getMetricsFactory().getCounter(MetricsType.CACHEABLE_DATA_PROVIDER_HITS).inc();
return getFromDataGridFuture;
}
// try to load entity from DB
ListenableFuture findFuture = super.findOneAsync(keys);
Futures.addCallback(findFuture, new FutureCallback() {
@Override
public void onSuccess(T result) {
if (result != null) {
result.setCacheKey(cacheKey);
getMap().putAsync(cacheKey, result);
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.warn("Can't put to cache", t);
}
});
return findFuture;
});
monitorFuture(time, findEntityFuture);
return findEntityFuture;
}
@Override
public ListenableFuture saveAsync(@NotNull T entity) {
Timer.Context time = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_PROVIDER_SAVE).time();
Timer.Context putAsyncTimer = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_PROVIDER_PUT_ASYNC).time();
ListenableFuture putToCacheFuture = executeOnEntry(entity, new PutToCacheOperation(entity));
monitorFuture(putAsyncTimer, putToCacheFuture);
ListenableFuture saveFuture = Futures.transform(putToCacheFuture, (Boolean result) -> {
return super.saveAsync(entity);
});
Futures.addCallback(saveFuture, new FutureCallback() {
@Override
public void onSuccess(@Nullable Boolean result) {
if (result != null && result && eventListener != null) {
eventListener.onEntrySave(entity);
}
}
@Override
public void onFailure(Throwable t) {
LOGGER.warn("Cant save entry", t);
}
});
return monitorFuture(time, saveFuture, result -> result);
}
@Override
public ListenableFuture removeAsync(@NotNull T entity) {
Timer.Context timer = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_PROVIDER_REMOVE).time();
long key = entity.getCacheKey() == null ? buildCacheKey(entity) : entity.getCacheKey();
SettableFuture deleteFuture = SettableFuture.create();
ICompletableFuture removeFromDataGridFuture = (ICompletableFuture) getMap().removeAsync(key);
removeFromDataGridFuture.andThen(new ExecutionCallback() {
@Override
public void onResponse(T response) {
deleteFuture.set(true);
}
@Override
public void onFailure(Throwable t) {
deleteFuture.setException(t);
}
});
monitorFuture(timer, deleteFuture, result -> {
ListenableFuture removeFuture = super.removeAsync(entity);
if (eventListener != null) {
eventListener.onEntryRemove(entity);
}
return removeFuture;
});
return deleteFuture;
}
/**
* Execute function on entry
*
* @param entry Entry
* @param entryProcessor User Function
* @param Return object type from EntryProcessor
*
* @return Future for current operation
*/
protected ListenableFuture executeOnEntry(@NotNull T entry, @NotNull EntryProcessor entryProcessor) {
Long entityId = entry.getCacheKey() == null ? buildCacheKey(entry) : entry.getCacheKey();
ICompletableFuture completableFuture = (ICompletableFuture) getMap().submitToKey(entityId, entryProcessor);
SettableFuture responseFuture = SettableFuture.create();
completableFuture.andThen(new ExecutionCallback() {
@Override
public void onResponse(O response) {
responseFuture.set(response);
}
@Override
public void onFailure(Throwable t) {
responseFuture.setException(t);
}
});
return responseFuture;
}
@NotNull
protected String getMapName() {
return getMap().getName();
}
protected IMap getMap() {
return dataGrid;
}
@NotNull
private String getMapName(Class clazz) {
return StringUtils.lowerCase(clazz.getName());
}
@NotNull
private ListenableFuture getFromDataGrid(long cacheKey) {
Timer.Context timer = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_GET_FROM_DATA_GRID).time();
// try to find entity in DataGrid then try to load it from DB
ICompletableFuture future = (ICompletableFuture) getMap().getAsync(cacheKey);
SettableFuture responseFuture = SettableFuture.create();
future.andThen(new ExecutionCallback() {
@Override
public void onResponse(T response) {
timer.stop();
responseFuture.set(response);
}
@Override
public void onFailure(Throwable t) {
timer.stop();
responseFuture.setException(t);
}
});
return responseFuture;
}
protected enum MetricsType implements MetricsFactory.Type {
CACHEABLE_DATA_PROVIDER_FIND,
CACHEABLE_DATA_PROVIDER_SAVE,
CACHEABLE_DATA_PROVIDER_CREATE_KEY,
CACHEABLE_DATA_PROVIDER_HITS,
CACHEABLE_DATA_PROVIDER_REMOVE, CACHEABLE_DATA_GET_FROM_DATA_GRID, CACHEABLE_DATA_PROVIDER_PUT_ASYNC;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy