HDFS-11448. JN log segment syncing should support HA upgrade. Contributed by Hanisha Koneru.

This commit is contained in:
Arpit Agarwal 2017-05-04 15:57:44 -07:00
parent 54e2b9e876
commit 07761af357
4 changed files with 108 additions and 49 deletions

View File

@ -58,6 +58,8 @@ class JNStorage extends Storage {
private static final List<Pattern> PAXOS_DIR_PURGE_REGEXES = private static final List<Pattern> PAXOS_DIR_PURGE_REGEXES =
ImmutableList.of(Pattern.compile("(\\d+)")); ImmutableList.of(Pattern.compile("(\\d+)"));
private static final String STORAGE_EDITS_SYNC = "edits.sync";
/** /**
* @param conf Configuration object * @param conf Configuration object
* @param logDir the path to the directory in which data will be stored * @param logDir the path to the directory in which data will be stored
@ -120,12 +122,29 @@ class JNStorage extends Storage {
return new File(sd.getCurrentDir(), name); return new File(sd.getCurrentDir(), name);
} }
File getTemporaryEditsFile(long startTxId, long endTxId, long timestamp) { File getCurrentDir() {
return NNStorage.getTemporaryEditsFile(sd, startTxId, endTxId, timestamp); return sd.getCurrentDir();
}
/**
* Directory {@code edits.sync} temporarily holds the log segments
* downloaded through {@link JournalNodeSyncer} before they are moved to
* {@code current} directory.
*
* @return the directory path
*/
File getEditsSyncDir() {
return new File(sd.getRoot(), STORAGE_EDITS_SYNC);
}
File getTemporaryEditsFile(long startTxId, long endTxId) {
return new File(getEditsSyncDir(), String.format("%s_%019d-%019d",
NNStorage.NameNodeFile.EDITS.getName(), startTxId, endTxId));
} }
File getFinalizedEditsFile(long startTxId, long endTxId) { File getFinalizedEditsFile(long startTxId, long endTxId) {
return NNStorage.getFinalizedEditsFile(sd, startTxId, endTxId); return new File(sd.getCurrentDir(), String.format("%s_%019d-%019d",
NNStorage.NameNodeFile.EDITS.getName(), startTxId, endTxId));
} }
/** /**

View File

@ -24,6 +24,8 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.net.URL; import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.security.PrivilegedExceptionAction; import java.security.PrivilegedExceptionAction;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -1092,19 +1094,28 @@ public class Journal implements Closeable {
committedTxnId.set(startTxId - 1); committedTxnId.set(startTxId - 1);
} }
synchronized boolean renameTmpSegment(File tmpFile, File finalFile, synchronized boolean moveTmpSegmentToCurrent(File tmpFile, File finalFile,
long endTxId) throws IOException { long endTxId) throws IOException {
final boolean success; final boolean success;
if (endTxId <= committedTxnId.get()) { if (endTxId <= committedTxnId.get()) {
success = tmpFile.renameTo(finalFile); if (!finalFile.getParentFile().exists()) {
if (!success) { LOG.error(finalFile.getParentFile() + " doesn't exist. Aborting tmp " +
LOG.warn("Unable to rename edits file from " + tmpFile + " to " + "segment move to current directory");
return false;
}
Files.move(tmpFile.toPath(), finalFile.toPath(),
StandardCopyOption.ATOMIC_MOVE);
if (finalFile.exists() && FileUtil.canRead(finalFile)) {
success = true;
} else {
success = false;
LOG.warn("Unable to move edits file from " + tmpFile + " to " +
finalFile); finalFile);
} }
} else { } else {
success = false; success = false;
LOG.error("The endTxId of the temporary file is not less than the " + LOG.error("The endTxId of the temporary file is not less than the " +
"last committed transaction id. Aborting renaming to final file" + "last committed transaction id. Aborting move to final file" +
finalFile); finalFile);
} }

View File

@ -41,7 +41,6 @@ import org.apache.hadoop.hdfs.server.protocol.RemoteEditLog;
import org.apache.hadoop.hdfs.util.DataTransferThrottler; import org.apache.hadoop.hdfs.util.DataTransferThrottler;
import org.apache.hadoop.ipc.RPC; import org.apache.hadoop.ipc.RPC;
import org.apache.hadoop.util.Daemon; import org.apache.hadoop.util.Daemon;
import org.apache.hadoop.util.Time;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -98,6 +97,11 @@ public class JournalNodeSyncer {
void stopSync() { void stopSync() {
shouldSync = false; shouldSync = false;
// Delete the edits.sync directory
File editsSyncDir = journal.getStorage().getEditsSyncDir();
if (editsSyncDir.exists()) {
FileUtil.fullyDelete(editsSyncDir);
}
if (syncJournalDaemon != null) { if (syncJournalDaemon != null) {
syncJournalDaemon.interrupt(); syncJournalDaemon.interrupt();
} }
@ -112,6 +116,15 @@ public class JournalNodeSyncer {
} }
} }
private boolean createEditsSyncDir() {
File editsSyncDir = journal.getStorage().getEditsSyncDir();
if (editsSyncDir.exists()) {
LOG.info(editsSyncDir + " directory already exists.");
return true;
}
return editsSyncDir.mkdir();
}
private boolean getOtherJournalNodeProxies() { private boolean getOtherJournalNodeProxies() {
List<InetSocketAddress> otherJournalNodes = getOtherJournalNodeAddrs(); List<InetSocketAddress> otherJournalNodes = getOtherJournalNodeAddrs();
if (otherJournalNodes == null || otherJournalNodes.isEmpty()) { if (otherJournalNodes == null || otherJournalNodes.isEmpty()) {
@ -135,35 +148,51 @@ public class JournalNodeSyncer {
} }
private void startSyncJournalsDaemon() { private void startSyncJournalsDaemon() {
syncJournalDaemon = new Daemon(new Runnable() { syncJournalDaemon = new Daemon(() -> {
@Override // Wait for journal to be formatted to create edits.sync directory
public void run() { while(!journal.isFormatted()) {
while(shouldSync) { try {
try { Thread.sleep(journalSyncInterval);
if (!journal.isFormatted()) { } catch (InterruptedException e) {
LOG.warn("Journal not formatted. Cannot sync."); LOG.error("JournalNodeSyncer daemon received Runtime exception.", e);
} else { Thread.currentThread().interrupt();
syncJournals(); return;
} }
Thread.sleep(journalSyncInterval); }
} catch (Throwable t) { if (!createEditsSyncDir()) {
if (!shouldSync) { LOG.error("Failed to create directory for downloading log " +
if (t instanceof InterruptedException) { "segments: %s. Stopping Journal Node Sync.",
LOG.info("Stopping JournalNode Sync."); journal.getStorage().getEditsSyncDir());
} else { return;
LOG.warn("JournalNodeSyncer received an exception while " + }
"shutting down.", t); while(shouldSync) {
} try {
break; if (!journal.isFormatted()) {
} else { LOG.warn("Journal cannot sync. Not formatted.");
if (t instanceof InterruptedException) { } else {
LOG.warn("JournalNodeSyncer interrupted", t); syncJournals();
break;
}
}
LOG.error(
"JournalNodeSyncer daemon received Runtime exception. ", t);
} }
Thread.sleep(journalSyncInterval);
} catch (Throwable t) {
if (!shouldSync) {
if (t instanceof InterruptedException) {
LOG.info("Stopping JournalNode Sync.");
Thread.currentThread().interrupt();
return;
} else {
LOG.warn("JournalNodeSyncer received an exception while " +
"shutting down.", t);
}
break;
} else {
if (t instanceof InterruptedException) {
LOG.warn("JournalNodeSyncer interrupted", t);
Thread.currentThread().interrupt();
return;
}
}
LOG.error(
"JournalNodeSyncer daemon received Runtime exception. ", t);
} }
} }
}); });
@ -335,8 +364,8 @@ public class JournalNodeSyncer {
/** /**
* Transfer an edit log from one journal node to another for sync-up. * Transfer an edit log from one journal node to another for sync-up.
*/ */
private boolean downloadMissingLogSegment(URL url, RemoteEditLog log) throws private boolean downloadMissingLogSegment(URL url, RemoteEditLog log)
IOException { throws IOException {
LOG.info("Downloading missing Edit Log from " + url + " to " + jnStorage LOG.info("Downloading missing Edit Log from " + url + " to " + jnStorage
.getRoot()); .getRoot());
@ -350,9 +379,10 @@ public class JournalNodeSyncer {
return true; return true;
} }
final long milliTime = Time.monotonicNow(); // Download the log segment to current.tmp directory first.
File tmpEditsFile = jnStorage.getTemporaryEditsFile(log.getStartTxId(), log File tmpEditsFile = jnStorage.getTemporaryEditsFile(
.getEndTxId(), milliTime); log.getStartTxId(), log.getEndTxId());
try { try {
Util.doGetUrl(url, ImmutableList.of(tmpEditsFile), jnStorage, false, Util.doGetUrl(url, ImmutableList.of(tmpEditsFile), jnStorage, false,
logSegmentTransferTimeout, throttler); logSegmentTransferTimeout, throttler);
@ -367,14 +397,12 @@ public class JournalNodeSyncer {
LOG.info("Downloaded file " + tmpEditsFile.getName() + " of size " + LOG.info("Downloaded file " + tmpEditsFile.getName() + " of size " +
tmpEditsFile.length() + " bytes."); tmpEditsFile.length() + " bytes.");
LOG.debug("Renaming " + tmpEditsFile.getName() + " to " final boolean moveSuccess = journal.moveTmpSegmentToCurrent(tmpEditsFile,
+ finalEditsFile.getName());
boolean renameSuccess = journal.renameTmpSegment(tmpEditsFile,
finalEditsFile, log.getEndTxId()); finalEditsFile, log.getEndTxId());
if (!renameSuccess) { if (!moveSuccess) {
//If rename is not successful, delete the tmpFile // If move is not successful, delete the tmpFile
LOG.debug("Renaming unsuccessful. Deleting temporary file: " LOG.debug("Move to current directory unsuccessful. Deleting temporary " +
+ tmpEditsFile); "file: " + tmpEditsFile);
if (!tmpEditsFile.delete()) { if (!tmpEditsFile.delete()) {
LOG.warn("Deleting " + tmpEditsFile + " has failed"); LOG.warn("Deleting " + tmpEditsFile + " has failed");
} }

View File

@ -57,6 +57,7 @@ public class TestJournalNodeSync {
@Before @Before
public void setUpMiniCluster() throws IOException { public void setUpMiniCluster() throws IOException {
final Configuration conf = new HdfsConfiguration(); final Configuration conf = new HdfsConfiguration();
conf.setBoolean(DFSConfigKeys.DFS_JOURNALNODE_ENABLE_SYNC_KEY, true);
conf.setLong(DFSConfigKeys.DFS_JOURNALNODE_SYNC_INTERVAL_KEY, 1000L); conf.setLong(DFSConfigKeys.DFS_JOURNALNODE_SYNC_INTERVAL_KEY, 1000L);
qjmhaCluster = new MiniQJMHACluster.Builder(conf).setNumNameNodes(2) qjmhaCluster = new MiniQJMHACluster.Builder(conf).setNumNameNodes(2)
.build(); .build();