package lsr.paxos.test; import static lsr.common.ProcessDescriptor.processDescriptor; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.math.BigInteger; import java.security.MessageDigest; import java.util.Arrays; import java.util.Random; import lsr.common.Configuration; import lsr.paxos.replica.Replica; import lsr.service.AbstractService; /** * This service calculates and logs hashes (sha512) of the requests it receives * and responds with the hash. Thus can be used for testing the correctness. */ public class DigestService extends AbstractService { /** Written to 'out' stream as the replica becomes up */ public static final String RECOVERY_FINISHED = "rec_finished"; protected MessageDigest sha512; protected byte[] previousDigest = new byte[512]; { Arrays.fill(previousDigest, 0, 512, (byte) 0); } protected int lastExecuteSeqNo; protected Random random = new Random(); protected int snapshotSeqNo; protected byte[] snapshot; protected DataOutputStream decisionsFile; protected final int localId; public DigestService(int localId) throws Exception { this.localId = localId; sha512 = MessageDigest.getInstance("SHA-512"); } private void initLogFile(String logPath) throws Exception { File logDirectory = new File(logPath, Integer.toString(localId)); logDirectory.mkdirs(); File logFile; int i = 0; do { logFile = new File(logDirectory.getAbsolutePath(), "decisions.log." + i++); } while (logFile.exists()); decisionsFile = new DataOutputStream(new FileOutputStream(logFile)); } public byte[] execute(byte[] value, int executeSeqNo) { lastExecuteSeqNo = executeSeqNo; sha512.update(previousDigest); byte[] digest = sha512.digest(value); StringBuffer sb = new StringBuffer(); sb.append(executeSeqNo); sb.append(' '); sb.append(new BigInteger(1, digest).toString(16)); sb.append('\n'); try { decisionsFile.writeBytes(sb.toString()); decisionsFile.flush(); } catch (Exception e) { throw new RuntimeException(e); } if (random.nextInt(100) < 10) { // 10% chance snapshotSeqNo = executeSeqNo; snapshot = digest; if (random.nextInt(100) < 10) { // 1% chance fireSnapshotMade(snapshotSeqNo + 1, snapshot, digest); } } previousDigest = digest; return digest; } public void askForSnapshot(int lastSnapshotNextRequestSeqNo) { if (random.nextInt(100) < 80) { // 80% chance ensureSnapshot(); fireSnapshotMade(snapshotSeqNo + 1, snapshot, null); } } public void forceSnapshot(int lastSnapshotNextRequestSeqNo) { ensureSnapshot(); fireSnapshotMade(snapshotSeqNo + 1, snapshot, null); } protected void ensureSnapshot() { if (snapshot == null) { snapshot = previousDigest; snapshotSeqNo = lastExecuteSeqNo; } } public void updateToSnapshot(int nextRequestSeqNo, byte[] snapshot) { previousDigest = snapshot; this.snapshot = snapshot; snapshotSeqNo = nextRequestSeqNo - 1; } public void recoveryFinished() { System.out.println(RECOVERY_FINISHED); } public static void main(String[] args) throws Exception { int localId = Integer.parseInt(args[0]); Configuration config = new Configuration(); DigestService service = new DigestService(localId); Replica replica = new Replica(config, localId, service); service.initLogFile(processDescriptor.logPath); replica.start(); System.in.read(); System.exit(-1); } }