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 static org.apache.hadoop.hbase.util.LocatedBlockHelper.getLocatedBlockLocations;
021
022import java.lang.reflect.Field;
023import org.apache.hadoop.conf.Configuration;
024import org.apache.hadoop.fs.FSDataOutputStream;
025import org.apache.hadoop.fs.FileSystem;
026import org.apache.hadoop.fs.Path;
027import org.apache.hadoop.hbase.HBaseClassTestRule;
028import org.apache.hadoop.hbase.HBaseTestingUtility;
029import org.apache.hadoop.hbase.HConstants;
030import org.apache.hadoop.hbase.MiniHBaseCluster;
031import org.apache.hadoop.hbase.testclassification.LargeTests;
032import org.apache.hadoop.hbase.testclassification.MiscTests;
033import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
034import org.apache.hadoop.hbase.wal.AbstractFSWALProvider;
035import org.apache.hadoop.hdfs.DFSClient;
036import org.apache.hadoop.hdfs.DistributedFileSystem;
037import org.apache.hadoop.hdfs.MiniDFSCluster;
038import org.apache.hadoop.hdfs.protocol.ClientProtocol;
039import org.apache.hadoop.hdfs.protocol.LocatedBlocks;
040import org.junit.After;
041import org.junit.Assert;
042import org.junit.Before;
043import org.junit.ClassRule;
044import org.junit.Rule;
045import org.junit.Test;
046import org.junit.experimental.categories.Category;
047import org.junit.rules.TestName;
048
049/**
050 * Tests for the hdfs fix from HBASE-6435. Please don't add new subtest which involves starting /
051 * stopping MiniDFSCluster in this class. When stopping MiniDFSCluster, shutdown hooks would be
052 * cleared in hadoop's ShutdownHookManager in hadoop 3. This leads to 'Failed suppression of fs
053 * shutdown hook' error in region server.
054 */
055@Category({ MiscTests.class, LargeTests.class })
056public class TestBlockReorderBlockLocation {
057
058  @ClassRule
059  public static final HBaseClassTestRule CLASS_RULE =
060    HBaseClassTestRule.forClass(TestBlockReorderBlockLocation.class);
061
062  private Configuration conf;
063  private MiniDFSCluster cluster;
064  private HBaseTestingUtility htu;
065  private DistributedFileSystem dfs;
066  private static final String host1 = "host1";
067  private static final String host2 = "host2";
068  private static final String host3 = "host3";
069
070  @Rule
071  public TestName name = new TestName();
072
073  @Before
074  public void setUp() throws Exception {
075    htu = new HBaseTestingUtility();
076    htu.getConfiguration().setInt("dfs.blocksize", 1024);// For the test with multiple blocks
077    htu.getConfiguration().setInt("dfs.replication", 3);
078    htu.startMiniDFSCluster(3, new String[] { "/r1", "/r2", "/r3" },
079      new String[] { host1, host2, host3 });
080
081    conf = htu.getConfiguration();
082    cluster = htu.getDFSCluster();
083    dfs = (DistributedFileSystem) FileSystem.get(conf);
084  }
085
086  @After
087  public void tearDownAfterClass() throws Exception {
088    htu.shutdownMiniCluster();
089  }
090
091  private static ClientProtocol getNamenode(DFSClient dfsc) throws Exception {
092    Field nf = DFSClient.class.getDeclaredField("namenode");
093    nf.setAccessible(true);
094    return (ClientProtocol) nf.get(dfsc);
095  }
096
097  /**
098   * Test that the reorder algo works as we expect.
099   */
100  @Test
101  public void testBlockLocation() throws Exception {
102    // We need to start HBase to get HConstants.HBASE_DIR set in conf
103    htu.startMiniZKCluster();
104    MiniHBaseCluster hbm = htu.startMiniHBaseCluster();
105    conf = hbm.getConfiguration();
106
107    // The "/" is mandatory, without it we've got a null pointer exception on the namenode
108    final String fileName = "/helloWorld";
109    Path p = new Path(fileName);
110
111    final int repCount = 3;
112    Assert.assertTrue((short) cluster.getDataNodes().size() >= repCount);
113
114    // Let's write the file
115    FSDataOutputStream fop = dfs.create(p, (short) repCount);
116    final double toWrite = 875.5613;
117    fop.writeDouble(toWrite);
118    fop.close();
119
120    for (int i = 0; i < 10; i++) {
121      // The interceptor is not set in this test, so we get the raw list at this point
122      LocatedBlocks lbs;
123      final long max = EnvironmentEdgeManager.currentTime() + 10000;
124      do {
125        lbs = getNamenode(dfs.getClient()).getBlockLocations(fileName, 0, 1);
126        Assert.assertNotNull(lbs.getLocatedBlocks());
127        Assert.assertEquals(1, lbs.getLocatedBlocks().size());
128        Assert.assertTrue(
129          "Expecting " + repCount + " , got " + getLocatedBlockLocations(lbs.get(0)).length,
130          EnvironmentEdgeManager.currentTime() < max);
131      } while (getLocatedBlockLocations(lbs.get(0)).length != repCount);
132
133      // Should be filtered, the name is different => The order won't change
134      Object[] originalList = lbs.getLocatedBlocks().toArray();
135      HFileSystem.ReorderWALBlocks lrb = new HFileSystem.ReorderWALBlocks();
136      lrb.reorderBlocks(conf, lbs, fileName);
137      Assert.assertArrayEquals(originalList, lbs.getLocatedBlocks().toArray());
138
139      // Should be reordered, as we pretend to be a file name with a compliant stuff
140      Assert.assertNotNull(conf.get(HConstants.HBASE_DIR));
141      Assert.assertFalse(conf.get(HConstants.HBASE_DIR).isEmpty());
142      String pseudoLogFile = conf.get(HConstants.HBASE_DIR) + "/" + HConstants.HREGION_LOGDIR_NAME
143        + "/" + host1 + ",6977,6576" + "/mylogfile";
144
145      // Check that it will be possible to extract a ServerName from our construction
146      Assert.assertNotNull("log= " + pseudoLogFile,
147        AbstractFSWALProvider.getServerNameFromWALDirectoryName(dfs.getConf(), pseudoLogFile));
148
149      // And check we're doing the right reorder.
150      lrb.reorderBlocks(conf, lbs, pseudoLogFile);
151      Assert.assertEquals(host1, getLocatedBlockLocations(lbs.get(0))[2].getHostName());
152
153      // Check again, it should remain the same.
154      lrb.reorderBlocks(conf, lbs, pseudoLogFile);
155      Assert.assertEquals(host1, getLocatedBlockLocations(lbs.get(0))[2].getHostName());
156    }
157  }
158
159}