2929import org .apache .hadoop .hdfs .protocol .HdfsConstants ;
3030import org .apache .hadoop .hdfs .server .blockmanagement .BlockStoragePolicySuite ;
3131import org .apache .hadoop .hdfs .server .namenode .snapshot .Snapshot ;
32+ import org .apache .hadoop .ipc .RemoteException ;
3233import org .apache .hadoop .test .GenericTestUtils ;
3334import org .apache .hadoop .test .PathUtils ;
3435import org .junit .jupiter .api .BeforeAll ;
3536import org .junit .jupiter .api .Test ;
37+ import org .mockito .Mockito ;
3638
3739import java .io .IOException ;
3840
3941import static org .apache .hadoop .hdfs .protocol .HdfsConstants .HOT_STORAGE_POLICY_NAME ;
4042import static org .apache .hadoop .hdfs .protocol .HdfsConstants .ONESSD_STORAGE_POLICY_NAME ;
4143import static org .junit .jupiter .api .Assertions .assertEquals ;
44+ import static org .junit .jupiter .api .Assertions .assertFalse ;
45+ import static org .junit .jupiter .api .Assertions .assertThrows ;
4246import static org .junit .jupiter .api .Assertions .assertTrue ;
47+ import static org .mockito .ArgumentMatchers .anyBoolean ;
48+ import static org .mockito .ArgumentMatchers .anyInt ;
4349
4450public class TestCorrectnessOfQuotaAfterRenameOp {
4551 private static MiniDFSCluster cluster ;
@@ -193,7 +199,7 @@ public void testRenameWithoutValidFeature() throws Exception {
193199 QuotaUsage rootQuotaUsage1 = dfs .getQuotaUsage (new Path ("/" ));
194200 ContentSummary contentSummary1 = dfs .getContentSummary (testParentDir1 );
195201 // srcDir=/testRename/testDir1/src-dir
196- // dstDir=/testRename/testDir2/dst-dir dstDir1 not exist
202+ // dstDir=/testRename/testDir2/dst-dir dstDir not exist
197203 final Path dstDir2 = new Path (testParentDir2 , "dst-dir" );
198204 assertTrue (dfs .rename (srcDir , dstDir2 ));
199205 ContentSummary contentSummary2 = dfs .getContentSummary (testParentDir2 );
@@ -208,19 +214,106 @@ public void testRenameWithoutValidFeature() throws Exception {
208214 // 2. Test rename2
209215 final Path dstDir3 = new Path (testParentDir3 , "dst-dir" );
210216 assertTrue (dfs .mkdirs (dstDir3 ));
211- long originDstDir2Usage = dfs .getQuotaUsage (dstDir3 ).getFileAndDirectoryCount ();
212- // Exclude dstDir2 usage
213- long rootINodeCount1 =
214- dfs .getQuotaUsage (new Path ("/" )).getFileAndDirectoryCount () - originDstDir2Usage ;
217+ long originDstDirUsage = dfs .getQuotaUsage (dstDir3 ).getFileAndDirectoryCount ();
218+ // Overwrite the rename destination, the usage of dstDir3 should be excluded
219+ long expectedCount =
220+ dfs .getQuotaUsage (new Path ("/" )).getFileAndDirectoryCount () - originDstDirUsage ;
215221 ContentSummary contentSummary3 = dfs .getContentSummary (testParentDir2 );
216222
217- // Src and dst must be same (all file or all dir)
218- // dstDir2=/testRename/testDir3 /dst-dir
223+ // Src and dst must be same
224+ // dstDir2=/testRename/testDir2 /dst-dir
219225 // dstDir3=/testRename/testDir3/dst-dir
220226 dfs .rename (dstDir2 , dstDir3 , Options .Rename .OVERWRITE );
221- long rootINodeCount2 = dfs .getQuotaUsage (new Path ("/" )).getFileAndDirectoryCount ();
222- assertEquals (rootINodeCount1 , rootINodeCount2 );
227+ long actualCount = dfs .getQuotaUsage (new Path ("/" )).getFileAndDirectoryCount ();
228+ assertEquals (expectedCount , actualCount );
223229 ContentSummary contentSummary4 = dfs .getContentSummary (testParentDir3 );
224230 assertEquals (contentSummary3 , contentSummary4 );
225231 }
232+
233+ @ Test
234+ public void testRenameUndoWithoutValidFeature () throws Exception {
235+ final int fileLen = 1024 ;
236+ final short replication = 3 ;
237+ final Path root = new Path ("/testRenameUndo" );
238+ assertTrue (dfs .mkdirs (root ));
239+
240+ Path testParentDir1 = new Path (root , "testDir1" );
241+ assertTrue (dfs .mkdirs (testParentDir1 ));
242+ Path testParentDir2 = new Path (root , "testDir2" );
243+ assertTrue (dfs .mkdirs (testParentDir2 ));
244+ Path testParentDir3 = new Path (root , "testDir3" );
245+ assertTrue (dfs .mkdirs (testParentDir3 ));
246+ Path testParentDir4 = new Path (root , "testDir4" );
247+ assertTrue (dfs .mkdirs (testParentDir4 ));
248+
249+ final Path srcDir1 = new Path (testParentDir1 , "src-dir" );
250+ for (int i = 0 ; i < 2 ; i ++) {
251+ Path file1 = new Path (srcDir1 , "file" + i );
252+ DFSTestUtil .createFile (dfs , file1 , fileLen , replication , 0 );
253+ }
254+
255+ final Path srcDir3 = new Path (testParentDir3 , "src-dir" );
256+ for (int i = 0 ; i < 2 ; i ++) {
257+ Path file1 = new Path (srcDir3 , "file" + i );
258+ DFSTestUtil .createFile (dfs , file1 , fileLen , replication , 0 );
259+ }
260+
261+ // Test rename1
262+ ContentSummary rootContentSummary1 = dfs .getContentSummary (new Path ("/" ));
263+ QuotaUsage rootQuotaUsage1 = dfs .getQuotaUsage (new Path ("/" ));
264+ ContentSummary contentSummary1 = dfs .getContentSummary (testParentDir1 );
265+
266+ FSNamesystem fsn = cluster .getNamesystem ();
267+ FSDirectory fsDirectory = fsn .getFSDirectory ();
268+
269+ // Replace INode, expected addChild return false
270+ INodeDirectory dir = fsDirectory .getINode4Write (testParentDir2 .toString ()).asDirectory ();
271+ INodeDirectory mockDir = Mockito .spy (dir );
272+ INode srcInode = fsDirectory .getINode (srcDir3 .toString ());
273+ // Fail the rename but succeed in undo
274+ Mockito .doReturn (false ).when (mockDir ).addChild (Mockito .eq (srcInode ), anyBoolean (), anyInt ());
275+ INodeDirectory rootDir = fsDirectory .getINode4Write (root .toString ()).asDirectory ();
276+ rootDir .replaceChild (dir , mockDir , fsDirectory .getINodeMap ());
277+ mockDir .setParent (rootDir );
278+
279+ // srcDir=/testRenameUndo/testDir1/src-dir
280+ // dstDir=/testRenameUndo/testDir2/
281+ assertFalse (dfs .rename (srcDir3 , testParentDir2 ));
282+
283+ ContentSummary rootContentSummary2 = dfs .getContentSummary (new Path ("/" ));
284+ QuotaUsage rootQuotaUsage2 = dfs .getQuotaUsage (new Path ("/" ));
285+ ContentSummary contentSummary2 = dfs .getContentSummary (testParentDir1 );
286+ assertEquals (rootContentSummary1 .toString (), rootContentSummary2 .toString ());
287+ assertEquals (rootQuotaUsage1 .toString (), rootQuotaUsage2 .toString ());
288+ assertEquals (contentSummary1 .toString (), contentSummary2 .toString ());
289+ assertEquals (rootContentSummary1 .getFileAndDirectoryCount (),
290+ rootQuotaUsage2 .getFileAndDirectoryCount ());
291+
292+ // Test rename2
293+ final Path dstDir4 = new Path (testParentDir4 , "src-dir" );
294+ assertTrue (dfs .mkdirs (dstDir4 ));
295+ ContentSummary rootContentSummary3 = dfs .getContentSummary (new Path ("/" ));
296+ QuotaUsage rootQuotaUsage3 = dfs .getQuotaUsage (new Path ("/" ));
297+ ContentSummary contentSummary3 = dfs .getContentSummary (testParentDir3 );
298+
299+ // Replace INode, expected addChild return false
300+ INodeDirectory dir4 = fsDirectory .getINode4Write (testParentDir4 .toString ()).asDirectory ();
301+ INodeDirectory mockDir4 = Mockito .spy (dir4 );
302+ INode srcInode3 = fsDirectory .getINode (srcDir3 .toString ());
303+ Mockito .doReturn (false ).when (mockDir4 ).addChild (Mockito .eq (srcInode3 ), anyBoolean (), anyInt ());
304+ rootDir .replaceChild (dir4 , mockDir4 , fsDirectory .getINodeMap ());
305+ mockDir4 .setParent (rootDir );
306+ // srcDir=/testRenameUndo/testDir3/src-dir
307+ // dstDir=/testRenameUndo/testDir4/src-dir dstDir exist
308+ assertThrows (RemoteException .class ,
309+ () -> dfs .rename (srcDir3 , dstDir4 , Options .Rename .OVERWRITE ));
310+ ContentSummary rootContentSummary4 = dfs .getContentSummary (new Path ("/" ));
311+ QuotaUsage rootQuotaUsage4 = dfs .getQuotaUsage (new Path ("/" ));
312+ ContentSummary contentSummary4 = dfs .getContentSummary (testParentDir3 );
313+ assertEquals (rootContentSummary3 .toString (), rootContentSummary4 .toString ());
314+ assertEquals (rootQuotaUsage3 .toString (), rootQuotaUsage4 .toString ());
315+ assertEquals (contentSummary3 .toString (), contentSummary4 .toString ());
316+ assertEquals (rootContentSummary3 .getFileAndDirectoryCount (),
317+ rootQuotaUsage4 .getFileAndDirectoryCount ());
318+ }
226319}
0 commit comments