
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 java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
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 {
/**
* hashing function to build Entity cache id
*/
private static final HashFunction HASH_FUNCTION = Hashing.murmur3_128();
/**
* Distributed object dataGrid
*/
@NotNull
private final IMap dataGrid;
@Nullable
private final EventListener eventListener;
public CacheableDataProvider(Class clazz,
CassandraClient cassandraClient,
HazelcastInstance hazelcastClient,
MetricsFactory metricsFactory,
@Nullable EventListener eventListener,
ListeningExecutorService executorService) {
super(clazz, cassandraClient, metricsFactory, executorService);
this.eventListener = eventListener;
/**
* Initialize dataGrid
*/
dataGrid = hazelcastClient.getMap(getMapName(clazz));
}
public CacheableDataProvider(Class clazz,
CassandraClient cassandraClient,
HazelcastInstance hazelcastClient,
MetricsFactory metricsFactory,
ListeningExecutorService executorService) {
super(clazz, cassandraClient, metricsFactory, executorService);
this.eventListener = null;
/**
* Initialize dataGrid
*/
dataGrid = hazelcastClient.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);
}
}, getExecutorService());
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);
}, getExecutorService());
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);
}
}, getExecutorService());
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) {
getExecutorService().submit(() -> eventListener.onEntryRemove(entity));
}
return removeFuture;
});
return deleteFuture;
}
/**
* Execute function on entry
*
* @param entry Entry
* @param entryProcessor User Function
*
* @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(Boolean 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;
}
protected final long buildCacheKey(@NotNull T entity) {
Timer.Context time = getMetricsFactory().getTimer(MetricsType.CACHEABLE_DATA_PROVIDER_CREATE_KEY).time();
int primaryKeysSize = getEntityMetadata().getPrimaryKeysSize();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy