package ch.usi.da.paxos.storage;
/*
* Copyright (c) 2013 Università della Svizzera italiana (USI)
*
* This file is part of URingPaxos.
*
* URingPaxos is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* URingPaxos is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with URingPaxos. If not, see <http://www.gnu.org/licenses/>.
*/
import java.io.File;
import java.io.IOException;
import org.apache.log4j.Logger;
import ch.usi.da.paxos.api.StableStorage;
import com.sleepycat.bind.EntryBinding;
import com.sleepycat.bind.serial.SerialBinding;
import com.sleepycat.bind.serial.StoredClassCatalog;
import com.sleepycat.bind.tuple.TupleBinding;
import com.sleepycat.je.CacheMode;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.Database;
import com.sleepycat.je.DatabaseConfig;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.DatabaseException;
import com.sleepycat.je.Durability;
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;
/**
* Name: BerkeleyStorage<br>
* Description: <br>
*
* Creation date: Feb 7, 2013<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class BerkeleyStorage implements StableStorage {
private final static Logger logger = Logger.getLogger(BerkeleyStorage.class);
private final Environment env;
private final Database valuedb;
private final Database ballotdb;
private final Database classCatalogDb;
private final StoredClassCatalog classCatalog;
private final DatabaseEntry key = new DatabaseEntry();
private final EntryBinding<Long> keyBinding;
private final DatabaseEntry data = new DatabaseEntry();
private final EntryBinding<Decision> dataBinding;
private final DatabaseEntry ballot_data = new DatabaseEntry();
private final EntryBinding<Integer> ballotBinding;
public BerkeleyStorage(){
this(null,false,true);
}
public BerkeleyStorage(File file, boolean readonly, boolean async){
if(file == null){
int pid = 0;
try {
pid = Integer.parseInt((new File("/proc/self")).getCanonicalFile().getName());
} catch (NumberFormatException | IOException e) {
}
String path = "/tmp";
String db_path = System.getenv("DB");
if(db_path != null){
path = db_path;
}
file = new File(path + "/ringpaxos-db/" + pid);
file.mkdirs();
}
EnvironmentConfig envConfig = new EnvironmentConfig();
DatabaseConfig dbConfig = new DatabaseConfig();
envConfig.setReadOnly(readonly);
dbConfig.setReadOnly(readonly);
envConfig.setAllowCreate(!readonly);
dbConfig.setAllowCreate(!readonly);
// performance settings
envConfig.setTransactional(true);
envConfig.setCacheMode(CacheMode.DEFAULT);
//envConfig.setCacheSize(1000000*800); // 800M
if(async){
dbConfig.setTransactional(false);
envConfig.setDurability(Durability.COMMIT_NO_SYNC);
dbConfig.setDeferredWrite(true);
}else{
dbConfig.setTransactional(true);
envConfig.setDurability(Durability.COMMIT_SYNC);
dbConfig.setDeferredWrite(false);
}
env = new Environment(file, envConfig);
valuedb = env.openDatabase(null,"valueDB",dbConfig);
ballotdb = env.openDatabase(null,"ballotDB",dbConfig);
classCatalogDb = env.openDatabase(null,"ClassCatalogDB", dbConfig);
classCatalog = new StoredClassCatalog(classCatalogDb);
keyBinding = TupleBinding.getPrimitiveBinding(Long.class);
dataBinding = new SerialBinding<Decision>(classCatalog,Decision.class);
ballotBinding = TupleBinding.getPrimitiveBinding(Integer.class);
logger.info("BerkeleyStorage cache size: " + env.getMutableConfig().getCacheSize());
logger.info("BerkeleyStorage durability: " + env.getMutableConfig().getDurability().getLocalSync());
logger.info("BerkeleyStorage deferred write: " + valuedb.getConfig().getDeferredWrite());
}
@Override
public synchronized void putBallot(Long instance, int ballot) {
keyBinding.objectToEntry(instance,key);
ballotBinding.objectToEntry(ballot,ballot_data);
OperationStatus status = ballotdb.put(null,key,ballot_data);
if(logger.isDebugEnabled()){
logger.debug("DB put ballot " + ballot + " for instance " + instance + " " + status.name());
}
}
@Override
public synchronized int getBallot(Long instance) {
keyBinding.objectToEntry(instance,key);
Integer ballot = null;
OperationStatus status = ballotdb.get(null,key,ballot_data,LockMode.DEFAULT);
if (status == OperationStatus.SUCCESS) {
ballot = ballotBinding.entryToObject(ballot_data);
}
if(logger.isDebugEnabled()){
logger.debug("DB get ballot " + ballot + " for instance " + instance + " " + status.name());
}
return ballot.intValue();
}
@Override
public synchronized boolean containsBallot(Long instance) {
boolean found = false;
keyBinding.objectToEntry(instance,key);
OperationStatus status = ballotdb.get(null,key,ballot_data,LockMode.DEFAULT);
if(status == OperationStatus.SUCCESS){
found = true;
}
if(logger.isDebugEnabled()){
logger.debug("DB contains ballot " + instance + " " + found + " (" + status.name() + ")");
}
return found;
}
@Override
public synchronized void putDecision(Long instance, Decision decision) {
keyBinding.objectToEntry(instance,key);
dataBinding.objectToEntry(decision,data);
OperationStatus status = valuedb.put(null,key,data);
if(logger.isDebugEnabled()){
logger.debug("DB put decision " + decision + " " + status.name());
}
}
@Override
public synchronized Decision getDecision(Long instance) {
keyBinding.objectToEntry(instance,key);
Decision decision = null;
OperationStatus status = valuedb.get(null,key,data,LockMode.DEFAULT);
if (status == OperationStatus.SUCCESS) {
decision = dataBinding.entryToObject(data);
}
if(logger.isDebugEnabled()){
logger.debug("DB get decision " + decision + " " + status.name());
}
return decision;
}
@Override
public synchronized boolean containsDecision(Long instance) {
boolean found = false;
keyBinding.objectToEntry(instance,key);
OperationStatus status = valuedb.get(null,key,data,LockMode.DEFAULT);
if(status == OperationStatus.SUCCESS){
found = true;
}
if(logger.isDebugEnabled()){
logger.debug("DB contains decision " + instance + " " + found + " (" + status.name() + ")");
}
return found;
}
@Override
public synchronized boolean trim(Long instance) {
if(instance == 0) { return true; } // fast track
putDecision(-1L,new Decision(0,instance,0,null)); // if the transaction aborts
Transaction t = null;
if(valuedb.getConfig().getTransactional()){
t = env.beginTransaction(null,null);
}
Cursor cursor = valuedb.openCursor(t, null);
boolean dirty = false;
try{
while (cursor.getNext(key,data,LockMode.READ_UNCOMMITTED) == OperationStatus.SUCCESS) {
Long i = keyBinding.entryToObject(key);
if(i > 0 && i < instance && cursor.delete() != OperationStatus.SUCCESS){
logger.error("Error deleting instance " + i + " from DB!");
dirty = true;
}
}
}finally{
cursor.close();
if(!dirty){
if(t != null){ t.commit(); }
}else{
if(t != null){ t.abort(); }
return false;
}
}
logger.debug("DB deltete up to instance " + instance);
return true;
}
@Override
public synchronized Long getLastTrimInstance() {
Decision d = getDecision(-1L);
if(d != null){
return getDecision(-1L).getInstance();
}else{
return 0L;
}
}
@Override
public synchronized void close() {
try {
valuedb.close();
ballotdb.close();
classCatalogDb.close();
env.close();
} catch(DatabaseException dbe) {
logger.error("Error closing db environment!",dbe);
}
}
/**
* Debug method
*/
public synchronized void listAllValues() {
Cursor cursor = valuedb.openCursor(null, null);
while (cursor.getNext(key,data,LockMode.DEFAULT) == OperationStatus.SUCCESS) {
Long instance = keyBinding.entryToObject(key);
Decision decision = dataBinding.entryToObject(data);
System.out.println("instance " + instance + " -> " + decision + "");
}
cursor.close();
}
/**
* Debug method
*/
public synchronized void listAllBallots() {
Cursor cursor = ballotdb.openCursor(null, null);
while (cursor.getNext(key,data,LockMode.DEFAULT) == OperationStatus.SUCCESS) {
Long instance = keyBinding.entryToObject(key);
Integer ballot = ballotBinding.entryToObject(data);
System.out.println("instance " + instance + " -> " + ballot + "");
}
cursor.close();
}
}