package edu.berkeley.thebes.common.persistence.disk;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Environment;
import com.sleepycat.je.EnvironmentConfig;
import com.sleepycat.je.LockMode;
import com.sleepycat.je.OperationStatus;
import com.sleepycat.je.Transaction;
import com.yammer.metrics.core.TimerContext;
import edu.berkeley.thebes.common.config.Config;
import edu.berkeley.thebes.common.data.DataItem;
import edu.berkeley.thebes.common.persistence.IPersistenceEngine;
import edu.berkeley.thebes.common.persistence.util.LockManager;
import edu.berkeley.thebes.common.thrift.ThriftDataItem;
import org.apache.commons.io.FileUtils;
import org.apache.thrift.TDeserializer;
import org.apache.thrift.TException;
import org.apache.thrift.TSerializer;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
public class BDBPersistenceEngine implements IPersistenceEngine {
private static org.slf4j.Logger logger = LoggerFactory.getLogger(BDBPersistenceEngine.class);
Database db;
Environment env;
ThreadLocal<TSerializer> serializer = new ThreadLocal<TSerializer>() {
@Override
protected TSerializer initialValue() {
return new TSerializer();
}
};
ThreadLocal<TDeserializer> deserializer = new ThreadLocal<TDeserializer>() {
@Override
protected TDeserializer initialValue() {
return new TDeserializer();
}
};
public void open() throws IOException {
if(Config.doCleanDatabaseFile()) {
try {
FileUtils.forceDelete(new File(Config.getDiskDatabaseFile()));
} catch(Exception e) {
if (!(e instanceof FileNotFoundException))
logger.warn("error: ", e) ;
}
}
new File(Config.getDiskDatabaseFile()).mkdirs();
EnvironmentConfig envConfig = new EnvironmentConfig();
envConfig.setAllowCreate(true);
envConfig.setTransactional(true);
envConfig.setLockTimeout(5, TimeUnit.SECONDS);
env = new Environment(new File(Config.getDiskDatabaseFile()), envConfig);
DatabaseConfig dbConfig = new DatabaseConfig();
dbConfig.setAllowCreate(true);
dbConfig.setTransactional(true);
db = env.openDatabase(null, "thebesDB", dbConfig);
}
@Override
public void force_put(String key, DataItem value) throws TException {
throw new UnsupportedOperationException("Figure it out if you want it.");
}
@Override
public void put_if_newer(String key, DataItem value) throws TException {
if(value == null) {
logger.warn("NULL write to key "+key);
return;
}
Transaction putTxn = null;
try {
putTxn = env.beginTransaction(null, null);
DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes());
DatabaseEntry existingEntry = new DatabaseEntry();
db.get(putTxn, keyEntry, existingEntry, LockMode.RMW);
if(existingEntry.getData() != null) {
ThriftDataItem existingThriftItem = new ThriftDataItem();
deserializer.get().deserialize(existingThriftItem, existingEntry.getData());
DataItem existingDataItem = new DataItem(existingThriftItem);
if (existingDataItem.getVersion().compareTo(value.getVersion()) > 0) {
return;
}
}
DatabaseEntry newDataEntry = new DatabaseEntry(serializer.get().serialize(value.toThrift()));
db.put(putTxn, keyEntry, newDataEntry);
} catch(Exception e) {
logger.warn("error: ", e);
if (putTxn != null) {
putTxn.abort();
putTxn = null;
}
return;
} finally {
if(putTxn != null)
putTxn.commit();
}
return;
}
public DataItem get(String key) throws TException {
DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes());
DatabaseEntry dataEntry = new DatabaseEntry();
OperationStatus status = db.get(null, keyEntry, dataEntry, LockMode.DEFAULT);
if(status != OperationStatus.SUCCESS || dataEntry.getData() == null)
return null;
ThriftDataItem tdrRet = new ThriftDataItem();
deserializer.get().deserialize(tdrRet, dataEntry.getData());
return new DataItem(tdrRet);
}
public void delete(String key) throws TException {
DatabaseEntry keyEntry = new DatabaseEntry(key.getBytes());
DatabaseEntry dataEntry = new DatabaseEntry();
OperationStatus status = db.delete(null, keyEntry);
return;
}
public void close() throws IOException {
db.close();
env.close();
}
}