diff --git a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt index 3b5d5e4e82b..2234b17fcc9 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt +++ b/hadoop-hdfs-project/hadoop-hdfs/CHANGES.txt @@ -142,6 +142,9 @@ Release 0.23.3 - UNRELEASED HDFS-3038. Add FSEditLog.metrics to findbugs exclude list. (todd via atm) + HDFS-2188. Make FSEditLog create its journals from a list of URIs rather + than NNStorage. (Ivan Kelly via jitendra) + Release 0.23.2 - UNRELEASED INCOMPATIBLE CHANGES diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLog.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLog.java index 0334bbdc6f4..4836b996c49 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLog.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSEditLog.java @@ -18,10 +18,11 @@ package org.apache.hadoop.hdfs.server.namenode; import static org.apache.hadoop.hdfs.server.common.Util.now; - +import java.net.URI; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.apache.commons.logging.Log; @@ -42,9 +43,11 @@ import org.apache.hadoop.hdfs.server.protocol.NamenodeRegistration; import org.apache.hadoop.hdfs.server.protocol.RemoteEditLogManifest; import org.apache.hadoop.io.IOUtils; import org.apache.hadoop.security.token.delegation.DelegationKey; +import org.apache.hadoop.conf.Configuration; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; /** * FSEditLog maintains a log of the namespace modifications. @@ -122,23 +125,68 @@ public class FSEditLog { } }; + final private Collection editsDirs; + + /** + * Construct FSEditLog with default configuration, taking editDirs from NNStorage + * @param storage Storage object used by namenode + */ + @VisibleForTesting FSEditLog(NNStorage storage) { + this(new Configuration(), storage, Collections.emptyList()); + } + + /** + * Constructor for FSEditLog. Add underlying journals are constructed, but + * no streams are opened until open() is called. + * + * @param conf The namenode configuration + * @param storage Storage object used by namenode + * @param editsDirs List of journals to use + */ + FSEditLog(Configuration conf, NNStorage storage, Collection editsDirs) { isSyncRunning = false; this.storage = storage; metrics = NameNode.getNameNodeMetrics(); lastPrintTime = now(); + + if (editsDirs.isEmpty()) { + // if this is the case, no edit dirs have been explictly configured + // image dirs are to be used for edits too + try { + editsDirs = Lists.newArrayList(storage.getEditsDirectories()); + } catch (IOException ioe) { + // cannot get list from storage, so the empty editsDirs + // will be assigned. an error will be thrown on first use + // of the editlog, as no journals will exist + } + this.editsDirs = editsDirs; + } else { + this.editsDirs = Lists.newArrayList(editsDirs); + } this.journalSet = new JournalSet(); - for (StorageDirectory sd : storage.dirIterable(NameNodeDirType.EDITS)) { - journalSet.add(new FileJournalManager(sd)); + for (URI u : this.editsDirs) { + StorageDirectory sd = storage.getStorageDirectory(u); + if (sd != null) { + journalSet.add(new FileJournalManager(sd)); + } } - + if (journalSet.isEmpty()) { LOG.error("No edits directories configured!"); } state = State.BETWEEN_LOG_SEGMENTS; } - + + /** + * Get the list of URIs the editlog is using for storage + * @return collection of URIs in use by the edit log + */ + Collection getEditURIs() { + return editsDirs; + } + /** * Initialize the output stream for logging, opening the first * log segment. diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java index dade5aba813..7977a4d86db 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java @@ -120,7 +120,7 @@ public class FSImage implements Closeable { storage.setRestoreFailedStorage(true); } - this.editLog = new FSEditLog(storage); + this.editLog = new FSEditLog(conf, storage, editsDirs); archivalManager = new NNStorageRetentionManager(conf, storage, editLog); } @@ -150,8 +150,7 @@ public class FSImage implements Closeable { "NameNode formatting should be performed before reading the image"; Collection imageDirs = storage.getImageDirectories(); - Collection editsDirs = storage.getEditsDirectories(); - + Collection editsDirs = editLog.getEditURIs(); // none of the data dirs exist if((imageDirs.size() == 0 || editsDirs.size() == 0) diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java index 4b282917211..1530cc5efad 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java @@ -57,6 +57,7 @@ import org.apache.hadoop.net.DNS; import com.google.common.base.Preconditions; import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.Lists; /** * NNStorage is responsible for management of the StorageDirectories used by @@ -157,7 +158,9 @@ public class NNStorage extends Storage implements Closeable { storageDirs = new CopyOnWriteArrayList(); - setStorageDirectories(imageDirs, editsDirs); + // this may modify the editsDirs, so copy before passing in + setStorageDirectories(imageDirs, + Lists.newArrayList(editsDirs)); } @Override // Storage @@ -299,6 +302,27 @@ public class NNStorage extends Storage implements Closeable { } } + /** + * Return the storage directory corresponding to the passed URI + * @param uri URI of a storage directory + * @return The matching storage directory or null if none found + */ + StorageDirectory getStorageDirectory(URI uri) { + try { + uri = Util.fileAsURI(new File(uri)); + Iterator it = dirIterator(); + for (; it.hasNext(); ) { + StorageDirectory sd = it.next(); + if (Util.fileAsURI(sd.getRoot()).equals(uri)) { + return sd; + } + } + } catch (IOException ioe) { + LOG.warn("Error converting file to URI", ioe); + } + return null; + } + /** * Checks the consistency of a URI, in particular if the scheme * is specified and is supported by a concrete implementation diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java index 58190296b3d..63628c09a60 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java @@ -45,10 +45,13 @@ import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType; import org.apache.hadoop.hdfs.util.Holder; import org.apache.hadoop.hdfs.util.MD5FileUtils; import org.apache.hadoop.io.IOUtils; +import org.apache.hadoop.conf.Configuration; import org.mockito.Mockito; +import org.mockito.Matchers; import com.google.common.base.Joiner; import com.google.common.collect.Lists; +import com.google.common.collect.ImmutableList; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.io.Files; @@ -111,7 +114,7 @@ public abstract class FSImageTestUtil { Mockito.doReturn(type) .when(sd).getStorageDirType(); Mockito.doReturn(currentDir).when(sd).getCurrentDir(); - + Mockito.doReturn(currentDir).when(sd).getRoot(); Mockito.doReturn(mockFile(true)).when(sd).getVersionFile(); Mockito.doReturn(mockFile(false)).when(sd).getPreviousDir(); return sd; @@ -133,7 +136,8 @@ public abstract class FSImageTestUtil { // Version file should always exist doReturn(mockFile(true)).when(sd).getVersionFile(); - + doReturn(mockFile(true)).when(sd).getRoot(); + // Previous dir optionally exists doReturn(mockFile(previousExists)) .when(sd).getPreviousDir(); @@ -148,6 +152,7 @@ public abstract class FSImageTestUtil { doReturn(files).when(mockDir).listFiles(); doReturn(mockDir).when(sd).getCurrentDir(); + return sd; } @@ -175,11 +180,16 @@ public abstract class FSImageTestUtil { assertTrue(logDir.mkdirs() || logDir.exists()); Files.deleteDirectoryContents(logDir); NNStorage storage = Mockito.mock(NNStorage.class); - List sds = Lists.newArrayList( - FSImageTestUtil.mockStorageDirectory(logDir, NameNodeDirType.EDITS)); + StorageDirectory sd + = FSImageTestUtil.mockStorageDirectory(logDir, NameNodeDirType.EDITS); + List sds = Lists.newArrayList(sd); Mockito.doReturn(sds).when(storage).dirIterable(NameNodeDirType.EDITS); + Mockito.doReturn(sd).when(storage) + .getStorageDirectory(Matchers.anyObject()); - return new FSEditLog(storage); + return new FSEditLog(new Configuration(), + storage, + ImmutableList.of(logDir.toURI())); } diff --git a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java index 2ebfc48d01b..bc9be8405b9 100644 --- a/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java +++ b/hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestEditLog.java @@ -46,6 +46,7 @@ import org.apache.hadoop.hdfs.DFSConfigKeys; import org.apache.hadoop.hdfs.HdfsConfiguration; import org.apache.hadoop.hdfs.MiniDFSCluster; import org.apache.hadoop.hdfs.protocol.HdfsFileStatus; +import org.apache.hadoop.hdfs.server.common.Util; import org.apache.hadoop.hdfs.server.namenode.EditLogFileInputStream; import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory; import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType; @@ -904,8 +905,11 @@ public class TestEditLog extends TestCase { * The syntax [1,] specifies an in-progress log starting at * txid 1. */ - private NNStorage mockStorageWithEdits(String... editsDirSpecs) { + private NNStorage mockStorageWithEdits(String... editsDirSpecs) throws IOException { List sds = Lists.newArrayList(); + List uris = Lists.newArrayList(); + + NNStorage storage = Mockito.mock(NNStorage.class); for (String dirSpec : editsDirSpecs) { List files = Lists.newArrayList(); String[] logSpecs = dirSpec.split("\\|"); @@ -921,13 +925,17 @@ public class TestEditLog extends TestCase { Long.valueOf(m.group(2)))); } } - sds.add(FSImageTestUtil.mockStorageDirectory( + StorageDirectory sd = FSImageTestUtil.mockStorageDirectory( NameNodeDirType.EDITS, false, - files.toArray(new String[0]))); - } - - NNStorage storage = Mockito.mock(NNStorage.class); + files.toArray(new String[0])); + sds.add(sd); + URI u = URI.create("file:///storage"+ Math.random()); + Mockito.doReturn(sd).when(storage).getStorageDirectory(u); + uris.add(u); + } + Mockito.doReturn(sds).when(storage).dirIterable(NameNodeDirType.EDITS); + Mockito.doReturn(uris).when(storage).getEditsDirectories(); return storage; }