synchronized Journal getOrCreateJournal(String jid, String nameServiceId, StartupOption startOpt) throws IOException { QuorumJournalManager.checkJournalId(jid); Journal journal = journalsById.get(jid); if (journal == null) { File logDir = getLogDir(jid, nameServiceId); LOG.info("Initializing journal in directory " + logDir); journal = new Journal(conf, logDir, jid, startOpt, new ErrorReporter()); journalsById.put(jid, journal); // Start SyncJouranl thread, if JournalNode Sync is enabled if (conf.getBoolean( DFSConfigKeys.DFS_JOURNALNODE_ENABLE_SYNC_KEY, DFSConfigKeys.DFS_JOURNALNODE_ENABLE_SYNC_DEFAULT)) { startSyncer(journal, jid, nameServiceId); } } else if (journalSyncersById.get(jid) != null && !journalSyncersById.get(jid).isJournalSyncerStarted() && !journalsById.get(jid).getTriedJournalSyncerStartedwithnsId() && nameServiceId != null) { startSyncer(journal, jid, nameServiceId); } return journal; }
@Override public void finalizeLogSegment(RequestInfo reqInfo, long startTxId, long endTxId) throws IOException { jn.getOrCreateJournal(reqInfo.getJournalId(), reqInfo.getNameServiceId()) .finalizeLogSegment(reqInfo, startTxId, endTxId); }
@Override public void format(String journalId, String nameServiceId, NamespaceInfo nsInfo, boolean force) throws IOException { jn.getOrCreateJournal(journalId, nameServiceId).format(nsInfo, force); }
JournalNodeSyncer(JournalNode jouranlNode, Journal journal, String jid, Configuration conf, String nameServiceId) { this.jn = jouranlNode; this.journal = journal; this.jid = jid; this.nameServiceId = nameServiceId; this.jnStorage = journal.getStorage(); this.conf = conf; journalSyncInterval = conf.getLong( DFSConfigKeys.DFS_JOURNALNODE_SYNC_INTERVAL_KEY, DFSConfigKeys.DFS_JOURNALNODE_SYNC_INTERVAL_DEFAULT); logSegmentTransferTimeout = conf.getInt( DFSConfigKeys.DFS_EDIT_LOG_TRANSFER_TIMEOUT_KEY, DFSConfigKeys.DFS_EDIT_LOG_TRANSFER_TIMEOUT_DEFAULT); throttler = getThrottler(conf); metrics = journal.getMetrics(); journalSyncerStarted = false; }
NamespaceInfo nsInfo, long epoch) throws IOException { checkFormatted(); storage.checkConsistentNamespace(nsInfo); if (epoch <= getLastPromisedEpoch()) { throw new IOException("Proposed epoch " + epoch + " <= last promise " + getLastPromisedEpoch() + " ; journal id: " + journalId); updateLastPromisedEpoch(epoch); abortCurSegment(); EditLogFile latestFile = scanStorageForLatestEdits();
private void startSyncJournalsDaemon() { syncJournalDaemon = new Daemon(() -> { while(!journal.isFormatted()) { try { Thread.sleep(journalSyncInterval); LOG.error("Failed to create directory for downloading log " + "segments: %s. Stopping Journal Node Sync.", journal.getStorage().getEditsSyncDir()); return; if (!journal.isFormatted()) { LOG.warn("Journal cannot sync. Not formatted."); } else {
SegmentStateProto segment, URL fromUrl) throws IOException { checkFormatted(); checkRequest(reqInfo); abortCurSegment(); PersistedRecoveryPaxosData oldData = getPersistedPaxosData(segmentTxId); PersistedRecoveryPaxosData newData = PersistedRecoveryPaxosData.newBuilder() .setAcceptedInEpoch(reqInfo.getEpoch()) alwaysAssert(oldData.getAcceptedInEpoch() <= reqInfo.getEpoch(), "Bad paxos transition, out-of-order epochs.\nOld: %s\nNew: " + "%s\nJournalId: %s\n", SegmentStateProto currentSegment = getSegmentInfo(segmentTxId); if (currentSegment == null || currentSegment.getEndTxId() != segment.getEndTxId()) { updateHighestWrittenTxId(Math.max(segment.getEndTxId(), highestWrittenTxId)); } else { if (txnRange(currentSegment).contains(committedTxnId.get()) && !txnRange(segment).contains(committedTxnId.get())) { throw new AssertionError( "Cannot replace segment " + alwaysAssert(currentSegment.getIsInProgress(),
checkFormatted(); checkRequest(reqInfo); abortCurSegment(); PersistedRecoveryPaxosData previouslyAccepted = getPersistedPaxosData(segmentTxId); completeHalfDoneAcceptRecovery(previouslyAccepted); SegmentStateProto segInfo = getSegmentInfo(segmentTxId); boolean hasFinalizedSegment = segInfo != null && !segInfo.getIsInProgress();
@Test (timeout = 10000) public void testFormatResetsCachedValues() throws Exception { journal.newEpoch(FAKE_NSINFO, 12345L); journal.startLogSegment(new RequestInfo(JID, 12345L, 1L, 0L), 1L, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); assertEquals(12345L, journal.getLastPromisedEpoch()); assertEquals(12345L, journal.getLastWriterEpoch()); assertTrue(journal.isFormatted()); // Close the journal in preparation for reformatting it. journal.close(); journal.format(FAKE_NSINFO_2); assertEquals(0, journal.getLastPromisedEpoch()); assertEquals(0, journal.getLastWriterEpoch()); assertTrue(journal.isFormatted()); }
@Test (timeout = 10000) public void testRestartJournal() throws Exception { journal.newEpoch(FAKE_NSINFO, 1); journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); journal.journal(makeRI(2), 1, 1, 2, QJMTestUtil.createTxnData(1, 2)); // Don't finalize. String storageString = journal.getStorage().toColonSeparatedString(); System.err.println("storage string: " + storageString); journal.close(); // close to unlock the storage dir // Now re-instantiate, make sure history is still there journal = new Journal(conf, TEST_LOG_DIR, JID, StartupOption.REGULAR, mockErrorReporter); // The storage info should be read, even if no writer has taken over. assertEquals(storageString, journal.getStorage().toColonSeparatedString()); assertEquals(1, journal.getLastPromisedEpoch()); NewEpochResponseProtoOrBuilder newEpoch = journal.newEpoch(FAKE_NSINFO, 2); assertEquals(1, newEpoch.getLastSegmentTxId()); }
/** * Test that, if the writer crashes at the very beginning of a segment, * before any transactions are written, that the next newEpoch() call * returns the prior segment txid as its most recent segment. */ @Test (timeout = 10000) public void testNewEpochAtBeginningOfSegment() throws Exception { journal.newEpoch(FAKE_NSINFO, 1); journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); journal.journal(makeRI(2), 1, 1, 2, QJMTestUtil.createTxnData(1, 2)); journal.finalizeLogSegment(makeRI(3), 1, 2); journal.startLogSegment(makeRI(4), 3, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); NewEpochResponseProto resp = journal.newEpoch(FAKE_NSINFO, 2); assertEquals(1, resp.getLastSegmentTxId()); }
journal.newEpoch(FAKE_NSINFO, 1); journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); journal.journal(makeRI(2), 1, 1, 3, QJMTestUtil.createTxnData(1, 3)); journal.finalizeLogSegment(makeRI(3), 1, 6); fail("did not fail to finalize"); } catch (JournalOutOfSyncException e) { journal.close(); journal = new Journal(conf, TEST_LOG_DIR, JID, StartupOption.REGULAR, mockErrorReporter); journal.finalizeLogSegment(makeRI(4), 1, 6); fail("did not fail to finalize"); } catch (JournalOutOfSyncException e) {
journal.newEpoch(FAKE_NSINFO, 1); journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); journal.journal(makeRI(2), 1, 1, 3, QJMTestUtil.createTxnData(1, 3)); journal.getStorage().getInProgressEditLog(1)); journal.startLogSegment(makeRI(3), 6, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); journal.journal(makeRI(4), 6, 6, 3, QJMTestUtil.createTxnData(6, 3)); journal.getStorage().getInProgressEditLog(1)); GenericTestUtils.assertExists( journal.getStorage().getInProgressEditLog(6));
@Test (timeout = 10000) public void testEpochHandling() throws Exception { assertEquals(0, journal.getLastPromisedEpoch()); NewEpochResponseProto newEpoch = journal.newEpoch(FAKE_NSINFO, 1); assertFalse(newEpoch.hasLastSegmentTxId()); assertEquals(1, journal.getLastPromisedEpoch()); journal.newEpoch(FAKE_NSINFO, 3); assertFalse(newEpoch.hasLastSegmentTxId()); assertEquals(3, journal.getLastPromisedEpoch()); try { journal.newEpoch(FAKE_NSINFO, 3); fail("Should have failed to promise same epoch twice"); } catch (IOException ioe) { journal.startLogSegment(makeRI(1), 12345L, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); fail("Should have rejected call from prior epoch"); journal.journal(makeRI(1), 12345L, 100L, 0, new byte[0]); fail("Should have rejected call from prior epoch"); } catch (IOException ioe) {
/** * Test whether JNs can correctly handle editlog that cannot be decoded. */ @Test public void testScanEditLog() throws Exception { // use a future layout version journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION - 1); // in the segment we write garbage editlog, which can be scanned but // cannot be decoded final int numTxns = 5; byte[] ops = QJMTestUtil.createGabageTxns(1, 5); journal.journal(makeRI(2), 1, 1, numTxns, ops); // verify the in-progress editlog segment SegmentStateProto segmentState = journal.getSegmentInfo(1); assertTrue(segmentState.getIsInProgress()); Assert.assertEquals(numTxns, segmentState.getEndTxId()); Assert.assertEquals(1, segmentState.getStartTxId()); // finalize the segment and verify it again journal.finalizeLogSegment(makeRI(3), 1, numTxns); segmentState = journal.getSegmentInfo(1); assertFalse(segmentState.getIsInProgress()); Assert.assertEquals(numTxns, segmentState.getEndTxId()); Assert.assertEquals(1, segmentState.getStartTxId()); }
@Test (timeout = 10000) public void testMaintainCommittedTxId() throws Exception { journal.newEpoch(FAKE_NSINFO, 1); journal.startLogSegment(makeRI(1), 1, NameNodeLayoutVersion.CURRENT_LAYOUT_VERSION); // Send txids 1-3, with a request indicating only 0 committed journal.journal(new RequestInfo(JID, 1, 2, 0), 1, 1, 3, QJMTestUtil.createTxnData(1, 3)); assertEquals(0, journal.getCommittedTxnIdForTests()); // Send 4-6, with request indicating that through 3 is committed. journal.journal(new RequestInfo(JID, 1, 3, 3), 1, 4, 3, QJMTestUtil.createTxnData(4, 6)); assertEquals(3, journal.getCommittedTxnIdForTests()); }
@Test (timeout = 10000) public void testJournalLocking() throws Exception { Assume.assumeTrue(journal.getStorage().getStorageDir(0).isLockSupported()); StorageDirectory sd = journal.getStorage().getStorageDir(0); File lockFile = new File(sd.getRoot(), Storage.STORAGE_FILE_LOCK); // Journal should be locked, since the format() call locks it. GenericTestUtils.assertExists(lockFile); journal.newEpoch(FAKE_NSINFO, 1); try { new Journal(conf, TEST_LOG_DIR, JID, StartupOption.REGULAR, mockErrorReporter); fail("Did not fail to create another journal in same dir"); } catch (IOException ioe) { GenericTestUtils.assertExceptionContains( "Cannot lock storage", ioe); } journal.close(); // Journal should no longer be locked after the close() call. // Hence, should be able to create a new Journal in the same dir. Journal journal2 = new Journal(conf, TEST_LOG_DIR, JID, StartupOption.REGULAR, mockErrorReporter); journal2.newEpoch(FAKE_NSINFO, 2); journal2.close(); }
@Override public boolean isFormatted(String journalId, String nameServiceId) throws IOException { return jn.getOrCreateJournal(journalId, nameServiceId).isFormatted(); }
@Metric("Last accepted epoch") public long getLastPromisedEpoch() { try { return journal.getLastPromisedEpoch(); } catch (IOException e) { return -1L; } }
private boolean createEditsSyncDir() { File editsSyncDir = journal.getStorage().getEditsSyncDir(); if (editsSyncDir.exists()) { LOG.info(editsSyncDir + " directory already exists."); return true; } return editsSyncDir.mkdir(); }