Skip to content

Commit b3786d6

Browse files
committed
HDFS-15567. [SBN Read] HDFS should expose msync() API to allow downstream applications call it explicitly. Contributed by Konstantin V Shvachko.
1 parent b76b36e commit b3786d6

File tree

10 files changed

+114
-4
lines changed

10 files changed

+114
-4
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/AbstractFileSystem.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,6 +864,19 @@ public abstract FileStatus getFileStatus(final Path f)
864864
throws AccessControlException, FileNotFoundException,
865865
UnresolvedLinkException, IOException;
866866

867+
/**
868+
* Synchronize client metadata state.
869+
* <p/>In some FileSystem implementations such as HDFS metadata
870+
* synchronization is essential to guarantee consistency of read requests
871+
* particularly in HA setting.
872+
* @throws IOException
873+
* @throws UnsupportedOperationException
874+
*/
875+
public void msync() throws IOException, UnsupportedOperationException {
876+
throw new UnsupportedOperationException(getClass().getCanonicalName() +
877+
" does not support method msync");
878+
}
879+
867880
/**
868881
* The specification of this method matches that of
869882
* {@link FileContext#access(Path, FsAction)}

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileContext.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1249,6 +1249,16 @@ public FileStatus next(final AbstractFileSystem fs, final Path p)
12491249
}.resolve(this, absF);
12501250
}
12511251

1252+
/**
1253+
* Synchronize client metadata state.
1254+
*
1255+
* @throws IOException
1256+
* @throws UnsupportedOperationException
1257+
*/
1258+
public void msync() throws IOException, UnsupportedOperationException {
1259+
defaultFS.msync();
1260+
}
1261+
12521262
/**
12531263
* Checks if the user can access a path. The mode specifies which access
12541264
* checks to perform. If the requested permissions are granted, then the

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FileSystem.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2675,6 +2675,19 @@ public short getDefaultReplication(Path path) {
26752675
*/
26762676
public abstract FileStatus getFileStatus(Path f) throws IOException;
26772677

2678+
/**
2679+
* Synchronize client metadata state.
2680+
* <p/>In some FileSystem implementations such as HDFS metadata
2681+
* synchronization is essential to guarantee consistency of read requests
2682+
* particularly in HA setting.
2683+
* @throws IOException
2684+
* @throws UnsupportedOperationException
2685+
*/
2686+
public void msync() throws IOException, UnsupportedOperationException {
2687+
throw new UnsupportedOperationException(getClass().getCanonicalName() +
2688+
" does not support method msync");
2689+
}
2690+
26782691
/**
26792692
* Checks if the user can access a path. The mode specifies which access
26802693
* checks to perform. If the requested permissions are granted, then the

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FilterFileSystem.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,11 @@ public FileStatus getFileStatus(Path f) throws IOException {
462462
return fs.getFileStatus(f);
463463
}
464464

465+
@Override
466+
public void msync() throws IOException, UnsupportedOperationException {
467+
fs.msync();
468+
}
469+
465470
@Override
466471
public void access(Path path, FsAction mode) throws AccessControlException,
467472
FileNotFoundException, IOException {

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/FilterFs.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,11 @@ public FileStatus getFileStatus(Path f)
124124
return myFs.getFileStatus(f);
125125
}
126126

127+
@Override
128+
public void msync() throws IOException, UnsupportedOperationException {
129+
myFs.msync();
130+
}
131+
127132
@Override
128133
public void access(Path path, FsAction mode) throws AccessControlException,
129134
FileNotFoundException, UnresolvedLinkException, IOException {

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/fs/HarFileSystem.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -676,6 +676,11 @@ private HarStatus getFileHarStatus(Path f) throws IOException {
676676
return hstatus;
677677
}
678678

679+
@Override
680+
public void msync() throws IOException, UnsupportedOperationException {
681+
fs.msync();
682+
}
683+
679684
/**
680685
* @return null since no checksum algorithm is implemented.
681686
*/

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/fs/Hdfs.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,18 @@ public FileStatus getFileStatus(Path f)
153153
throw new FileNotFoundException("File does not exist: " + f.toString());
154154
}
155155
}
156-
156+
157+
/**
158+
* Synchronize client metadata state with Active NameNode.
159+
* <p/>In HA the client synchronizes its state with the Active NameNode
160+
* in order to guarantee subsequent read consistency from Observer Nodes.
161+
* @throws IOException
162+
*/
163+
@Override
164+
public void msync() throws IOException {
165+
dfs.msync();
166+
}
167+
157168
@Override
158169
public FileStatus getFileLinkStatus(Path f)
159170
throws IOException, UnresolvedLinkException {

hadoop-hdfs-project/hadoop-hdfs-client/src/main/java/org/apache/hadoop/hdfs/DistributedFileSystem.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1767,6 +1767,17 @@ public FileStatus next(final FileSystem fs, final Path p)
17671767
}.resolve(this, absF);
17681768
}
17691769

