Permalink
Browse files

Limit the synchronization blocking time on some Cache operations.

Using a Reentrant lock instead of the intrinsic synchronization lock
permits limiting the blocking time to acquire a lock.

Useful on a very busy Cache concurrently accessed by many threads : when
the time to acquire a lock is too high, getting/storing content on the
cache becomes inefficient, and it is then better to fall back to loading
remote resources.

Illustrated by the CacheTest stress test and some traces reported in
mantis 751 ( http://mantis.tokeek.de/view.php?id=751 )
  • Loading branch information...
luccioman committed Jun 14, 2017
1 parent 73ab4a7 commit a7394b479b4a2ecb9bd0c9696355e5d1008b8742
@@ -57,6 +57,16 @@
public final class Cache {
/** Default size in bytes of the backend buffer (buffered bytes before writing to the the file system) */
protected static final int DEFAULT_BACKEND_BUFFER_SIZE = 1024 * 1024 * 2;
/** Default size in bytes of the compressor buffer (buffered bytes before compressing and sending to the backend ) */
protected static final int DEFAULT_COMPRESSOR_BUFFER_SIZE = 6 * 1024 * 1024;
/** Default size in bytes of the response header data base buffer (buffered bytes before writing to the file system) */
protected static final int DEFAULT_RESPONSE_HEADER_BUFFER_SIZE = 2048;
private static final String RESPONSE_HEADER_DB_NAME = "responseHeader.heap";
private static final String FILE_DB_NAME = "file.array";
@@ -69,10 +79,16 @@
private static String prefix;
public static final ConcurrentLog log = new ConcurrentLog("HTCACHE");
public static void init(final File htCachePath, final String peerSalt, final long CacheSizeMax) {
/**
* @param htCachePath folder path for the cache
* @param peerSalt peer identifier
* @param cacheSizeMax maximum cache size in bytes
* @param lockTimeout maximum time (in milliseconds) to acquire a synchronization lock on store() and getContent()
*/
public static void init(final File htCachePath, final String peerSalt, final long cacheSizeMax, final long lockTimeout) {
cachePath = htCachePath;
maxCacheSize = CacheSizeMax;
maxCacheSize = cacheSizeMax;
prefix = peerSalt;
// set/make cache path
@@ -83,33 +99,33 @@ public static void init(final File htCachePath, final String peerSalt, final lon
// open the response header database
final File dbfile = new File(cachePath, RESPONSE_HEADER_DB_NAME);
try {
responseHeaderDB = new MapHeap(dbfile, Word.commonHashLength, Base64Order.enhancedCoder, 2048, 100, ' ');
responseHeaderDB = new MapHeap(dbfile, Word.commonHashLength, Base64Order.enhancedCoder, DEFAULT_RESPONSE_HEADER_BUFFER_SIZE, 100, ' ');
} catch (final IOException e) {
ConcurrentLog.logException(e);
// try a healing
if (dbfile.exists()) {
dbfile.delete();
try {
responseHeaderDB = new MapHeap(dbfile, Word.commonHashLength, Base64Order.enhancedCoder, 2048, 100, ' ');
responseHeaderDB = new MapHeap(dbfile, Word.commonHashLength, Base64Order.enhancedCoder, DEFAULT_RESPONSE_HEADER_BUFFER_SIZE, 100, ' ');
} catch (final IOException ee) {
ConcurrentLog.logException(e);
}
}
}
// open the cache file
try {
fileDBunbuffered = new ArrayStack(new File(cachePath, FILE_DB_NAME), prefix, Base64Order.enhancedCoder, 12, 1024 * 1024 * 2, false, true);
fileDBunbuffered = new ArrayStack(new File(cachePath, FILE_DB_NAME), prefix, Base64Order.enhancedCoder, 12, DEFAULT_BACKEND_BUFFER_SIZE, false, true);
fileDBunbuffered.setMaxSize(maxCacheSize);
fileDB = new Compressor(fileDBunbuffered, 6 * 1024 * 1024);
fileDB = new Compressor(fileDBunbuffered, DEFAULT_COMPRESSOR_BUFFER_SIZE, lockTimeout);
} catch (final IOException e) {
ConcurrentLog.logException(e);
// try a healing
if (cachePath.exists()) {
cachePath.delete();
try {
fileDBunbuffered = new ArrayStack(new File(cachePath, FILE_DB_NAME), prefix, Base64Order.enhancedCoder, 12, 1024 * 1024 * 2, false, true);
fileDBunbuffered = new ArrayStack(new File(cachePath, FILE_DB_NAME), prefix, Base64Order.enhancedCoder, 12, DEFAULT_BACKEND_BUFFER_SIZE, false, true);
fileDBunbuffered.setMaxSize(maxCacheSize);
fileDB = new Compressor(fileDBunbuffered, 6 * 1024 * 1024);
fileDB = new Compressor(fileDBunbuffered, DEFAULT_COMPRESSOR_BUFFER_SIZE, lockTimeout);
} catch (final IOException ee) {
ConcurrentLog.logException(e);
}
@@ -196,16 +212,16 @@ public static void setMaxCacheSize(final long newCacheSize) {
}
/**
* get the current actual cache size
* @return
* Warning : even when the cache is empty,
* the actual cache size may not be zero because heap files still containing zeros after deletions
* @return the current actual cache size stored on disk
*/
public static long getActualCacheSize() {
return fileDBunbuffered.length();
}
/**
* get the current actual cache size
* @return
* @return the current actual number of cached documents stored on disk
*/
public static long getActualCacheDocCount() {
return fileDBunbuffered.size();
@@ -99,7 +99,7 @@
private final int buffersize;
private final boolean trimall;
// the thread pool for the keeperOf executor service
/** the thread pool for the keeperOf executor service */
private final ExecutorService executor;
// use our own formatter to prevent concurrency locks with other processes
@@ -881,6 +881,22 @@ public Boolean call() {
private static final ExecutorService DELETE_EXECUTOR = Executors
.newCachedThreadPool(new NamePrefixThreadFactory(ArrayStack.class.getSimpleName() + ".DELETE_EXECUTOR"));
/**
* Shutdown the delete executor service used to run asynchronously deletions on any ArrayStack instance
*/
public static void shutdownDeleteService() {
DELETE_EXECUTOR.shutdown();
final long timeout = 1;
try {
final boolean terminated = DELETE_EXECUTOR.awaitTermination(timeout, TimeUnit.SECONDS);
if(!terminated) {
ConcurrentLog.warn("ArrayStack", "Delete executor service could not terminated within " + timeout + " second");
}
} catch (InterruptedException e) {
ConcurrentLog.warn("ArrayStack", "Interrupted before termination of the delete executor service");
}
}
/**
* close the BLOB
@@ -890,6 +906,7 @@ public synchronized void close(final boolean writeIDX) {
for (final blobItem bi: this.blobs) bi.blob.close(writeIDX);
this.blobs.clear();
this.blobs = null;
this.executor.shutdown();
}
/**
Oops, something went wrong.

1 comment on commit a7394b4

@smokingwheels

This comment has been minimized.

smokingwheels commented on a7394b4 Jun 16, 2017

Wow that's a lot of code @luccioman

Please sign in to comment.