21
21
import static org .apache .hadoop .hdfs .DFSConfigKeys .DFS_NAMENODE_STATE_CONTEXT_ENABLED_KEY ;
22
22
import static org .apache .hadoop .hdfs .server .namenode .NameNodeAdapter .getServiceState ;
23
23
import static org .apache .hadoop .hdfs .server .namenode .ha .ObserverReadProxyProvider .*;
24
- import static org .junit .Assert .assertEquals ;
25
- import static org .junit .Assert .assertNotNull ;
26
- import static org .junit .Assert .assertTrue ;
27
- import static org .junit .Assert .fail ;
24
+ import static org .junit .jupiter . api . Assertions .assertEquals ;
25
+ import static org .junit .jupiter . api . Assertions .assertNotNull ;
26
+ import static org .junit .jupiter . api . Assertions .assertTrue ;
27
+ import static org .junit .jupiter . api . Assertions .fail ;
28
28
import static org .mockito .ArgumentMatchers .any ;
29
29
import static org .mockito .ArgumentMatchers .anyBoolean ;
30
30
import static org .mockito .ArgumentMatchers .anyLong ;
58
58
import org .apache .hadoop .hdfs .MiniDFSCluster ;
59
59
import org .apache .hadoop .hdfs .protocol .Block ;
60
60
import org .apache .hadoop .hdfs .protocol .DatanodeInfo ;
61
+ import org .apache .hadoop .hdfs .protocol .ErasureCodingPolicy ;
61
62
import org .apache .hadoop .hdfs .protocol .ExtendedBlock ;
62
63
import org .apache .hadoop .hdfs .protocol .LocatedBlock ;
63
64
import org .apache .hadoop .hdfs .protocol .LocatedBlocks ;
69
70
import org .apache .hadoop .hdfs .server .namenode .NameNodeRpcServer ;
70
71
import org .apache .hadoop .hdfs .server .namenode .TestFsck ;
71
72
import org .apache .hadoop .hdfs .tools .GetGroups ;
73
+ import org .apache .hadoop .io .erasurecode .ECSchema ;
72
74
import org .apache .hadoop .ipc .ObserverRetryOnActiveException ;
73
75
import org .apache .hadoop .ipc .metrics .RpcMetrics ;
74
76
import org .apache .hadoop .test .GenericTestUtils ;
75
77
import org .apache .hadoop .test .LambdaTestUtils ;
76
78
import org .apache .hadoop .util .Time ;
77
79
import org .apache .hadoop .util .concurrent .HadoopExecutors ;
78
- import org .junit .After ;
79
- import org .junit .AfterClass ;
80
- import org .junit .Before ;
81
- import org .junit .BeforeClass ;
82
- import org .junit .Test ;
80
+ import org .junit .jupiter . api . AfterEach ;
81
+ import org .junit .jupiter . api . AfterAll ;
82
+ import org .junit .jupiter . api . BeforeEach ;
83
+ import org .junit .jupiter . api . BeforeAll ;
84
+ import org .junit .jupiter . api . Test ;
83
85
import org .mockito .Mockito ;
84
86
import org .slf4j .Logger ;
85
87
import org .slf4j .LoggerFactory ;
@@ -98,7 +100,7 @@ public class TestObserverNode {
98
100
99
101
private final Path testPath = new Path ("/TestObserverNode" );
100
102
101
- @ BeforeClass
103
+ @ BeforeAll
102
104
public static void startUpCluster () throws Exception {
103
105
conf = new Configuration ();
104
106
conf .setBoolean (DFS_NAMENODE_STATE_CONTEXT_ENABLED_KEY , true );
@@ -110,23 +112,23 @@ public static void startUpCluster() throws Exception {
110
112
dfsCluster = qjmhaCluster .getDfsCluster ();
111
113
}
112
114
113
- @ Before
115
+ @ BeforeEach
114
116
public void setUp () throws Exception {
115
117
setObserverRead (true );
116
118
}
117
119
118
- @ After
120
+ @ AfterEach
119
121
public void cleanUp () throws IOException {
120
122
dfs .delete (testPath , true );
121
- assertEquals ("NN[0] should be active" , HAServiceState .ACTIVE ,
122
- getServiceState ( dfsCluster . getNameNode ( 0 )) );
123
- assertEquals ("NN[1] should be standby" , HAServiceState .STANDBY ,
124
- getServiceState ( dfsCluster . getNameNode ( 1 )) );
125
- assertEquals ("NN[2] should be observer" , HAServiceState .OBSERVER ,
126
- getServiceState ( dfsCluster . getNameNode ( 2 )) );
123
+ assertEquals (HAServiceState .ACTIVE , getServiceState ( dfsCluster . getNameNode ( 0 )) ,
124
+ "NN[0] should be active" );
125
+ assertEquals (HAServiceState .STANDBY , getServiceState ( dfsCluster . getNameNode ( 1 )) ,
126
+ "NN[1] should be standby" );
127
+ assertEquals (HAServiceState .OBSERVER , getServiceState ( dfsCluster . getNameNode ( 2 )) ,
128
+ "NN[2] should be observer" );
127
129
}
128
130
129
- @ AfterClass
131
+ @ AfterAll
130
132
public static void shutDownCluster () throws IOException {
131
133
if (qjmhaCluster != null ) {
132
134
qjmhaCluster .shutdown ();
@@ -228,8 +230,8 @@ public void testConfigStartup() throws Exception {
228
230
}
229
231
230
232
// Confirm that the namenode at nnIdx is standby
231
- assertTrue ("The NameNode is observer despite being transitioned to standby" ,
232
- dfsCluster . getNameNode ( nnIdx ). isStandbyState () );
233
+ assertTrue (dfsCluster . getNameNode ( nnIdx ). isStandbyState () ,
234
+ "The NameNode is observer despite being transitioned to standby" );
233
235
234
236
// Restart the NameNode with observer startup option as false
235
237
dfsCluster .getConfiguration (nnIdx )
@@ -238,9 +240,9 @@ public void testConfigStartup() throws Exception {
238
240
239
241
// Verify that the NameNode is not in Observer state
240
242
dfsCluster .waitNameNodeUp (nnIdx );
241
- assertTrue ("The NameNode started as Observer despite "
242
- + DFS_NAMENODE_OBSERVER_ENABLED_KEY + " being false" ,
243
- dfsCluster . getNameNode ( nnIdx ). isStandbyState () );
243
+ assertTrue (dfsCluster . getNameNode ( nnIdx ). isStandbyState (),
244
+ "The NameNode started as Observer despite "
245
+ + DFS_NAMENODE_OBSERVER_ENABLED_KEY + " being false" );
244
246
245
247
dfs .mkdir (testPath , FsPermission .getDefault ());
246
248
assertSentTo (0 );
@@ -260,9 +262,9 @@ public void testConfigStartup() throws Exception {
260
262
261
263
// Check that the NameNode is in Observer state
262
264
dfsCluster .waitNameNodeUp (nnIdx );
263
- assertTrue ("The NameNode did not start as Observer despite "
264
- + DFS_NAMENODE_OBSERVER_ENABLED_KEY + " being true" ,
265
- dfsCluster . getNameNode ( nnIdx ). isObserverState () );
265
+ assertTrue (dfsCluster . getNameNode ( nnIdx ). isObserverState (),
266
+ "The NameNode did not start as Observer despite "
267
+ + DFS_NAMENODE_OBSERVER_ENABLED_KEY + " being true" );
266
268
267
269
dfs .mkdir (testPath2 , FsPermission .getDefault ());
268
270
assertSentTo (0 );
@@ -437,6 +439,43 @@ public void testObserverNodeSafeModeWithBlockLocations() throws Exception {
437
439
dfs .open (testPath ).close ();
438
440
assertSentTo (0 );
439
441
442
+ // Test erasure coded files
443
+ ErasureCodingPolicy ecPolicy = new ErasureCodingPolicy (new ECSchema ("rs" , 3 , 2 ), 1024 );
444
+
445
+ // Fake a small file that only needs 1 block
446
+ doAnswer ((invocation ) -> {
447
+ List <LocatedBlock > fakeBlocks = new ArrayList <>();
448
+ // Return a single location, which is enough for the small file but not for the large file
449
+ ExtendedBlock b = new ExtendedBlock ("fake-pool" , new Block (12345L , 1 , 0 ));
450
+ DatanodeInfo datanodeInfo = new DatanodeInfo .DatanodeInfoBuilder ().build ();
451
+ LocatedBlock fakeBlock = new LocatedBlock (b , new DatanodeInfo [] {datanodeInfo });
452
+ fakeBlocks .add (fakeBlock );
453
+ return new LocatedBlocks (1 , false , fakeBlocks , null , true , null , ecPolicy );
454
+ }).when (bmSpy ).createLocatedBlocks (Mockito .any (), anyLong (),
455
+ anyBoolean (), anyLong (), anyLong (), anyBoolean (), anyBoolean (),
456
+ Mockito .any (), Mockito .any ());
457
+
458
+ // Small file should suceed with just the one block
459
+ dfs .open (testPath ).close ();
460
+ assertSentTo (2 );
461
+
462
+ // Fake a larger file that needs all 3 data shards
463
+ doAnswer ((invocation ) -> {
464
+ List <LocatedBlock > fakeBlocks = new ArrayList <>();
465
+ // Return a single location, which is enough for the small file but not for the large file
466
+ ExtendedBlock b = new ExtendedBlock ("fake-pool" , new Block (12345L , 1024 * 3 , 0 ));
467
+ DatanodeInfo datanodeInfo = new DatanodeInfo .DatanodeInfoBuilder ().build ();
468
+ LocatedBlock fakeBlock = new LocatedBlock (b , new DatanodeInfo [] {datanodeInfo });
469
+ fakeBlocks .add (fakeBlock );
470
+ return new LocatedBlocks (1024 * 3 , false , fakeBlocks , null , true , null , ecPolicy );
471
+ }).when (bmSpy ).createLocatedBlocks (Mockito .any (), anyLong (),
472
+ anyBoolean (), anyLong (), anyLong (), anyBoolean (), anyBoolean (),
473
+ Mockito .any (), Mockito .any ());
474
+
475
+ // Large file should failover to the active
476
+ dfs .open (testPath ).close ();
477
+ assertSentTo (0 );
478
+
440
479
Mockito .reset (bmSpy );
441
480
442
481
// Remove safe mode on observer, request should still go to it.
@@ -471,7 +510,62 @@ public void testObserverNodeBlockMissingRetry() throws Exception {
471
510
anyBoolean (), anyLong (), anyLong (), anyBoolean (), anyBoolean (),
472
511
Mockito .any (), Mockito .any ());
473
512
474
- dfs .open (testPath );
513
+ dfs .open (testPath ).close ();
514
+ assertSentTo (0 );
515
+
516
+ dfs .getClient ().listPaths ("/" , new byte [0 ], true );
517
+ assertSentTo (0 );
518
+
519
+ dfs .getClient ().getLocatedFileInfo (testPath .toString (), false );
520
+ assertSentTo (0 );
521
+
522
+ dfs .getClient ().batchedListPaths (new String []{"/" }, new byte [0 ], true );
523
+ assertSentTo (0 );
524
+
525
+ // Test erasure coded files
526
+ ErasureCodingPolicy ecPolicy = new ErasureCodingPolicy (new ECSchema ("rs" , 3 , 2 ), 1024 );
527
+
528
+ // Fake a small file that only needs 1 block
529
+ doAnswer ((invocation ) -> {
530
+ List <LocatedBlock > fakeBlocks = new ArrayList <>();
531
+ // Return a single location, which is enough for the small file but not for the large file
532
+ ExtendedBlock b = new ExtendedBlock ("fake-pool" , new Block (12345L , 1 , 0 ));
533
+ DatanodeInfo datanodeInfo = new DatanodeInfo .DatanodeInfoBuilder ().build ();
534
+ LocatedBlock fakeBlock = new LocatedBlock (b , new DatanodeInfo [] {datanodeInfo });
535
+ fakeBlocks .add (fakeBlock );
536
+ return new LocatedBlocks (1 , false , fakeBlocks , null , true , null , ecPolicy );
537
+ }).when (bmSpy ).createLocatedBlocks (Mockito .any (), anyLong (),
538
+ anyBoolean (), anyLong (), anyLong (), anyBoolean (), anyBoolean (),
539
+ Mockito .any (), Mockito .any ());
540
+
541
+ // The small file should succeed on the observer, while the large file should not
542
+
543
+ dfs .open (testPath ).close ();
544
+ assertSentTo (2 );
545
+
546
+ dfs .getClient ().listPaths ("/" , new byte [0 ], true );
547
+ assertSentTo (2 );
548
+
549
+ dfs .getClient ().getLocatedFileInfo (testPath .toString (), false );
550
+ assertSentTo (2 );
551
+
552
+ dfs .getClient ().batchedListPaths (new String []{"/" }, new byte [0 ], true );
553
+ assertSentTo (2 );
554
+
555
+ // Fake a larger file that needs all 3 data shards
556
+ doAnswer ((invocation ) -> {
557
+ List <LocatedBlock > fakeBlocks = new ArrayList <>();
558
+ // Return a single location, which is enough for the small file but not for the large file
559
+ ExtendedBlock b = new ExtendedBlock ("fake-pool" , new Block (12345L , 1024 * 3 , 0 ));
560
+ DatanodeInfo datanodeInfo = new DatanodeInfo .DatanodeInfoBuilder ().build ();
561
+ LocatedBlock fakeBlock = new LocatedBlock (b , new DatanodeInfo [] {datanodeInfo });
562
+ fakeBlocks .add (fakeBlock );
563
+ return new LocatedBlocks (1024 * 3 , false , fakeBlocks , null , true , null , ecPolicy );
564
+ }).when (bmSpy ).createLocatedBlocks (Mockito .any (), anyLong (),
565
+ anyBoolean (), anyLong (), anyLong (), anyBoolean (), anyBoolean (),
566
+ Mockito .any (), Mockito .any ());
567
+
568
+ dfs .open (testPath ).close ();
475
569
assertSentTo (0 );
476
570
477
571
dfs .getClient ().listPaths ("/" , new byte [0 ], true );
@@ -501,7 +595,7 @@ public void testFsckWithObserver() throws Exception {
501
595
/**
502
596
* Test that, if a write happens happens to go to Observer,
503
597
* Observer would throw {@link ObserverRetryOnActiveException},
504
- * to inform client to retry on Active
598
+ * to inform client to retry on Active.
505
599
*
506
600
* @throws Exception
507
601
*/
@@ -563,16 +657,15 @@ public void testStickyActive() throws Exception {
563
657
dfsCluster .rollEditLogAndTail (0 );
564
658
// No Observers present, should still go to Active
565
659
dfsCluster .transitionToStandby (2 );
566
- assertEquals ("NN[2] should be standby" , HAServiceState .STANDBY ,
567
- getServiceState ( dfsCluster . getNameNode ( 2 )) );
660
+ assertEquals (HAServiceState .STANDBY , getServiceState ( dfsCluster . getNameNode ( 2 )) ,
661
+ "NN[2] should be standby" );
568
662
newFs .open (testFile ).close ();
569
663
assertSentTo (0 );
570
664
// Restore Observer
571
665
int newObserver = 1 ;
572
666
dfsCluster .transitionToObserver (newObserver );
573
- assertEquals ("NN[" + newObserver + "] should be observer" ,
574
- HAServiceState .OBSERVER ,
575
- getServiceState (dfsCluster .getNameNode (newObserver )));
667
+ assertEquals (HAServiceState .OBSERVER , getServiceState (dfsCluster .getNameNode (newObserver )),
668
+ "NN[" + newObserver + "] should be observer" );
576
669
long startTime = Time .monotonicNow ();
577
670
try {
578
671
while (Time .monotonicNow () - startTime <= 5000 ) {
@@ -661,19 +754,19 @@ public void testMkdirsRaceWithObserverRead() throws Exception {
661
754
LOG .warn ("MkDirRunner thread failed" , e .getCause ());
662
755
}
663
756
}
664
- assertTrue ("Not all threads finished" , finished );
757
+ assertTrue (finished , "Not all threads finished" );
665
758
threadPool .shutdown ();
666
759
667
- assertEquals ("Active and Observer stateIds don't match" ,
668
- dfsCluster .getNameNode (0 ).getFSImage ().getLastAppliedOrWrittenTxId (),
669
- dfsCluster . getNameNode ( 2 ). getFSImage (). getLastAppliedOrWrittenTxId () );
760
+ assertEquals (dfsCluster . getNameNode ( 0 ). getFSImage (). getLastAppliedOrWrittenTxId () ,
761
+ dfsCluster .getNameNode (2 ).getFSImage ().getLastAppliedOrWrittenTxId (),
762
+ "Active and Observer stateIds don't match" );
670
763
for (int i = 0 ; i < numThreads ; i ++) {
671
- assertTrue ("Client #" + i
764
+ assertTrue (clientStates [i ].lastSeenStateId >= activStateId &&
765
+ clientStates [i ].fnfe == null ,
766
+ "Client #" + i
672
767
+ " lastSeenStateId=" + clientStates [i ].lastSeenStateId
673
768
+ " activStateId=" + activStateId
674
- + "\n " + clientStates [i ].fnfe ,
675
- clientStates [i ].lastSeenStateId >= activStateId &&
676
- clientStates [i ].fnfe == null );
769
+ + "\n " + clientStates [i ].fnfe );
677
770
}
678
771
679
772
// Restore edit log
@@ -707,7 +800,7 @@ public void run() {
707
800
708
801
FileStatus stat = fs .getFileStatus (DIR_PATH );
709
802
assertSentTo (fs , 2 );
710
- assertTrue ("Should be a directory" , stat . isDirectory () );
803
+ assertTrue (stat . isDirectory (), "Should be a directory" );
711
804
} catch (FileNotFoundException ioe ) {
712
805
clientState .fnfe = ioe ;
713
806
} catch (Exception e ) {
@@ -752,13 +845,13 @@ public void testSimpleReadEmptyDirOrFile() throws IOException {
752
845
753
846
private static void assertSentTo (DistributedFileSystem fs , int nnIdx )
754
847
throws IOException {
755
- assertTrue ("Request was not sent to the expected namenode " + nnIdx ,
756
- HATestUtil . isSentToAnyOfNameNodes ( fs , dfsCluster , nnIdx ) );
848
+ assertTrue (HATestUtil . isSentToAnyOfNameNodes ( fs , dfsCluster , nnIdx ) ,
849
+ "Request was not sent to the expected namenode " + nnIdx );
757
850
}
758
851
759
852
private void assertSentTo (int nnIdx ) throws IOException {
760
- assertTrue ("Request was not sent to the expected namenode " + nnIdx ,
761
- HATestUtil . isSentToAnyOfNameNodes ( dfs , dfsCluster , nnIdx ) );
853
+ assertTrue (HATestUtil . isSentToAnyOfNameNodes ( dfs , dfsCluster , nnIdx ) ,
854
+ "Request was not sent to the expected namenode " + nnIdx );
762
855
}
763
856
764
857
private static void setObserverRead (boolean flag ) throws Exception {
0 commit comments