package lsr.paxos.storage; import static lsr.common.ProcessDescriptor.processDescriptor; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.EOFException; import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.SyncFailedException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.regex.Matcher; import java.util.regex.Pattern; import lsr.common.ClientRequest; import lsr.paxos.replica.ClientBatchID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * */ public class SynchronousClientBatchStore extends ClientBatchStore { private static final byte BATCH_VALUE = 0x01; private static final byte ASSOCIATE = 0x02; private static final byte REMOVE = 0x03; private final FileDescriptor fd; private final DataOutputStream file; protected SynchronousClientBatchStore() { super(); try { String logPath = processDescriptor.logPath + '/' + processDescriptor.localId; File logDir = new File(logPath); logDir.mkdirs(); Pattern pattern = Pattern.compile("batches\\.(\\d+)\\.log"); ArrayList<Integer> numbers = new ArrayList<Integer>(); for (String fileName : logDir.list()) { Matcher matcher = pattern.matcher(fileName); if (matcher.find()) { int x = Integer.parseInt(matcher.group(1)); numbers.add(x); } } Collections.sort(numbers); for (Integer number : numbers) { DataInputStream dis = new DataInputStream(new FileInputStream(new File(logDir, "batches." + number + ".log"))); load(dis); } logger.info("Loaded batch store files"); int newFileId; if (numbers.isEmpty()) { newFileId = 0; } else { newFileId = numbers.get(numbers.size() - 1) + 1; } FileOutputStream fis = new FileOutputStream(new File(logDir, "batches." + newFileId + ".log")); fd = fis.getFD(); file = new DataOutputStream(fis); } catch (Exception e) { throw new RuntimeException("Could not read batch values or start new file!", e); } } private void load(DataInputStream dis) throws Exception { try { while (true) { byte type = dis.readByte(); switch (type) { case BATCH_VALUE: { ClientBatchID cbid = new ClientBatchID(dis); int count = dis.readInt(); ClientRequest[] value = new ClientRequest[count]; for (int i = 0; i < count; ++i) value[i] = ClientRequest.create(dis); super.setBatch(cbid, value); break; } case REMOVE: { int count = dis.readInt(); ArrayList<ClientBatchID> cbids = new ArrayList<ClientBatchID>(count); for (int i = 0; i < count; ++i) cbids.add(new ClientBatchID(dis)); super.removeBatches(cbids); break; } case ASSOCIATE: { ClientBatchID cbid = new ClientBatchID(dis); super.associateWithInstance(cbid); break; } default: throw new Exception("Wrong record type"); } } } catch (EOFException e) { } } /** * Forces synchronizing file with batch values, must be called before any * consensus value sync */ public synchronized void sync() throws SyncFailedException { fd.sync(); } @Override public synchronized void setBatch(ClientBatchID batchId, ClientRequest[] value) { super.setBatch(batchId, value); try { file.writeByte(BATCH_VALUE); batchId.writeTo(file); file.writeInt(value.length); for (int i = 0; i < value.length; i++) value[i].writeTo(file); } catch (IOException e) { fail(e); } } private void fail(IOException e) { throw new RuntimeException("Could not write batch value related record", e); } @Override public synchronized void associateWithInstance(ClientBatchID batchId) { super.associateWithInstance(batchId); try { file.writeByte(ASSOCIATE); batchId.writeTo(file); } catch (IOException e) { fail(e); } } @Override public synchronized void removeBatches(Collection<ClientBatchID> cbids) { super.removeBatches(cbids); try { file.writeByte(REMOVE); file.writeInt(cbids.size()); for (ClientBatchID cbid : cbids) cbid.writeTo(file); } catch (IOException e) { fail(e); } } private final static Logger logger = LoggerFactory.getLogger(SynchronousClientBatchStore.class); }