package lsr.paxos; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.Serializable; import java.nio.ByteBuffer; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Vector; import lsr.common.Reply; /** * Structure - snapshot wrapped with all necessary additional data * * The snapshot instanceId is the instanceId of the next instance to be * executed, so that one might also record a snapshot before any instance has * been decided (even if this has no real use, and enabling it is easy) * * @author JK */ public class Snapshot implements Serializable { private static final long serialVersionUID = -7961820683501513465L; // Replica part /** Id of next instance to be executed */ private int nextIntanceId; /** The real snapshot - data from the Service */ private byte[] value; /** RequestId of last executed request for each client */ private Map<Long, Reply> lastReplyForClient; // ServiceProxy part /** Next request ID to be executed */ private int nextRequestSeqNo; /** First requestSeqNo for the instance nextIntanceId */ private int startingRequestSeqNo; /** Cache for the last instance, the partially executed one */ private List<Reply> partialResponseCache; /** * Creates empty snapshot. */ public Snapshot() { } /** * Reads previously recorded snapshot from input stream. * * @param input - the input stream with serialized snapshot * @throws IOException if I/O error occurs */ public Snapshot(DataInputStream input) throws IOException { // instance id nextIntanceId = input.readInt(); // value int size = input.readInt(); value = new byte[size]; input.readFully(value); // executed requests size = input.readInt(); lastReplyForClient = new HashMap<Long, Reply>(size); for (int i = 0; i < size; i++) { long key = input.readLong(); int replySize = input.readInt(); byte[] reply = new byte[replySize]; input.readFully(reply); lastReplyForClient.put(key, new Reply(reply)); } // request sequential number nextRequestSeqNo = input.readInt(); // first request sequential number in next instance startingRequestSeqNo = input.readInt(); // cached replies for the next instance size = input.readInt(); partialResponseCache = new Vector<Reply>(size); for (int i = 0; i < size; i++) { int replySize = input.readInt(); byte[] reply = new byte[replySize]; input.readFully(reply); partialResponseCache.add(new Reply(reply)); } } /** * @return id of next instance to be executed */ public int getNextInstanceId() { return nextIntanceId; } public void setNextInstanceId(int nextInstanceId) { this.nextIntanceId = nextInstanceId; } public byte[] getValue() { return value; } public void setValue(byte[] value) { this.value = value; } public Map<Long, Reply> getLastReplyForClient() { return lastReplyForClient; } public void setLastReplyForClient(Map<Long, Reply> lastReplyForClient) { this.lastReplyForClient = lastReplyForClient; } public int getNextRequestSeqNo() { return nextRequestSeqNo; } public void setNextRequestSeqNo(int nextRequestSeqNo) { this.nextRequestSeqNo = nextRequestSeqNo; } public int getStartingRequestSeqNo() { return startingRequestSeqNo; } public void setStartingRequestSeqNo(int startingRequestSeqNo) { this.startingRequestSeqNo = startingRequestSeqNo; } public List<Reply> getPartialResponseCache() { return partialResponseCache; } public void setPartialResponseCache(List<Reply> partialResponseCache) { this.partialResponseCache = partialResponseCache; } /** * Returns size of this snapshot in bytes */ public int byteSize() { int size = 4; // next instance ID size += 4 + value.length; // value size += 4; // last replies for (Reply reply : lastReplyForClient.values()) { size += 8 + 4 + reply.byteSize(); } size += 4; // nextSeqNo size += 4; // startingSeqNo size += 4; // cached replies for (Reply reply : partialResponseCache) { size += 4 + reply.byteSize(); } return size; } /** * Writes the snapshot at the end of given {@link ByteBuffer} */ public void writeTo(ByteBuffer bb) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); try { writeTo(new DataOutputStream(baos)); baos.close(); } catch (IOException e) { throw new RuntimeException(e); } bb.put(baos.toByteArray()); } /** * Writes the snapshot to the given {@link DataOutputStream} */ public void writeTo(DataOutputStream snapshotStream) throws IOException { // instance id snapshotStream.writeInt(nextIntanceId); // value snapshotStream.writeInt(value.length); snapshotStream.write(value); // executed requests snapshotStream.writeInt(lastReplyForClient.size()); for (Entry<Long, Reply> entry : lastReplyForClient.entrySet()) { snapshotStream.writeLong(entry.getKey()); snapshotStream.writeInt(entry.getValue().byteSize()); snapshotStream.write(entry.getValue().toByteArray()); } // request sequential number snapshotStream.writeInt(nextRequestSeqNo); // first request sequential number in next instance snapshotStream.writeInt(startingRequestSeqNo); // cached replies for the next instance snapshotStream.writeInt(partialResponseCache.size()); for (Reply reply : partialResponseCache) { snapshotStream.writeInt(reply.byteSize()); snapshotStream.write(reply.toByteArray()); } } /** * Compares two snapshot states. * * <ul> * <li><b><0</b> object is older than argument * <li><b>0</b> object and argument have similar state, can't say for sure * <li><b>>0</b> object is newer than argument * </ul> */ public int compareTo(Snapshot other) { int compareTo = Integer.valueOf(nextIntanceId).compareTo(other.nextIntanceId); if (compareTo == 0) { compareTo = Integer.valueOf(nextRequestSeqNo).compareTo(other.nextRequestSeqNo); } return compareTo; } public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } Snapshot other = (Snapshot) obj; return nextIntanceId == other.nextIntanceId && nextRequestSeqNo == other.nextRequestSeqNo; } public int hashCode() { int hash = 7; hash = 31 * hash + nextIntanceId; hash = 31 * hash + nextRequestSeqNo; return hash; } public String toString() { return "Snapshot inst:" + nextIntanceId; } }