package ch.usi.da.smr.recovery;
/*
* Copyright (c) 2014 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.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.apache.log4j.Logger;
import ch.usi.da.smr.PartitionManager;
import ch.usi.da.smr.Replica;
/**
* Name: DfsRecovery<br>
* Description: <br>
*
* Assumes a local mounted distributed file system to access
* local and remote snapshots.
*
* Mountpoint: /prefix
*
* Creation date: Nov 6, 2014<br>
* $Id$
*
* @author Samuel Benz benz@geoid.ch
*/
public class DfsRecovery implements RecoveryInterface {
private final static Logger logger = Logger.getLogger(DfsRecovery.class);
public final int nodeID;
public final String token;
private final PartitionManager partitions;
private final int max_ring = 20;
private final String path;
private final String prefix;
public static final String snapshot_file = "snapshot.ser";
public static final String state_file = "snapshot.state"; // only used to quickly decide if remote snapshot is newer
public DfsRecovery(int nodeID, String token, String prefix, PartitionManager partitions){
this.nodeID = nodeID;
this.token = token;
this.prefix = prefix;
this.partitions = partitions;
this.path = prefix + "/" + token + "/" + nodeID;
File dir = new File(path);
dir.mkdirs();
}
public boolean storeState(Map<Integer,Long> instances, Map<String,byte[]> db){
try {
synchronized(db){
logger.info("Replica start storing state ... ");
for(Entry<Integer,Long> e : instances.entrySet()){
db.put("r:" + e.getKey(),e.getValue().toString().getBytes());
}
FileOutputStream fs = new FileOutputStream(path + "/" + snapshot_file);
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(db);
os.flush();
fs.getChannel().force(false); // fsync
os.close();
fs = new FileOutputStream(path + "/" + state_file);
for(Entry<Integer, Long> e : instances.entrySet()){
fs.write((e.getKey() + "=" + e.getValue() + "\n").getBytes());
}
fs.getChannel().force(false);
os.close();
logger.info("... state stored up to instance: " + instances);
}
return true;
} catch (IOException e) {
logger.error(e);
}
return false;
}
public Map<Integer,Long> installState(String token,Map<String,byte[]> db) throws Exception{
Map<Integer,Long> instances = new HashMap<Integer,Long>();
String node = null;
InputStreamReader isr;
String line;
Map<Integer, Long> state = new HashMap<Integer, Long>();
// local
try{
isr = new InputStreamReader(new FileInputStream(path + "/" + state_file));
BufferedReader bin = new BufferedReader(isr);
while ((line = bin.readLine()) != null){
String[] s = line.split("=");
state.put(Integer.parseInt(s[0]),Long.parseLong(s[1]));
}
logger.info("Replica found local snapshot: " + state);
bin.close();
}catch (FileNotFoundException e){
logger.info("No local snapshot present.");
}
// remote TODO: must ask min. GSQ replicas
for(String n : partitions.getReplicaIDs(token)){
try{
isr = new InputStreamReader(new FileInputStream(prefix + "/" + token + "/" + n + "/" + state_file));
BufferedReader in = new BufferedReader(isr);
Map<Integer, Long> nstate = new HashMap<Integer, Long>();
while ((line = in.readLine()) != null){
String[] s = line.split("=");
nstate.put(Integer.parseInt(s[0]),Long.parseLong(s[1]));
}
if(state.isEmpty()){
state = nstate;
node = n;
}else if(Replica.newerState(nstate,state)){
state = nstate;
node = n;
}
logger.info("Replica found remote snapshot: " + nstate + " (" + n + ")");
in.close();
}catch(Exception e){
logger.error("Error getting state from " + n,e);
}
}
synchronized(db){
InputStream in;
if(node != null){
logger.info("Use remote snapshot from host " + node);
in = new FileInputStream(new File(prefix + "/" + token + "/" + node + "/" + snapshot_file));
}else{
in = new FileInputStream(new File(path + "/" + snapshot_file));
}
ObjectInputStream ois = new ObjectInputStream(in);
@SuppressWarnings("unchecked")
Map<String,byte[]> m = (Map<String,byte[]>) ois.readObject();
ois.close();
in.close();
db.clear();
db.putAll(m);
byte[] b = null;
for(int i = 1;i<max_ring+1;i++){
if((b = db.get("r:" + i)) != null){
instances.put(i,Long.valueOf(new String(b)));
}
}
}
logger.info("Replica installed snapshot instance: " + instances);
return instances;
}
public void close(){
}
}