001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase.fs;
019
020import java.lang.reflect.Field;
021import org.apache.hadoop.conf.Configuration;
022import org.apache.hadoop.fs.FSDataOutputStream;
023import org.apache.hadoop.fs.FileSystem;
024import org.apache.hadoop.fs.Path;
025import org.apache.hadoop.hbase.HBaseClassTestRule;
026import org.apache.hadoop.hbase.HBaseTestingUtil;
027import org.apache.hadoop.hbase.HConstants;
028import org.apache.hadoop.hbase.SingleProcessHBaseCluster;
029import org.apache.hadoop.hbase.testclassification.LargeTests;
030import org.apache.hadoop.hbase.testclassification.MiscTests;
031import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
032import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
033import org.apache.hadoop.hdfs.DFSClient;
034import org.apache.hadoop.hdfs.DistributedFileSystem;
035import org.apache.hadoop.hdfs.MiniDFSCluster;
036import org.apache.hadoop.hdfs.protocol.ClientProtocol;
037import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
038import org.junit.After;
039import org.junit.Assert;
040import org.junit.Before;
041import org.junit.ClassRule;
042import org.junit.Rule;
043import org.junit.Test;
044import org.junit.experimental.categories.Category;
045import org.junit.rules.TestName;
046
047/**
048 * Tests for the hdfs fix from HBASE-6435. Please don't add new subtest which involves starting /
049 * stopping MiniDFSCluster in this class. When stopping MiniDFSCluster, shutdown hooks would be
050 * cleared in hadoop's ShutdownHookManager in hadoop 3. This leads to 'Failed suppression of fs
051 * shutdown hook' error in region server.
052 */
053@Category({ MiscTests.class, LargeTests.class })
054public class TestBlockReorderBlockLocation {
055
056  @ClassRule
057  public static final HBaseClassTestRule CLASS_RULE =
058    HBaseClassTestRule.forClass(TestBlockReorderBlockLocation.class);
059
060  private Configuration conf;
061  private MiniDFSCluster cluster;
062  private HBaseTestingUtil htu;
063  private DistributedFileSystem dfs;
064  private static final String host1 = "host1";
065  private static final String host2 = "host2";
066  private static final String host3 = "host3";
067
068  @Rule
069  public TestName name = new TestName();
070
071  @Before
072  public void setUp() throws Exception {
073    htu = new HBaseTestingUtil();
074    htu.getConfiguration().setInt("dfs.blocksize", 1024);// For the test with multiple blocks
075    htu.getConfiguration().setInt("dfs.replication", 3);
076    htu.startMiniDFSCluster(3, new String[] { "/r1", "/r2", "/r3" },
077      new String[] { host1, host2, host3 });
078
079    conf = htu.getConfiguration();
080    cluster = htu.getDFSCluster();
081    dfs = (DistributedFileSystem) FileSystem.get(conf);
082  }
083
084  @After
085  public void tearDownAfterClass() throws Exception {
086    htu.shutdownMiniCluster();
087  }
088
089  private static ClientProtocol getNamenode(DFSClient dfsc) throws Exception {
090    Field nf = DFSClient.class.getDeclaredField("namenode");
091    nf.setAccessible(true);
092    return (ClientProtocol) nf.get(dfsc);
093  }
094
095  /**
096   * Test that the reorder algo works as we expect.
097   */
098  @Test
099  public void testBlockLocation() throws Exception {
100    // We need to start HBase to get HConstants.HBASE_DIR set in conf
101    htu.startMiniZKCluster();
102    SingleProcessHBaseCluster hbm = htu.startMiniHBaseCluster();
103    conf = hbm.getConfiguration();
104
105    // The "/" is mandatory, without it we've got a null pointer exception on the namenode
106    final String fileName = "/helloWorld";
107    Path p = new Path(fileName);
108
109    final int repCount = 3;
110    Assert.assertTrue((short) cluster.getDataNodes().size() >= repCount);
111
112    // Let's write the file
113    FSDataOutputStream fop = dfs.create(p, (short) repCount);
114    final double toWrite = 875.5613;
115    fop.writeDouble(toWrite);
116    fop.close();
117
118    for (int i = 0; i < 10; i++) {
119      // The interceptor is not set in this test, so we get the raw list at this point
120      LocatedBlocks l;
121      final long max = EnvironmentEdgeManager.currentTime() + 10000;
122      do {
123        l = getNamenode(dfs.getClient()).getBlockLocations(fileName, 0, 1);
124        Assert.assertNotNull(l.getLocatedBlocks());
125        Assert.assertEquals(1, l.getLocatedBlocks().size());
126        Assert.assertTrue("Expecting " + repCount + " , got " + l.get(0).getLocations().length,
127          EnvironmentEdgeManager.currentTime() < max);
128      } while (l.get(0).getLocations().length != repCount);
129
130      // Should be filtered, the name is different => The order won't change
131      Object originalList[] = l.getLocatedBlocks().toArray();
132      HFileSystem.ReorderWALBlocks lrb = new HFileSystem.ReorderWALBlocks();
133      lrb.reorderBlocks(conf, l, fileName);
134      Assert.assertArrayEquals(originalList, l.getLocatedBlocks().toArray());
135
136      // Should be reordered, as we pretend to be a file name with a compliant stuff
137      Assert.assertNotNull(conf.get(HConstants.HBASE_DIR));
138      Assert.assertFalse(conf.get(HConstants.HBASE_DIR).isEmpty());
139      String pseudoLogFile = conf.get(HConstants.HBASE_DIR) + "/" + HConstants.HREGION_LOGDIR_NAME
140        + "/" + host1 + ",6977,6576" + "/mylogfile";
141
142      // Check that it will be possible to extract a ServerName from our construction
143      Assert.assertNotNull("log= " + pseudoLogFile,
144        AbstractFSWALProvider.getServerNameFromWALDirectoryName(dfs.getConf(), pseudoLogFile));
145
146      // And check we're doing the right reorder.
147      lrb.reorderBlocks(conf, l, pseudoLogFile);
148      Assert.assertEquals(host1, l.get(0).getLocations()[2].getHostName());
149
150      // Check again, it should remain the same.
151      lrb.reorderBlocks(conf, l, pseudoLogFile);
152      Assert.assertEquals(host1, l.get(0).getLocations()[2].getHostName());
153    }
154  }
155
156}