package ch.usi.da.paxos.storage;
/*
* Copyright (c) 2015 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 java.util.ArrayList;
import java.util.List;
import org.apache.log4j.Logger;
import org.rocksdb.ColumnFamilyDescriptor;
import org.rocksdb.ColumnFamilyHandle;
import org.rocksdb.DBOptions;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;
import org.rocksdb.WriteOptions;
import ch.usi.da.paxos.Util;
import ch.usi.da.paxos.api.PaxosRole;
import ch.usi.da.paxos.api.StableStorage;
import ch.usi.da.paxos.message.Message;
import ch.usi.da.paxos.message.MessageType;
import com.sleepycat.je.DatabaseException;
/**
* Name: RocksDbStorage<br>
* Description: <br>
*
* Creation date: Aug 4, 2015<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class RocksDbStorage implements StableStorage {
private final static Logger logger = Logger.getLogger(RocksDbStorage.class);
private RocksDB db = null;
private final DBOptions dboptions = new DBOptions();
private final WriteOptions woptions = new WriteOptions();
private ColumnFamilyHandle ballotdb;
static {
RocksDB.loadLibrary();
}
public RocksDbStorage(){
this(null,true);
}
public RocksDbStorage(File file, 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();
}
try {
dboptions.setCreateIfMissing(true);
dboptions.setCreateMissingColumnFamilies(true);
List<ColumnFamilyDescriptor> cfdesc = new ArrayList<ColumnFamilyDescriptor>();
cfdesc.add(new ColumnFamilyDescriptor(RocksDB.DEFAULT_COLUMN_FAMILY));
cfdesc.add(new ColumnFamilyDescriptor("ballot".getBytes()));
List<ColumnFamilyHandle> cfhandle = new ArrayList<ColumnFamilyHandle>();
db = RocksDB.open(dboptions,file.toString(),cfdesc,cfhandle);
for(ColumnFamilyHandle handle : cfhandle){ // better way to identify ColumnFamilyHandle ?
if(handle != db.getDefaultColumnFamily()){
ballotdb = handle;
}
}
} catch (RocksDBException e) {
logger.error("RocksDbStorage DB create failed!", e);
}
if(async){
woptions.setSync(false);
}else{
woptions.setSync(true);
}
logger.info("RocksDbStorage sync: " + woptions.sync());
}
@Override
public synchronized void putBallot(Long instance, int ballot) {
try {
db.put(ballotdb,woptions,Util.longToByte(instance),Util.intToByte(ballot));
} catch (RocksDBException e) {
logger.error("RocksDbStorage ballot put failed!", e);
}
}
@Override
public synchronized int getBallot(Long instance) {
try {
byte[] ret = db.get(ballotdb,Util.longToByte(instance));
if(ret != null){
return Util.byteToInt(ret);
}
} catch (RocksDBException e) {
logger.error("RocksDbStorage ballot get failed!", e);
}
return -1;
}
@Override
public synchronized boolean containsBallot(Long instance) {
boolean found = false;
if(getBallot(instance) > 0){
found = true;
}
return found;
}
@Override
public synchronized void putDecision(Long instance, Decision decision) {
Message m = new Message(decision.getInstance(), decision.getRing(), PaxosRole.Proposer, MessageType.Value, decision.getBallot(), decision.getBallot(), decision.getValue());
try {
db.put(woptions,Util.longToByte(instance),Message.toWire(m));
} catch (RocksDBException e) {
logger.error("RocksDbStorage decision put failed!", e);
}
}
@Override
public synchronized Decision getDecision(Long instance) {
Decision decision = null;
try {
byte[] ret = db.get(Util.longToByte(instance));
if(ret != null){
Message m = Message.fromWire(ret);
Decision d = new Decision(m.getSender(),m.getInstance(),m.getBallot(),m.getValue());
return d;
}
} catch (RocksDBException e) {
logger.error("RocksDbStorage decision get failed!", e);
}
return decision;
}
@Override
public synchronized boolean containsDecision(Long instance) {
boolean found = false;
if(getDecision(instance) != null){
found = true;
}
return found;
}
@Override
public synchronized boolean trim(Long instance) {
if(instance == 0) { return true; } // fast track
long init = getLastTrimInstance();
putDecision(-1L,new Decision(0,instance,0,null));
for(long i = init; i < instance; i++){
try {
db.remove(Util.longToByte(i));
} catch (RocksDBException e) {
logger.error("RocksDbStorage decision remove failed!", e);
}
}
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 {
if(db != null){
db.close();
}
dboptions.dispose();
} catch(DatabaseException dbe) {
logger.error("Error closing db environment!",dbe);
}
}
}