package org.apache.hadoop.hdfs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.URI;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.conf.Configuration;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.data.Stat;
public class CachingAvatarZooKeeperClient extends AvatarZooKeeperClient {
private String cacheDir = null;
private boolean useCache = false;
private Map<String, Long> lastCacheMod = Collections
.synchronizedMap(new HashMap<String, Long>());
public CachingAvatarZooKeeperClient(Configuration conf, Watcher watcher) {
super(conf, watcher);
this.cacheDir = conf.get("fs.ha.zookeeper.cache.dir", "/tmp");
this.useCache = conf.getBoolean("fs.ha.zookeeper.cache", false);
}
private String getFromCache(URI address, Stat stat, boolean retry)
throws IOException, InterruptedException {
FileLock lock = tryLock(true);
String result = null;
try {
File cache = new File(cacheDir, address.getAuthority());
if (!cache.exists()) {
return null;
}
lastCacheMod.put(address.getAuthority(), cache.lastModified());
BufferedReader reader = new BufferedReader(new FileReader(cache));
result = reader.readLine();
} finally {
lock.release();
}
return result;
}
private FileLock tryLock(boolean retry) throws IOException,
InterruptedException {
File lockFile = new File(cacheDir, ".lock");
RandomAccessFile file = new RandomAccessFile(lockFile, "rws");
FileLock lock = null;
do {
try {
lock = file.getChannel().tryLock();
} catch (OverlappingFileLockException ex) {
// A thread inside of this JVM has the lock on the file
lock = null;
Thread.sleep(1000);
}
} while (lock == null && retry);
return lock;
}
private String populateCache(URI address, Stat stat, boolean retry)
throws IOException, InterruptedException, KeeperException {
FileLock lock = tryLock(true);
String val = null;
try {
File cache = new File(cacheDir, address.getAuthority());
long fileModTime = cache.lastModified();
Long lastFileReadTime = lastCacheMod.get(address.getAuthority());
if (lastFileReadTime == null || lastFileReadTime >= fileModTime) {
// Cache has not been updated and we need to populate it
val = super.getPrimaryAvatarAddress(address, stat, retry);
FileWriter writer = new FileWriter(cache);
writer.write(val);
writer.close();
lastCacheMod.put(address.getAuthority(), cache.lastModified());
}
} finally {
if (lock != null) {
lock.release();
}
}
if (val == null) {
val = getFromCache(address, stat, retry);
}
return val;
}
public String getPrimaryAvatarAddress(URI address, Stat stat, boolean retry,
boolean firstAttempt) throws IOException, KeeperException,
InterruptedException {
String result = null;
if (!useCache) {
return super.getPrimaryAvatarAddress(address, stat, retry);
}
if (firstAttempt) {
result = getFromCache(address, stat, retry);
}
if (result == null) {
result = populateCache(address, stat, retry);
}
return result;
}
public boolean isCacheEnabled() {
return useCache;
}
}