PageRenderTime 51ms CodeModel.GetById 14ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/apache/hadoop-common
Java | 225 lines | 160 code | 20 blank | 45 comment | 9 complexity | 457a057b271c2890133e15c977bc5063 MD5 | raw file
Possible License(s): Apache-2.0, BSD-3-Clause
  1. /**
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing, software
  13. * distributed under the License is distributed on an "AS IS" BASIS,
  14. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  15. * See the License for the specific language governing permissions and
  16. * limitations under the License.
  17. */
  18. package org.apache.hadoop.hdfs.server.namenode;
  19. import java.io.IOException;
  20. import java.util.Random;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.hadoop.conf.Configuration;
  24. import org.apache.hadoop.fs.FileSystem;
  25. import org.apache.hadoop.fs.Path;
  26. import org.apache.hadoop.hdfs.DFSConfigKeys;
  27. import org.apache.hadoop.hdfs.DFSTestUtil;
  28. import org.apache.hadoop.hdfs.HdfsConfiguration;
  29. import org.apache.hadoop.hdfs.MiniDFSCluster;
  30. import org.apache.hadoop.util.Time;
  31. import org.junit.Assert;
  32. import org.junit.Test;
  33. /**
  34. * Ensure during large directory delete, namenode does not block until the
  35. * deletion completes and handles new requests from other clients
  36. */
  37. public class TestLargeDirectoryDelete {
  38. private static final Log LOG = LogFactory.getLog(TestLargeDirectoryDelete.class);
  39. private static final Configuration CONF = new HdfsConfiguration();
  40. private static final int TOTAL_BLOCKS = 10000;
  41. private MiniDFSCluster mc = null;
  42. private int createOps = 0;
  43. private int lockOps = 0;
  44. static {
  45. CONF.setLong(DFSConfigKeys.DFS_BLOCK_SIZE_KEY, 1);
  46. CONF.setInt(DFSConfigKeys.DFS_BYTES_PER_CHECKSUM_KEY, 1);
  47. }
  48. /** create a file with a length of <code>filelen</code> */
  49. private void createFile(final String fileName, final long filelen) throws IOException {
  50. FileSystem fs = mc.getFileSystem();
  51. Path filePath = new Path(fileName);
  52. DFSTestUtil.createFile(fs, filePath, filelen, (short) 1, 0);
  53. }
  54. /** Create a large number of directories and files */
  55. private void createFiles() throws IOException {
  56. Random rand = new Random();
  57. // Create files in a directory with random depth
  58. // ranging from 0-10.
  59. for (int i = 0; i < TOTAL_BLOCKS; i+=100) {
  60. String filename = "/root/";
  61. int dirs = rand.nextInt(10); // Depth of the directory
  62. for (int j=i; j >=(i-dirs); j--) {
  63. filename += j + "/";
  64. }
  65. filename += "file" + i;
  66. createFile(filename, 100);
  67. }
  68. }
  69. private int getBlockCount() {
  70. Assert.assertNotNull("Null cluster", mc);
  71. Assert.assertNotNull("No Namenode in cluster", mc.getNameNode());
  72. FSNamesystem namesystem = mc.getNamesystem();
  73. Assert.assertNotNull("Null Namesystem in cluster", namesystem);
  74. Assert.assertNotNull("Null Namesystem.blockmanager", namesystem.getBlockManager());
  75. return (int) namesystem.getBlocksTotal();
  76. }
  77. /** Run multiple threads doing simultaneous operations on the namenode
  78. * while a large directory is being deleted.
  79. */
  80. private void runThreads() throws Throwable {
  81. final TestThread threads[] = new TestThread[2];
  82. // Thread for creating files
  83. threads[0] = new TestThread() {
  84. @Override
  85. protected void execute() throws Throwable {
  86. while(live) {
  87. try {
  88. int blockcount = getBlockCount();
  89. if (blockcount < TOTAL_BLOCKS && blockcount > 0) {
  90. String file = "/tmp" + createOps;
  91. createFile(file, 1);
  92. mc.getFileSystem().delete(new Path(file), true);
  93. createOps++;
  94. }
  95. } catch (IOException ex) {
  96. LOG.info("createFile exception ", ex);
  97. break;
  98. }
  99. }
  100. }
  101. };
  102. // Thread that periodically acquires the FSNamesystem lock
  103. threads[1] = new TestThread() {
  104. @Override
  105. protected void execute() throws Throwable {
  106. while(live) {
  107. try {
  108. int blockcount = getBlockCount();
  109. if (blockcount < TOTAL_BLOCKS && blockcount > 0) {
  110. mc.getNamesystem().writeLock();
  111. try {
  112. lockOps++;
  113. } finally {
  114. mc.getNamesystem().writeUnlock();
  115. }
  116. Thread.sleep(1);
  117. }
  118. } catch (InterruptedException ex) {
  119. LOG.info("lockOperation exception ", ex);
  120. break;
  121. }
  122. }
  123. }
  124. };
  125. threads[0].start();
  126. threads[1].start();
  127. final long start = Time.now();
  128. FSNamesystem.BLOCK_DELETION_INCREMENT = 1;
  129. mc.getFileSystem().delete(new Path("/root"), true); // recursive delete
  130. final long end = Time.now();
  131. threads[0].endThread();
  132. threads[1].endThread();
  133. LOG.info("Deletion took " + (end - start) + "msecs");
  134. LOG.info("createOperations " + createOps);
  135. LOG.info("lockOperations " + lockOps);
  136. Assert.assertTrue(lockOps + createOps > 0);
  137. threads[0].rethrow();
  138. threads[1].rethrow();
  139. }
  140. /**
  141. * An abstract class for tests that catches exceptions and can
  142. * rethrow them on a different thread, and has an {@link #endThread()}
  143. * operation that flips a volatile boolean before interrupting the thread.
  144. * Also: after running the implementation of {@link #execute()} in the
  145. * implementation class, the thread is notified: other threads can wait
  146. * for it to terminate
  147. */
  148. private abstract class TestThread extends Thread {
  149. volatile Throwable thrown;
  150. protected volatile boolean live = true;
  151. @Override
  152. public void run() {
  153. try {
  154. execute();
  155. } catch (Throwable throwable) {
  156. LOG.warn(throwable);
  157. setThrown(throwable);
  158. } finally {
  159. synchronized (this) {
  160. this.notify();
  161. }
  162. }
  163. }
  164. protected abstract void execute() throws Throwable;
  165. protected synchronized void setThrown(Throwable thrown) {
  166. this.thrown = thrown;
  167. }
  168. /**
  169. * Rethrow anything caught
  170. * @throws Throwable any non-null throwable raised by the execute method.
  171. */
  172. public synchronized void rethrow() throws Throwable {
  173. if (thrown != null) {
  174. throw thrown;
  175. }
  176. }
  177. /**
  178. * End the thread by setting the live p
  179. */
  180. public synchronized void endThread() {
  181. live = false;
  182. interrupt();
  183. try {
  184. wait();
  185. } catch (InterruptedException e) {
  186. if(LOG.isDebugEnabled()) {
  187. LOG.debug("Ignoring " + e, e);
  188. }
  189. }
  190. }
  191. }
  192. @Test
  193. public void largeDelete() throws Throwable {
  194. mc = new MiniDFSCluster.Builder(CONF).build();
  195. try {
  196. mc.waitActive();
  197. Assert.assertNotNull("No Namenode in cluster", mc.getNameNode());
  198. createFiles();
  199. Assert.assertEquals(TOTAL_BLOCKS, getBlockCount());
  200. runThreads();
  201. } finally {
  202. mc.shutdown();
  203. }
  204. }
  205. }