@Override public void close() throws IOException { stateMachineUpdater.stop(); LOG.info("{} closes. The last applied log index is {}", getSelfId(), getLastAppliedIndex()); log.close(); storage.close(); }
@Override public String toString() { return getStorageDir() + ""; } }
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); } } }
@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)); } }
@Test public void testNotExistent() throws IOException { FileUtils.deleteFully(storageDir); // we will format the empty directory RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); Assert.assertEquals(StorageState.NORMAL, storage.getState()); try { new RaftStorage(storageDir, StartupOption.FORMAT).close(); Assert.fail("the format should fail since the storage is still locked"); } catch (IOException e) { Assert.assertTrue(e.getMessage().contains("directory is already locked")); } storage.close(); FileUtils.deleteFully(storageDir); Assert.assertTrue(storageDir.createNewFile()); try { new RaftStorage(storageDir, StartupOption.REGULAR); Assert.fail(); } catch (IOException e) { Assert.assertTrue( e.getMessage().contains(StorageState.NON_EXISTENT.name())); } }
final RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); File openSegment = storage.getStorageDir().getOpenLogFile(0);
private void testLoadSegment(boolean loadInitial) throws Exception { // load an open segment File openSegmentFile = prepareLog(true, 0, 100, 0); RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); LogSegment openSegment = LogSegment.loadSegment(storage, openSegmentFile, 0, INVALID_LOG_INDEX, true, loadInitial, null); checkLogSegment(openSegment, 0, 99, true, openSegmentFile.length(), 0); storage.close(); // for open segment we currently always keep log entries in the memory Assert.assertEquals(0, openSegment.getLoadingTimes()); // load a closed segment (1000-1099) File closedSegmentFile = prepareLog(false, 1000, 100, 1); LogSegment closedSegment = LogSegment.loadSegment(storage, closedSegmentFile, 1000, 1099, false, loadInitial, null); checkLogSegment(closedSegment, 1000, 1099, false, closedSegment.getTotalSize(), 1); Assert.assertEquals(loadInitial ? 0 : 1, closedSegment.getLoadingTimes()); }
RaftStorage storage = new RaftStorage(storageDir, RaftServerConstants.StartupOption.REGULAR);
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; } }
@Override public Metadata loadMetadata() throws IOException { return new Metadata( RaftPeerId.getRaftPeerId(storage.getMetaFile().getVotedFor()), storage.getMetaFile().getTerm()); }
public SnapshotInfo getLastestSnapshot() throws IOException { return getStateMachineStorage().getLatestSnapshot(); }
@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)); } }
@Test public void testNotExistent() throws IOException { FileUtils.deleteFully(storageDir); // we will format the empty directory RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); Assert.assertEquals(StorageState.NORMAL, storage.getState()); try { new RaftStorage(storageDir, StartupOption.FORMAT).close(); Assert.fail("the format should fail since the storage is still locked"); } catch (IOException e) { Assert.assertTrue(e.getMessage().contains("directory is already locked")); } storage.close(); FileUtils.deleteFully(storageDir); Assert.assertTrue(storageDir.createNewFile()); try { new RaftStorage(storageDir, StartupOption.REGULAR); Assert.fail(); } catch (IOException e) { Assert.assertTrue( e.getMessage().contains(StorageState.NON_EXISTENT.name())); } }
final RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); File openSegment = storage.getStorageDir().getOpenLogFile(0);
private void testLoadSegment(boolean loadInitial, boolean isLastEntryPartiallyWritten) throws Exception { // load an open segment final File openSegmentFile = prepareLog(true, 0, 100, 0, isLastEntryPartiallyWritten); RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); LogSegment openSegment = LogSegment.loadSegment(storage, openSegmentFile, 0, INVALID_LOG_INDEX, true, loadInitial, null); final int delta = isLastEntryPartiallyWritten? 1: 0; checkLogSegment(openSegment, 0, 99 - delta, true, openSegmentFile.length(), 0); storage.close(); // for open segment we currently always keep log entries in the memory Assert.assertEquals(0, openSegment.getLoadingTimes()); // load a closed segment (1000-1099) final File closedSegmentFile = prepareLog(false, 1000, 100, 1, false); LogSegment closedSegment = LogSegment.loadSegment(storage, closedSegmentFile, 1000, 1099, false, loadInitial, null); checkLogSegment(closedSegment, 1000, 1099, false, closedSegment.getTotalSize(), 1); Assert.assertEquals(loadInitial ? 0 : 1, closedSegment.getLoadingTimes()); }
ServerState(RaftPeerId id, RaftGroup group, RaftProperties prop, RaftServerImpl server, StateMachine stateMachine) throws IOException { this.selfId = id; this.server = server; RaftConfiguration initialConf = RaftConfiguration.newBuilder() .setConf(group.getPeers()).build(); configurationManager = new ConfigurationManager(initialConf); LOG.info("{}: {}", id, configurationManager); // use full uuid string to create a subdirectory final File dir = chooseStorageDir(RaftServerConfigKeys.storageDirs(prop), group.getGroupId().getUuid().toString()); storage = new RaftStorage(dir, RaftServerConstants.StartupOption.REGULAR); snapshotManager = new SnapshotManager(storage, id); long lastApplied = initStatemachine(stateMachine, group.getGroupId()); // On start the leader is null, start the clock now leaderId = null; this.lastNoLeaderTime = Timestamp.currentTime(); this.leaderElectionTimeoutMs = RaftServerConfigKeys.leaderElectionTimeout(prop).toIntExact(TimeUnit.MILLISECONDS); // we cannot apply log entries to the state machine in this step, since we // do not know whether the local log entries have been committed. log = initLog(id, prop, lastApplied, this::setRaftConf); RaftLog.Metadata metadata = log.loadMetadata(); currentTerm = metadata.getTerm(); votedFor = metadata.getVotedFor(); stateMachineUpdater = new StateMachineUpdater(stateMachine, server, log, lastApplied, prop); }
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; } }
/** * {@inheritDoc} * * This operation is protected by the RaftServer's lock */ @Override public void writeMetadata(long term, RaftPeerId votedFor) throws IOException { storage.getMetaFile().set(term, votedFor != null ? votedFor.toString() : null); }
public SnapshotInfo getLatestSnapshot() throws IOException { return getStateMachineStorage().getLatestSnapshot(); }
private File prepareLog(boolean isOpen, long start, int size, long term) throws IOException { RaftStorage storage = new RaftStorage(storageDir, StartupOption.REGULAR); File file = isOpen ? storage.getStorageDir().getOpenLogFile(start) : storage.getStorageDir().getClosedLogFile(start, start + size - 1); LogEntryProto[] entries = new LogEntryProto[size]; try (LogOutputStream out = new LogOutputStream(file, false, segmentMaxSize, preallocatedSize, bufferSize)) { for (int i = 0; i < size; i++) { SimpleOperation op = new SimpleOperation("m" + i); entries[i] = ProtoUtils.toLogEntryProto(op.getLogEntryContent(), term, i + start, clientId, callId); out.write(entries[i]); } } storage.close(); return file; }