public RaftStorage(File dir, RaftServerConstants.StartupOption option) throws IOException { storageDir = new RaftStorageDirectory(dir); if (option == RaftServerConstants.StartupOption.FORMAT) { if (storageDir.analyzeStorage(false) == StorageState.NON_EXISTENT) { throw new IOException("Cannot format " + storageDir); } storageDir.lock(); format(); state = storageDir.analyzeStorage(false); Preconditions.assertTrue(state == StorageState.NORMAL); } else { state = analyzeAndRecoverStorage(true); // metaFile is initialized here if (state != StorageState.NORMAL) { storageDir.unlock(); throw new IOException("Cannot load " + storageDir + ". Its state: " + state); } } }
private File getSegmentFile() { return isOpen ? storage.getStorageDir().getOpenLogFile(startIndex) : storage.getStorageDir().getClosedLogFile(startIndex, endIndex); }
private StorageState analyzeAndRecoverStorage(boolean toLock) throws IOException { StorageState storageState = storageDir.analyzeStorage(toLock); if (storageState == StorageState.NORMAL) { metaFile = new MetaFile(storageDir.getMetaFile()); Preconditions.assertTrue(metaFile.exists(), () -> "Meta file " + metaFile + " does not exists."); metaFile.readFile(); // Existence of raft-meta.tmp means the change of votedFor/term has not // been committed. Thus we should delete the tmp file. cleanMetaTmpFile(); return StorageState.NORMAL; } else if (storageState == StorageState.NOT_FORMATTED && storageDir.isCurrentEmpty()) { format(); return StorageState.NORMAL; } else { return storageState; } }
private void format() throws IOException { storageDir.clearDirectory(); metaFile = writeMetaFile(MetaFile.DEFAULT_TERM, MetaFile.EMPTY_VOTEFOR); LOG.info("Storage directory " + storageDir.getRoot() + " has been successfully formatted."); }
RaftStorageDirectory sd = new RaftStorageDirectory(storageDir); try { StorageState state = sd.analyzeStorage(true); Assert.assertEquals(StorageState.NOT_FORMATTED, state); Assert.assertTrue(sd.isCurrentEmpty()); } finally { sd.unlock(); storage.close(); Assert.assertEquals(StorageState.NORMAL, sd.analyzeStorage(false)); File m = sd.getMetaFile(); Assert.assertTrue(m.exists()); MetaFile metaFile = new MetaFile(m); metaFile = new MetaFile(sd.getMetaFile()); Assert.assertEquals(MetaFile.DEFAULT_TERM, metaFile.getTerm()); Assert.assertEquals(MetaFile.EMPTY_VOTEFOR, metaFile.getVotedFor());
storage.close(); RaftStorageDirectory sd = new RaftStorageDirectory(storageDir); File metaFile = sd.getMetaFile(); FileUtils.move(metaFile, sd.getMetaTmpFile()); Assert.assertEquals(StorageState.NOT_FORMATTED, sd.analyzeStorage(false)); Assert.assertTrue(sd.getMetaFile().exists()); Assert.assertTrue(sd.getMetaTmpFile().createNewFile()); Assert.assertTrue(sd.getMetaTmpFile().exists()); try { storage = new RaftStorage(storageDir, StartupOption.REGULAR); Assert.assertEquals(StorageState.NORMAL, storage.getState()); Assert.assertFalse(sd.getMetaTmpFile().exists()); Assert.assertTrue(sd.getMetaFile().exists()); } finally { storage.close();
@Override void execute() throws IOException { File openFile = storage.getStorageDir().getOpenLogFile(newStartIndex); LOG.debug("{} creating new log segment {}", name, openFile); Preconditions.assertTrue(!openFile.exists(), "open file %s exists for %s", openFile, name); Preconditions.assertTrue(out == null && pendingFlushNum == 0); out = new LogOutputStream(openFile, false, segmentMaxSize, preallocatedSize, bufferSize); Preconditions.assertTrue(openFile.exists(), "Failed to create file %s for %s", openFile.getAbsolutePath(), name); }
@Test public void testZeroSizeInProgressFile() throws Exception { final RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); final File file = storage.getStorageDir().getOpenLogFile(0); storage.close(); // create zero size in-progress file LOG.info("file: " + file); Assert.assertTrue(file.createNewFile()); final Path path = file.toPath(); Assert.assertTrue(Files.exists(path)); Assert.assertEquals(0, Files.size(path)); // getLogSegmentFiles should remove it. final List<RaftStorageDirectory.LogPathAndIndex> logs = storage.getStorageDir().getLogSegmentFiles(); Assert.assertEquals(0, logs.size()); Assert.assertFalse(Files.exists(path)); } }
boolean hasMetaFile() throws IOException { return getMetaFile().exists(); }
static List<Path> getOpenLogFiles(RaftServerImpl server) throws Exception { return server.getState().getStorage().getStorageDir().getLogSegmentFiles().stream() .filter(LogPathAndIndex::isOpen) .map(LogPathAndIndex::getPath) .collect(Collectors.toList()); }
public File getStateMachineDir() { return new File(getRoot(), STATE_MACHINE); }
final RaftStorageDirectory dir = storage.getStorageDir(); File tmpDir = dir.getNewTempDir(); FileUtils.createDirectories(tmpDir); tmpDir.deleteOnExit(); new File(dir.getRoot(), fileName).getName()); tmpDir, dir.getStateMachineDir()); dir.getStateMachineDir().delete(); tmpDir.renameTo(dir.getStateMachineDir());
@Override public void close() throws IOException { storageDir.unlock(); }
private void cleanMetaTmpFile() throws IOException { Files.deleteIfExists(storageDir.getMetaTmpFile().toPath()); }
RaftStorageDirectory sd = new RaftStorageDirectory(storageDir); try { StorageState state = sd.analyzeStorage(true); Assert.assertEquals(StorageState.NOT_FORMATTED, state); Assert.assertTrue(sd.isCurrentEmpty()); } finally { sd.unlock(); storage.close(); Assert.assertEquals(StorageState.NORMAL, sd.analyzeStorage(false)); File m = sd.getMetaFile(); Assert.assertTrue(m.exists()); MetaFile metaFile = new MetaFile(m); metaFile = new MetaFile(sd.getMetaFile()); Assert.assertEquals(MetaFile.DEFAULT_TERM, metaFile.getTerm()); Assert.assertEquals(MetaFile.EMPTY_VOTEFOR, metaFile.getVotedFor());
storage.close(); RaftStorageDirectory sd = new RaftStorageDirectory(storageDir); File metaFile = sd.getMetaFile(); FileUtils.move(metaFile, sd.getMetaTmpFile()); Assert.assertEquals(StorageState.NOT_FORMATTED, sd.analyzeStorage(false)); Assert.assertTrue(sd.getMetaFile().exists()); Assert.assertTrue(sd.getMetaTmpFile().createNewFile()); Assert.assertTrue(sd.getMetaTmpFile().exists()); try { storage = new RaftStorage(storageDir, StartupOption.REGULAR); Assert.assertEquals(StorageState.NORMAL, storage.getState()); Assert.assertFalse(sd.getMetaTmpFile().exists()); Assert.assertTrue(sd.getMetaFile().exists()); } finally { storage.close();
@Override void execute() throws IOException { File openFile = storage.getStorageDir().getOpenLogFile(newStartIndex); Preconditions.assertTrue(!openFile.exists(), "open file %s exists for %s", openFile, name); Preconditions.assertTrue(out == null && pendingFlushNum == 0); out = new LogOutputStream(openFile, false, segmentMaxSize, preallocatedSize, bufferSize); Preconditions.assertTrue(openFile.exists(), "Failed to create file %s for %s", openFile.getAbsolutePath(), name); LOG.info("{}: created new log segment {}", name, openFile); }
@Test public void testZeroSizeInProgressFile() throws Exception { final RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); final File file = storage.getStorageDir().getOpenLogFile(0); storage.close(); // create zero size in-progress file LOG.info("file: " + file); Assert.assertTrue(file.createNewFile()); final Path path = file.toPath(); Assert.assertTrue(Files.exists(path)); Assert.assertEquals(0, Files.size(path)); // getLogSegmentFiles should remove it. final List<RaftStorageDirectory.LogPathAndIndex> logs = storage.getStorageDir().getLogSegmentFiles(); Assert.assertEquals(0, logs.size()); Assert.assertFalse(Files.exists(path)); } }
public boolean hasMetaFile() { return getMetaFile().exists(); }
private void loadLogSegments(long lastIndexInSnapshot, Consumer<LogEntryProto> logConsumer) throws IOException { try(AutoCloseableLock writeLock = writeLock()) { List<LogPathAndIndex> paths = storage.getStorageDir().getLogSegmentFiles(); int i = 0; for (LogPathAndIndex pi : paths) { // During the initial loading, we can only confirm the committed // index based on the snapshot. This means if a log segment is not kept // in cache after the initial loading, later we have to load its content // again for updating the state machine. // TODO we should let raft peer persist its committed index periodically // so that during the initial loading we can apply part of the log // entries to the state machine boolean keepEntryInCache = (paths.size() - i++) <= cache.getMaxCachedSegments(); cache.loadSegment(pi, keepEntryInCache, logConsumer); } // if the largest index is smaller than the last index in snapshot, we do // not load the log to avoid holes between log segments. This may happen // when the local I/O worker is too slow to persist log (slower than // committing the log and taking snapshot) if (!cache.isEmpty() && cache.getEndIndex() < lastIndexInSnapshot) { LOG.warn("End log index {} is smaller than last index in snapshot {}", cache.getEndIndex(), lastIndexInSnapshot); cache.clear(); // TODO purge all segment files } } }