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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 package org
.apache
.hadoop
.hbase
.master
;
20 import static org
.junit
.Assert
.assertArrayEquals
;
21 import static org
.junit
.Assert
.assertEquals
;
22 import static org
.junit
.Assert
.assertFalse
;
23 import static org
.junit
.Assert
.assertTrue
;
24 import static org
.junit
.Assert
.fail
;
26 import java
.io
.IOException
;
27 import java
.util
.List
;
29 import java
.util
.concurrent
.atomic
.AtomicReference
;
30 import org
.apache
.hadoop
.conf
.Configuration
;
31 import org
.apache
.hadoop
.fs
.FileSystem
;
32 import org
.apache
.hadoop
.fs
.Path
;
33 import org
.apache
.hadoop
.hbase
.CatalogFamilyFormat
;
34 import org
.apache
.hadoop
.hbase
.ClientMetaTableAccessor
;
35 import org
.apache
.hadoop
.hbase
.HBaseClassTestRule
;
36 import org
.apache
.hadoop
.hbase
.HBaseTestingUtil
;
37 import org
.apache
.hadoop
.hbase
.HConstants
;
38 import org
.apache
.hadoop
.hbase
.MetaTableAccessor
;
39 import org
.apache
.hadoop
.hbase
.PleaseHoldException
;
40 import org
.apache
.hadoop
.hbase
.ServerName
;
41 import org
.apache
.hadoop
.hbase
.SingleProcessHBaseCluster
;
42 import org
.apache
.hadoop
.hbase
.TableName
;
43 import org
.apache
.hadoop
.hbase
.UnknownRegionException
;
44 import org
.apache
.hadoop
.hbase
.client
.Admin
;
45 import org
.apache
.hadoop
.hbase
.client
.ColumnFamilyDescriptor
;
46 import org
.apache
.hadoop
.hbase
.client
.ColumnFamilyDescriptorBuilder
;
47 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
48 import org
.apache
.hadoop
.hbase
.client
.RegionInfoBuilder
;
49 import org
.apache
.hadoop
.hbase
.client
.Result
;
50 import org
.apache
.hadoop
.hbase
.client
.Table
;
51 import org
.apache
.hadoop
.hbase
.client
.TableDescriptor
;
52 import org
.apache
.hadoop
.hbase
.client
.TableDescriptorBuilder
;
53 import org
.apache
.hadoop
.hbase
.client
.TableState
;
54 import org
.apache
.hadoop
.hbase
.testclassification
.MasterTests
;
55 import org
.apache
.hadoop
.hbase
.testclassification
.MediumTests
;
56 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
57 import org
.apache
.hadoop
.hbase
.util
.HBaseFsck
;
58 import org
.apache
.hadoop
.hbase
.util
.Pair
;
59 import org
.apache
.hadoop
.hbase
.util
.Threads
;
60 import org
.apache
.hadoop
.util
.StringUtils
;
61 import org
.junit
.AfterClass
;
62 import org
.junit
.BeforeClass
;
63 import org
.junit
.ClassRule
;
64 import org
.junit
.Rule
;
65 import org
.junit
.Test
;
66 import org
.junit
.experimental
.categories
.Category
;
67 import org
.junit
.rules
.TestName
;
68 import org
.slf4j
.Logger
;
69 import org
.slf4j
.LoggerFactory
;
71 import org
.apache
.hbase
.thirdparty
.com
.google
.common
.base
.Joiner
;
73 @Category({MasterTests
.class, MediumTests
.class})
74 public class TestMaster
{
77 public static final HBaseClassTestRule CLASS_RULE
=
78 HBaseClassTestRule
.forClass(TestMaster
.class);
80 private static final HBaseTestingUtil TEST_UTIL
= new HBaseTestingUtil();
81 private static final Logger LOG
= LoggerFactory
.getLogger(TestMaster
.class);
82 private static final TableName TABLENAME
= TableName
.valueOf("TestMaster");
83 private static final byte[] FAMILYNAME
= Bytes
.toBytes("fam");
84 private static Admin admin
;
87 public TestName name
= new TestName();
90 public static void beforeAllTests() throws Exception
{
91 // we will retry operations when PleaseHoldException is thrown
92 TEST_UTIL
.getConfiguration().setInt(HConstants
.HBASE_CLIENT_RETRIES_NUMBER
, 3);
93 // Set hbase.min.version.move.system.tables as version 0 so that
94 // testMoveRegionWhenNotInitialized never fails even if hbase-default has valid default
95 // value present for production use-case.
96 // See HBASE-22923 for details.
97 TEST_UTIL
.getConfiguration().set("hbase.min.version.move.system.tables", "0.0.0");
98 // Start a cluster of two regionservers.
99 TEST_UTIL
.startMiniCluster(2);
100 admin
= TEST_UTIL
.getAdmin();
104 public static void afterAllTests() throws Exception
{
105 TEST_UTIL
.shutdownMiniCluster();
109 * Return the region and current deployment for the region containing the given row. If the region
110 * cannot be found, returns null. If it is found, but not currently deployed, the second element
111 * of the pair may be null.
113 private Pair
<RegionInfo
, ServerName
> getTableRegionForRow(HMaster master
, TableName tableName
,
114 byte[] rowKey
) throws IOException
{
115 final AtomicReference
<Pair
<RegionInfo
, ServerName
>> result
= new AtomicReference
<>(null);
117 ClientMetaTableAccessor
.Visitor visitor
= new ClientMetaTableAccessor
.Visitor() {
119 public boolean visit(Result data
) throws IOException
{
120 if (data
== null || data
.size() <= 0) {
123 Pair
<RegionInfo
, ServerName
> pair
= new Pair
<>(CatalogFamilyFormat
.getRegionInfo(data
),
124 CatalogFamilyFormat
.getServerName(data
, 0));
125 if (!pair
.getFirst().getTable().equals(tableName
)) {
133 MetaTableAccessor
.scanMeta(master
.getConnection(), visitor
, tableName
, rowKey
, 1);
138 @SuppressWarnings("deprecation")
139 public void testMasterOpsWhileSplitting() throws Exception
{
140 SingleProcessHBaseCluster cluster
= TEST_UTIL
.getHBaseCluster();
141 HMaster m
= cluster
.getMaster();
143 try (Table ht
= TEST_UTIL
.createTable(TABLENAME
, FAMILYNAME
)) {
144 assertTrue(m
.getTableStateManager().isTableState(TABLENAME
, TableState
.State
.ENABLED
));
145 TEST_UTIL
.loadTable(ht
, FAMILYNAME
, false);
148 List
<Pair
<RegionInfo
, ServerName
>> tableRegions
= MetaTableAccessor
.getTableRegionsAndLocations(
149 m
.getConnection(), TABLENAME
);
150 LOG
.info("Regions after load: " + Joiner
.on(',').join(tableRegions
));
151 assertEquals(1, tableRegions
.size());
152 assertArrayEquals(HConstants
.EMPTY_START_ROW
,
153 tableRegions
.get(0).getFirst().getStartKey());
154 assertArrayEquals(HConstants
.EMPTY_END_ROW
,
155 tableRegions
.get(0).getFirst().getEndKey());
157 // Now trigger a split and stop when the split is in progress
158 LOG
.info("Splitting table");
159 TEST_UTIL
.getAdmin().split(TABLENAME
);
161 LOG
.info("Making sure we can call getTableRegions while opening");
162 while (tableRegions
.size() < 3) {
163 tableRegions
= MetaTableAccessor
.getTableRegionsAndLocations(m
.getConnection(),
167 LOG
.info("Regions: " + Joiner
.on(',').join(tableRegions
));
168 // We have three regions because one is split-in-progress
169 assertEquals(3, tableRegions
.size());
170 LOG
.info("Making sure we can call getTableRegionClosest while opening");
171 Pair
<RegionInfo
, ServerName
> pair
= getTableRegionForRow(m
, TABLENAME
, Bytes
.toBytes("cde"));
172 LOG
.info("Result is: " + pair
);
173 Pair
<RegionInfo
, ServerName
> tableRegionFromName
=
174 MetaTableAccessor
.getRegion(m
.getConnection(),
175 pair
.getFirst().getRegionName());
176 assertTrue(RegionInfo
.COMPARATOR
.compare(tableRegionFromName
.getFirst(), pair
.getFirst()) == 0);
180 public void testMoveRegionWhenNotInitialized() {
181 SingleProcessHBaseCluster cluster
= TEST_UTIL
.getHBaseCluster();
182 HMaster m
= cluster
.getMaster();
184 m
.setInitialized(false); // fake it, set back later
185 RegionInfo meta
= RegionInfoBuilder
.FIRST_META_REGIONINFO
;
186 m
.move(meta
.getEncodedNameAsBytes(), null);
187 fail("Region should not be moved since master is not initialized");
188 } catch (IOException ioe
) {
189 assertTrue(ioe
instanceof PleaseHoldException
);
191 m
.setInitialized(true);
196 public void testMoveThrowsUnknownRegionException() throws IOException
{
197 final TableName tableName
= TableName
.valueOf(name
.getMethodName());
198 TableDescriptorBuilder tableDescriptorBuilder
=
199 TableDescriptorBuilder
.newBuilder(tableName
);
200 ColumnFamilyDescriptor columnFamilyDescriptor
=
201 ColumnFamilyDescriptorBuilder
.newBuilder(Bytes
.toBytes("value")).build();
202 tableDescriptorBuilder
.setColumnFamily(columnFamilyDescriptor
);
204 admin
.createTable(tableDescriptorBuilder
.build());
206 RegionInfo hri
= RegionInfoBuilder
.newBuilder(tableName
)
207 .setStartKey(Bytes
.toBytes("A"))
208 .setEndKey(Bytes
.toBytes("Z"))
210 admin
.move(hri
.getEncodedNameAsBytes());
211 fail("Region should not be moved since it is fake");
212 } catch (IOException ioe
) {
213 assertTrue(ioe
instanceof UnknownRegionException
);
215 TEST_UTIL
.deleteTable(tableName
);
220 public void testMoveThrowsPleaseHoldException() throws IOException
{
221 final TableName tableName
= TableName
.valueOf(name
.getMethodName());
222 HMaster master
= TEST_UTIL
.getMiniHBaseCluster().getMaster();
223 TableDescriptorBuilder tableDescriptorBuilder
=
224 TableDescriptorBuilder
.newBuilder(tableName
);
225 ColumnFamilyDescriptor columnFamilyDescriptor
=
226 ColumnFamilyDescriptorBuilder
.newBuilder(Bytes
.toBytes("value")).build();
227 tableDescriptorBuilder
.setColumnFamily(columnFamilyDescriptor
);
229 admin
.createTable(tableDescriptorBuilder
.build());
231 List
<RegionInfo
> tableRegions
= admin
.getRegions(tableName
);
233 master
.setInitialized(false); // fake it, set back later
234 admin
.move(tableRegions
.get(0).getEncodedNameAsBytes());
235 fail("Region should not be moved since master is not initialized");
236 } catch (IOException ioe
) {
237 assertTrue(StringUtils
.stringifyException(ioe
).contains("PleaseHoldException"));
239 master
.setInitialized(true);
240 TEST_UTIL
.deleteTable(tableName
);
245 public void testFlushedSequenceIdPersistLoad() throws Exception
{
246 Configuration conf
= TEST_UTIL
.getConfiguration();
247 int msgInterval
= conf
.getInt("hbase.regionserver.msginterval", 100);
248 // insert some data into META
249 TableName tableName
= TableName
.valueOf("testFlushSeqId");
250 TableDescriptor tableDescriptor
= TableDescriptorBuilder
.newBuilder(tableName
)
251 .setColumnFamily(ColumnFamilyDescriptorBuilder
.of(Bytes
.toBytes("cf"))).build();
252 Table table
= TEST_UTIL
.createTable(tableDescriptor
, null);
254 TEST_UTIL
.flush(TableName
.META_TABLE_NAME
);
255 // wait for regionserver report
256 Threads
.sleep(msgInterval
* 2);
257 // record flush seqid before cluster shutdown
258 Map
<byte[], Long
> regionMapBefore
=
259 TEST_UTIL
.getHBaseCluster().getMaster().getServerManager()
260 .getFlushedSequenceIdByRegion();
261 // restart hbase cluster which will cause flushed sequence id persist and reload
262 TEST_UTIL
.getMiniHBaseCluster().shutdown();
263 TEST_UTIL
.restartHBaseCluster(2);
264 TEST_UTIL
.waitUntilNoRegionsInTransition();
265 // check equality after reloading flushed sequence id map
266 Map
<byte[], Long
> regionMapAfter
=
267 TEST_UTIL
.getHBaseCluster().getMaster().getServerManager()
268 .getFlushedSequenceIdByRegion();
269 assertTrue(regionMapBefore
.equals(regionMapAfter
));
273 public void testBlockingHbkc1WithLockFile() throws IOException
{
274 // This is how the patch to the lock file is created inside in HBaseFsck. Too hard to use its
275 // actual method without disturbing HBaseFsck... Do the below mimic instead.
276 Path hbckLockPath
= new Path(HBaseFsck
.getTmpDir(TEST_UTIL
.getConfiguration()),
277 HBaseFsck
.HBCK_LOCK_FILE
);
278 FileSystem fs
= TEST_UTIL
.getTestFileSystem();
279 assertTrue(fs
.exists(hbckLockPath
));
280 TEST_UTIL
.getMiniHBaseCluster().
281 killMaster(TEST_UTIL
.getMiniHBaseCluster().getMaster().getServerName());
282 assertTrue(fs
.exists(hbckLockPath
));
283 TEST_UTIL
.getMiniHBaseCluster().startMaster();
284 TEST_UTIL
.waitFor(30000, () -> TEST_UTIL
.getMiniHBaseCluster().getMaster() != null &&
285 TEST_UTIL
.getMiniHBaseCluster().getMaster().isInitialized());
286 assertTrue(fs
.exists(hbckLockPath
));
287 // Start a second Master. Should be fine.
288 TEST_UTIL
.getMiniHBaseCluster().startMaster();
289 assertTrue(fs
.exists(hbckLockPath
));
290 fs
.delete(hbckLockPath
, true);
291 assertFalse(fs
.exists(hbckLockPath
));
293 TEST_UTIL
.getMiniHBaseCluster().getLiveMasterThreads().stream().
294 map(sn
-> sn
.getMaster().getServerName()).forEach(sn
-> {
296 TEST_UTIL
.getMiniHBaseCluster().killMaster(sn
);
297 } catch (IOException e
) {
302 TEST_UTIL
.getMiniHBaseCluster().startMaster();
303 TEST_UTIL
.waitFor(30000, () -> TEST_UTIL
.getMiniHBaseCluster().getMaster() != null &&
304 TEST_UTIL
.getMiniHBaseCluster().getMaster().isInitialized());
305 // Assert lock gets put in place again.
306 assertTrue(fs
.exists(hbckLockPath
));