/**
* Log for Two-Phase Commit
*
* @author Mosharaf Chowdhury (http://www.mosharaf.com)
*
* Copyright (c) 2012, University of California at Berkeley
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of University of California, Berkeley nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL PRASHANTH MOHAN BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package edu.berkeley.cs162;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Scanner;
public class TPCLog {
// Path to log file
public String logPath = null;
// Reference to the KVServer of this slave. Populated by rebuildKeyServer()
public KVServer kvServer = null;
int numSets;
int maxElemsPerSet;
// Log entries
public ArrayList<KVMessage> entries = null;
/*
* Keeps track of the interrupted 2PC operation if there is one.
* There can be at most one, ie., when the last 2PC operation before
* crashing was in READY state. This should be set during a call to
* rebuildKeyServer() during recovery.
*/
public KVMessage interruptedTpcOperation = null;
/**
*
* @param logPath
* @param kvServer Reference to the KVServer of this slave. Populated by
* rebuildKeyServer() during start.
*/
public TPCLog(String logPath, KVServer kvServer) {
this.logPath = logPath;
entries = new ArrayList<KVMessage>();
this.kvServer = kvServer;
this.numSets = kvServer.getNumSets();
this.maxElemsPerSet = kvServer.getMaxElemsPerSet();
}
public ArrayList<KVMessage> getEntries() {
return entries;
}
public boolean empty() {
return entries.size() == 0;
}
/**
* Add an entry to the log and flush the entire log to disk. You do not have
* to efficiently append entries onto the log stored on disk.
*
* @param entry KVMessage to write to the log
*/
public void appendAndFlush(KVMessage entry) {
entries.add(entry);
flushToDisk();
}
/**
* Load log from persistent storage.
*/
@SuppressWarnings("unchecked")
public void loadFromDisk() {
ObjectInputStream inputStream = null;
try {
inputStream = new ObjectInputStream(new FileInputStream(logPath));
entries = (ArrayList<KVMessage>) inputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
} finally {
// If log never existed, there are no entries
if (entries == null) {
entries = new ArrayList<KVMessage>();
}
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Writes the log to persistent storage.
*/
public void flushToDisk() {
ObjectOutputStream outputStream = null;
try {
outputStream = new ObjectOutputStream(new FileOutputStream(logPath));
outputStream.writeObject(entries);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (outputStream != null) {
outputStream.flush();
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public KVServer getKvServer() {
return kvServer;
}
public void setKvServer(KVServer kvServer) {
this.kvServer = kvServer;
}
/**
* Load log and rebuild KVServer by iterating over log entries. You do not
* need to restore the previous cache state (ie. ignore GETS).
* Set interruptedTpcOperation, if there is one (ie., slave crashed
* in the READY state).
* @throws KVException if an error occurs in KVServer (we do not expect any)
*/
public void rebuildKeyServer() throws KVException {
kvServer = new KVServer(numSets, maxElemsPerSet);
for (KVMessage message : entries) {
if (message.getMsgType().equals("putreq")) {
interruptedTpcOperation = message;
kvServer.put(message.getKey(), message.getValue());
}
if (message.getMsgType().equals("delreq")) {
interruptedTpcOperation = message;
kvServer.del(message.getKey());
}
if (message.getMsgType().equals("abort") || message.getMsgType().equals("commit")) {
interruptedTpcOperation = null;
}
}
}
/**
*
* @return Interrupted 2PC operation, if any
*/
public KVMessage getInterruptedTpcOperation() {
KVMessage logEntry = interruptedTpcOperation;
interruptedTpcOperation = null;
return logEntry;
}
/**
*
* @return True if TPCLog contains an interrupted 2PC operation
*/
public boolean hasInterruptedTpcOperation() {
return interruptedTpcOperation != null;
}
}