1770+
/**
1771+
* Synchronize client metadata state with Active NameNode.
1772+
* <p/>In HA the client synchronizes its state with the Active NameNode
1773+
* in order to guarantee subsequent read consistency from Observer Nodes.
1774+
* @throws IOException
1775+
*/
1776+
@Override
1777+
public void msync() throws IOException {
1778+
dfs.msync();
1779+
}
1780+
17701781
@SuppressWarnings("deprecation")
17711782
@Override
17721783
public void createSymlink(final Path target, final Path link,

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/MiniDFSCluster.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2679,7 +2679,8 @@ public void transitionToObserver(int nnIndex) throws IOException,
26792679
public void rollEditLogAndTail(int nnIndex) throws Exception {
26802680
getNameNode(nnIndex).getRpcServer().rollEditLog();
26812681
for (int i = 2; i < getNumNameNodes(); i++) {
2682-
getNameNode(i).getNamesystem().getEditLogTailer().doTailEdits();
2682+
long el = getNameNode(i).getNamesystem().getEditLogTailer().doTailEdits();
2683+
LOG.info("editsLoaded {}", el);
26832684
}
26842685
}
26852686

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/ha/TestConsistentReadsObserver.java

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import static org.junit.Assert.assertTrue;
2323
import static org.junit.Assert.fail;
2424

25+
import java.io.FileNotFoundException;
2526
import java.io.IOException;
2627
import java.util.Collections;
2728
import java.util.concurrent.TimeUnit;
@@ -30,9 +31,12 @@
3031

3132
import org.apache.hadoop.conf.Configuration;
3233
import org.apache.hadoop.fs.CommonConfigurationKeys;
34+
import org.apache.hadoop.fs.FileContext;
3335
import org.apache.hadoop.fs.FileSystem;
3436
import org.apache.hadoop.fs.Path;
3537
import org.apache.hadoop.fs.permission.FsPermission;
38+
import org.apache.hadoop.ha.HAServiceProtocol.HAServiceState;
39+
import org.apache.hadoop.ha.HAServiceStatus;
3640
import org.apache.hadoop.hdfs.DistributedFileSystem;
3741
import org.apache.hadoop.hdfs.MiniDFSCluster;
3842
import org.apache.hadoop.hdfs.client.HdfsClientConfigKeys;
@@ -108,7 +112,8 @@ public void testRequeueCall() throws Exception {
108112
final int observerIdx = 2;
109113
NameNode nn = dfsCluster.getNameNode(observerIdx);
110114
int port = nn.getNameNodeAddress().getPort();
111-
Configuration configuration = dfsCluster.getConfiguration(observerIdx);
115+
Configuration originalConf = dfsCluster.getConfiguration(observerIdx);
116+
Configuration configuration = new Configuration(originalConf);
112117
String prefix = CommonConfigurationKeys.IPC_NAMESPACE + "." + port + ".";
113118
configuration.set(prefix + CommonConfigurationKeys.IPC_SCHEDULER_IMPL_KEY,
114119
TestRpcScheduler.class.getName());
@@ -125,6 +130,8 @@ public void testRequeueCall() throws Exception {
125130
// be triggered and client should retry active NN.
126131
dfs.getFileStatus(testPath);
127132
assertSentTo(0);
133+
// reset the original call queue
134+
NameNodeAdapter.getRpcServer(nn).refreshCallQueue(originalConf);
128135
}
129136

130137
@Test
@@ -194,7 +201,7 @@ private void testMsync(boolean autoMsync, long autoMsyncPeriodMs)
194201
// Therefore, the subsequent getFileStatus call should succeed.
195202
if (!autoMsync) {
196203
// If not testing auto-msync, perform an explicit one here
197-
dfs2.getClient().msync();
204+
dfs2.msync();
198205
} else if (autoMsyncPeriodMs > 0) {
199206
Thread.sleep(autoMsyncPeriodMs);
200207
}
@@ -383,6 +390,35 @@ public void testRequestFromNonObserverProxyProvider() throws Exception {
383390
}
384391
}
385392

393+
@Test(timeout=10000)
394+
public void testMsyncFileContext() throws Exception {
395+
NameNode nn0 = dfsCluster.getNameNode(0);
396+
NameNode nn2 = dfsCluster.getNameNode(2);
397+
HAServiceStatus st = nn0.getRpcServer().getServiceStatus();
398+
assertEquals("nn0 is not active", HAServiceState.ACTIVE, st.getState());
399+
st = nn2.getRpcServer().getServiceStatus();
400+
assertEquals("nn2 is not observer", HAServiceState.OBSERVER, st.getState());
401+
402+
FileContext fc = FileContext.getFileContext(conf);
403+
// initialize observer proxy for FileContext
404+
fc.getFsStatus(testPath);
405+
406+
Path p = new Path(testPath, "testMsyncFileContext");
407+
fc.mkdir(p, FsPermission.getDefault(), true);
408+
fc.msync();
409+
dfsCluster.rollEditLogAndTail(0);
410+
LOG.info("State id active = {}, Stat id observer = {}",
411+
nn0.getNamesystem().getFSImage().getLastAppliedOrWrittenTxId(),
412+
nn2.getNamesystem().getFSImage().getLastAppliedOrWrittenTxId());
413+
try {
414+
// if getFileStatus is taking too long due to server requeueing
415+
// the test will time out
416+
fc.getFileStatus(p);
417+
} catch (FileNotFoundException e) {
418+
fail("File should exist on Observer after msync");
419+
}
420+
}
421+
386422
private void assertSentTo(int nnIdx) throws IOException {
387423
assertTrue("Request was not sent to the expected namenode " + nnIdx,
388424
HATestUtil.isSentToAnyOfNameNodes(dfs, dfsCluster, nnIdx));

0 commit comments

Comments
 (0)