org.infinispan.persistence.async.AsyncCacheWriter Maven / Gradle / Ivy
package org.infinispan.persistence.async;
import net.jcip.annotations.GuardedBy;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.AsyncStoreConfiguration;
import org.infinispan.configuration.cache.Configuration;
import org.infinispan.persistence.spi.PersistenceException;
import org.infinispan.persistence.modifications.Modification;
import org.infinispan.persistence.modifications.Remove;
import org.infinispan.persistence.modifications.Store;
import org.infinispan.commons.util.CollectionFactory;
import org.infinispan.persistence.spi.CacheWriter;
import org.infinispan.persistence.spi.InitializationContext;
import org.infinispan.marshall.core.MarshalledEntry;
import org.infinispan.persistence.support.DelegatingCacheWriter;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
/**
* The AsyncCacheWriter is a delegating CacheStore that buffers changes and writes them asynchronously to
* the underlying CacheStore.
*
* Read operations are done synchronously, taking into account the current state of buffered changes.
*
* There is no provision for exception handling for problems encountered with the underlying store
* during a write operation, and the exception is just logged.
*
* When configuring the loader, use the following element:
*
* <async enabled="true" />
*
* to define whether cache loader operations are to be asynchronous. If not specified, a cache loader operation is
* assumed synchronous and this decorator is not applied.
*
* Write operations affecting same key are now coalesced so that only the final state is actually stored.
*
*
* @author Manik Surtani
* @author Galder Zamarreño
* @author Sanne Grinovero
* @author Karsten Blees
* @author Mircea Markus
* @since 4.0
*/
public class AsyncCacheWriter extends DelegatingCacheWriter {
private static final Log log = LogFactory.getLog(AsyncCacheWriter.class);
private static final boolean trace = log.isTraceEnabled();
private static final AtomicInteger threadId = new AtomicInteger(0);
private ExecutorService executor;
private Thread coordinator;
private int concurrencyLevel;
private long shutdownTimeout;
private String cacheName;
protected BufferLock stateLock;
@GuardedBy("stateLock")
protected final AtomicReference state = new AtomicReference();
protected AsyncStoreConfiguration asyncConfiguration;
public AsyncCacheWriter(CacheWriter delegate) {
super(delegate);
}
@Override
public void init(InitializationContext ctx) {
super.init(ctx);
this.asyncConfiguration = ctx.getConfiguration().async();
Cache cache = ctx.getCache();
Configuration cacheCfg = cache != null ? cache.getCacheConfiguration() : null;
concurrencyLevel = cacheCfg != null ? cacheCfg.locking().concurrencyLevel() : 16;
long cacheStopTimeout = cacheCfg != null ? cacheCfg.transaction().cacheStopTimeout() : 30000;
Long configuredAsyncStopTimeout = this.asyncConfiguration.shutdownTimeout();
cacheName = cache != null ? cache.getName() : null;
// Async store shutdown timeout cannot be bigger than
// the overall cache stop timeout, so limit it accordingly.
if (configuredAsyncStopTimeout >= cacheStopTimeout) {
shutdownTimeout = Math.round(cacheStopTimeout * 0.90);
log.asyncStoreShutdownTimeoutTooHigh(configuredAsyncStopTimeout, cacheStopTimeout, shutdownTimeout);
} else {
shutdownTimeout = configuredAsyncStopTimeout;
}
}
@Override
public void start() {
log.debugf("Async cache loader starting %s", this);
state.set(newState(false, null));
stateLock = new BufferLock(asyncConfiguration.modificationQueueSize());
int poolSize = asyncConfiguration.threadPoolSize();
executor = new ThreadPoolExecutor(0, poolSize, 120L, TimeUnit.SECONDS, new LinkedBlockingQueue(),
new ThreadFactory() {
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r, "AsyncStoreProcessor-" + cacheName + "-" + threadId.getAndIncrement());
t.setDaemon(true);
return t;
}
});
coordinator = new Thread(new AsyncStoreCoordinator(), "AsyncStoreCoordinator-" + cacheName);
coordinator.setDaemon(true);
coordinator.start();
}
@Override
public void stop() {
if (trace) log.tracef("Stop async store %s", this);
stateLock.writeLock(1);
state.get().stopped = true;
stateLock.writeUnlock();
try {
coordinator.join(shutdownTimeout);
if (coordinator.isAlive())
log.errorAsyncStoreNotStopped();
} catch (InterruptedException e) {
log.interruptedWaitingAsyncStorePush(e);
Thread.currentThread().interrupt();
}
}
@Override
public void write(MarshalledEntry entry) {
put(new Store(entry.getKey(), entry), 1);
}
@Override
public boolean delete(Object key) {
put(new Remove(key), 1);
return true;
}
protected void applyModificationsSync(List mods) throws PersistenceException {
for (Modification m : mods) {
switch (m.getType()) {
case STORE:
actual.write(((Store) m).getStoredValue());
break;
case REMOVE:
actual.delete(((Remove) m).getKey());
break;
default:
throw new IllegalArgumentException("Unknown modification type " + m.getType());
}
}
}
State newState(boolean clear, State next) {
ConcurrentMap
© 2015 - 2025 Weber Informatics LLC | Privacy Policy