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.master.assignment; 019 020import static org.apache.hadoop.hbase.TestMetaTableAccessor.assertEmptyMetaLocation; 021import static org.junit.Assert.assertEquals; 022import static org.junit.Assert.assertFalse; 023import static org.junit.Assert.assertNotEquals; 024import static org.junit.Assert.assertNotNull; 025import static org.junit.Assert.assertNull; 026import static org.junit.Assert.assertTrue; 027 028import java.io.IOException; 029import java.util.ArrayList; 030import java.util.List; 031import java.util.concurrent.ThreadLocalRandom; 032import java.util.concurrent.TimeUnit; 033import java.util.concurrent.atomic.AtomicBoolean; 034import org.apache.hadoop.hbase.CatalogFamilyFormat; 035import org.apache.hadoop.hbase.Cell; 036import org.apache.hadoop.hbase.HBaseClassTestRule; 037import org.apache.hadoop.hbase.HBaseTestingUtil; 038import org.apache.hadoop.hbase.HConstants; 039import org.apache.hadoop.hbase.MetaTableAccessor; 040import org.apache.hadoop.hbase.ServerName; 041import org.apache.hadoop.hbase.TableName; 042import org.apache.hadoop.hbase.TableNameTestRule; 043import org.apache.hadoop.hbase.client.Admin; 044import org.apache.hadoop.hbase.client.Get; 045import org.apache.hadoop.hbase.client.Put; 046import org.apache.hadoop.hbase.client.RegionInfo; 047import org.apache.hadoop.hbase.client.RegionInfoBuilder; 048import org.apache.hadoop.hbase.client.Result; 049import org.apache.hadoop.hbase.client.Table; 050import org.apache.hadoop.hbase.client.TableDescriptorBuilder; 051import org.apache.hadoop.hbase.master.RegionState; 052import org.apache.hadoop.hbase.regionserver.HRegion; 053import org.apache.hadoop.hbase.testclassification.MasterTests; 054import org.apache.hadoop.hbase.testclassification.MediumTests; 055import org.apache.hadoop.hbase.util.Bytes; 056import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 057import org.apache.hadoop.hbase.util.ManualEnvironmentEdge; 058import org.junit.AfterClass; 059import org.junit.BeforeClass; 060import org.junit.ClassRule; 061import org.junit.Rule; 062import org.junit.Test; 063import org.junit.experimental.categories.Category; 064 065import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 066 067@Category({ MasterTests.class, MediumTests.class }) 068public class TestRegionStateStore { 069 070 @ClassRule 071 public static final HBaseClassTestRule CLASS_RULE = 072 HBaseClassTestRule.forClass(TestRegionStateStore.class); 073 074 private static HBaseTestingUtil UTIL = new HBaseTestingUtil(); 075 076 @Rule 077 public final TableNameTestRule name = new TableNameTestRule(); 078 079 @BeforeClass 080 public static void beforeClass() throws Exception { 081 UTIL.startMiniCluster(); 082 } 083 084 @AfterClass 085 public static void tearDown() throws Exception { 086 UTIL.shutdownMiniCluster(); 087 } 088 089 @Test 090 public void testVisitMetaForRegionExistingRegion() throws Exception { 091 final TableName tableName = TableName.valueOf("testVisitMetaForRegion"); 092 UTIL.createTable(tableName, "cf"); 093 final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName); 094 final String encodedName = regions.get(0).getRegionInfo().getEncodedName(); 095 final RegionStateStore regionStateStore = 096 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 097 final AtomicBoolean visitorCalled = new AtomicBoolean(false); 098 regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() { 099 @Override 100 public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state, 101 ServerName regionLocation, ServerName lastHost, long openSeqNum) { 102 assertEquals(encodedName, regionInfo.getEncodedName()); 103 visitorCalled.set(true); 104 } 105 }); 106 assertTrue("Visitor has not been called.", visitorCalled.get()); 107 } 108 109 @Test 110 public void testVisitMetaForBadRegionState() throws Exception { 111 final TableName tableName = TableName.valueOf("testVisitMetaForBadRegionState"); 112 UTIL.createTable(tableName, "cf"); 113 final List<HRegion> regions = UTIL.getHBaseCluster().getRegions(tableName); 114 final String encodedName = regions.get(0).getRegionInfo().getEncodedName(); 115 final RegionStateStore regionStateStore = 116 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 117 118 // add the BAD_STATE which does not exist in enum RegionState.State 119 Put put = 120 new Put(regions.get(0).getRegionInfo().getRegionName(), EnvironmentEdgeManager.currentTime()); 121 put.addColumn(HConstants.CATALOG_FAMILY, HConstants.STATE_QUALIFIER, 122 Bytes.toBytes("BAD_STATE")); 123 124 try (Table table = UTIL.getConnection().getTable(TableName.META_TABLE_NAME)) { 125 table.put(put); 126 } 127 128 final AtomicBoolean visitorCalled = new AtomicBoolean(false); 129 regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() { 130 @Override 131 public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state, 132 ServerName regionLocation, ServerName lastHost, long openSeqNum) { 133 assertEquals(encodedName, regionInfo.getEncodedName()); 134 assertNull(state); 135 visitorCalled.set(true); 136 } 137 }); 138 assertTrue("Visitor has not been called.", visitorCalled.get()); 139 } 140 141 @Test 142 public void testVisitMetaForRegionNonExistingRegion() throws Exception { 143 final String encodedName = "fakeencodedregionname"; 144 final RegionStateStore regionStateStore = 145 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 146 final AtomicBoolean visitorCalled = new AtomicBoolean(false); 147 regionStateStore.visitMetaForRegion(encodedName, new RegionStateStore.RegionStateVisitor() { 148 @Override 149 public void visitRegionState(Result result, RegionInfo regionInfo, RegionState.State state, 150 ServerName regionLocation, ServerName lastHost, long openSeqNum) { 151 visitorCalled.set(true); 152 } 153 }); 154 assertFalse("Visitor has been called, but it shouldn't.", visitorCalled.get()); 155 } 156 157 @Test 158 public void testMetaLocationForRegionReplicasIsAddedAtRegionSplit() throws IOException { 159 long regionId = EnvironmentEdgeManager.currentTime(); 160 ServerName serverName0 = 161 ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong()); 162 TableName tableName = name.getTableName(); 163 RegionInfo parent = RegionInfoBuilder.newBuilder(tableName) 164 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) 165 .setRegionId(regionId).setReplicaId(0).build(); 166 167 RegionInfo splitA = RegionInfoBuilder.newBuilder(tableName) 168 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false) 169 .setRegionId(regionId + 1).setReplicaId(0).build(); 170 RegionInfo splitB = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a")) 171 .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId + 1).setReplicaId(0) 172 .build(); 173 List<RegionInfo> regionInfos = Lists.newArrayList(parent); 174 MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3); 175 final RegionStateStore regionStateStore = 176 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 177 regionStateStore.splitRegion(parent, splitA, splitB, serverName0, 178 TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build()); 179 try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { 180 assertEmptyMetaLocation(meta, splitA.getRegionName(), 1); 181 assertEmptyMetaLocation(meta, splitA.getRegionName(), 2); 182 assertEmptyMetaLocation(meta, splitB.getRegionName(), 1); 183 assertEmptyMetaLocation(meta, splitB.getRegionName(), 2); 184 } 185 } 186 187 @Test 188 public void testEmptyMetaDaughterLocationDuringSplit() throws IOException { 189 TableName tableName = name.getTableName(); 190 long regionId = EnvironmentEdgeManager.currentTime(); 191 ServerName serverName0 = 192 ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong()); 193 RegionInfo parent = RegionInfoBuilder.newBuilder(tableName) 194 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) 195 .setRegionId(regionId).setReplicaId(0).build(); 196 RegionInfo splitA = RegionInfoBuilder.newBuilder(tableName) 197 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false) 198 .setRegionId(regionId + 1).setReplicaId(0).build(); 199 RegionInfo splitB = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a")) 200 .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId + 1).setReplicaId(0) 201 .build(); 202 List<RegionInfo> regionInfos = Lists.newArrayList(parent); 203 MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3); 204 final RegionStateStore regionStateStore = 205 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 206 regionStateStore.splitRegion(parent, splitA, splitB, serverName0, 207 TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build()); 208 try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { 209 Get get1 = new Get(splitA.getRegionName()); 210 Result resultA = meta.get(get1); 211 Cell serverCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY, 212 CatalogFamilyFormat.getServerColumn(splitA.getReplicaId())); 213 Cell startCodeCellA = resultA.getColumnLatestCell(HConstants.CATALOG_FAMILY, 214 CatalogFamilyFormat.getStartCodeColumn(splitA.getReplicaId())); 215 assertNull(serverCellA); 216 assertNull(startCodeCellA); 217 218 Get get2 = new Get(splitB.getRegionName()); 219 Result resultB = meta.get(get2); 220 Cell serverCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY, 221 CatalogFamilyFormat.getServerColumn(splitB.getReplicaId())); 222 Cell startCodeCellB = resultB.getColumnLatestCell(HConstants.CATALOG_FAMILY, 223 CatalogFamilyFormat.getStartCodeColumn(splitB.getReplicaId())); 224 assertNull(serverCellB); 225 assertNull(startCodeCellB); 226 } 227 } 228 229 @Test 230 public void testMetaLocationForRegionReplicasIsAddedAtRegionMerge() throws IOException { 231 long regionId = EnvironmentEdgeManager.currentTime(); 232 ServerName serverName0 = 233 ServerName.valueOf("foo", 60010, ThreadLocalRandom.current().nextLong()); 234 235 TableName tableName = name.getTableName(); 236 RegionInfo parentA = RegionInfoBuilder.newBuilder(tableName).setStartKey(Bytes.toBytes("a")) 237 .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId).setReplicaId(0) 238 .build(); 239 240 RegionInfo parentB = RegionInfoBuilder.newBuilder(tableName) 241 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(Bytes.toBytes("a")).setSplit(false) 242 .setRegionId(regionId).setReplicaId(0).build(); 243 RegionInfo merged = RegionInfoBuilder.newBuilder(tableName) 244 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) 245 .setRegionId(regionId + 1).setReplicaId(0).build(); 246 247 final RegionStateStore regionStateStore = 248 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 249 250 try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { 251 List<RegionInfo> regionInfos = Lists.newArrayList(parentA, parentB); 252 MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3); 253 regionStateStore.mergeRegions(merged, new RegionInfo[] { parentA, parentB }, serverName0, 254 TableDescriptorBuilder.newBuilder(tableName).setRegionReplication(3).build()); 255 assertEmptyMetaLocation(meta, merged.getRegionName(), 1); 256 assertEmptyMetaLocation(meta, merged.getRegionName(), 2); 257 } 258 } 259 260 @Test 261 public void testMastersSystemTimeIsUsedInMergeRegions() throws IOException { 262 long regionId = EnvironmentEdgeManager.currentTime(); 263 TableName tableName = name.getTableName(); 264 265 RegionInfo regionInfoA = RegionInfoBuilder.newBuilder(tableName) 266 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(new byte[] { 'a' }).setSplit(false) 267 .setRegionId(regionId).setReplicaId(0).build(); 268 269 RegionInfo regionInfoB = RegionInfoBuilder.newBuilder(tableName).setStartKey(new byte[] { 'a' }) 270 .setEndKey(HConstants.EMPTY_END_ROW).setSplit(false).setRegionId(regionId).setReplicaId(0) 271 .build(); 272 RegionInfo mergedRegionInfo = RegionInfoBuilder.newBuilder(tableName) 273 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) 274 .setRegionId(regionId).setReplicaId(0).build(); 275 276 ServerName sn = ServerName.valueOf("bar", 0, 0); 277 try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { 278 List<RegionInfo> regionInfos = Lists.newArrayList(regionInfoA, regionInfoB); 279 MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 1); 280 281 // write the serverName column with a big current time, but set the masters time as even 282 // bigger. When region merge deletes the rows for regionA and regionB, the serverName columns 283 // should not be seen by the following get 284 long serverNameTime = EnvironmentEdgeManager.currentTime() + 100000000; 285 long masterSystemTime = EnvironmentEdgeManager.currentTime() + 123456789; 286 287 // write the serverName columns 288 MetaTableAccessor.updateRegionLocation(UTIL.getConnection(), regionInfoA, sn, 1, 289 serverNameTime); 290 291 // assert that we have the serverName column with expected ts 292 Get get = new Get(mergedRegionInfo.getRegionName()); 293 Result result = meta.get(get); 294 Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 295 CatalogFamilyFormat.getServerColumn(0)); 296 assertNotNull(serverCell); 297 assertEquals(serverNameTime, serverCell.getTimestamp()); 298 299 final RegionStateStore regionStateStore = 300 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 301 302 ManualEnvironmentEdge edge = new ManualEnvironmentEdge(); 303 edge.setValue(masterSystemTime); 304 EnvironmentEdgeManager.injectEdge(edge); 305 try { 306 // now merge the regions, effectively deleting the rows for region a and b. 307 regionStateStore.mergeRegions(mergedRegionInfo, 308 new RegionInfo[] { regionInfoA, regionInfoB }, sn, 309 TableDescriptorBuilder.newBuilder(tableName).build()); 310 } finally { 311 EnvironmentEdgeManager.reset(); 312 } 313 314 result = meta.get(get); 315 serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 316 CatalogFamilyFormat.getServerColumn(0)); 317 Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 318 CatalogFamilyFormat.getStartCodeColumn(0)); 319 Cell seqNumCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 320 CatalogFamilyFormat.getSeqNumColumn(0)); 321 assertNull(serverCell); 322 assertNull(startCodeCell); 323 assertNull(seqNumCell); 324 } 325 } 326 327 /** 328 * Test for HBASE-23044. 329 */ 330 @Test 331 public void testGetMergeRegions() throws Exception { 332 TableName tn = name.getTableName(); 333 UTIL.createMultiRegionTable(tn, Bytes.toBytes("CF"), 4); 334 UTIL.waitTableAvailable(tn); 335 Admin admin = UTIL.getAdmin(); 336 List<RegionInfo> regions = admin.getRegions(tn); 337 assertEquals(4, regions.size()); 338 admin 339 .mergeRegionsAsync( 340 new byte[][] { regions.get(0).getRegionName(), regions.get(1).getRegionName() }, false) 341 .get(60, TimeUnit.SECONDS); 342 admin 343 .mergeRegionsAsync( 344 new byte[][] { regions.get(2).getRegionName(), regions.get(3).getRegionName() }, false) 345 .get(60, TimeUnit.SECONDS); 346 347 List<RegionInfo> mergedRegions = admin.getRegions(tn); 348 assertEquals(2, mergedRegions.size()); 349 RegionInfo mergedRegion0 = mergedRegions.get(0); 350 RegionInfo mergedRegion1 = mergedRegions.get(1); 351 352 final RegionStateStore regionStateStore = 353 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 354 355 List<RegionInfo> mergeParents = regionStateStore.getMergeRegions(mergedRegion0); 356 assertTrue(mergeParents.contains(regions.get(0))); 357 assertTrue(mergeParents.contains(regions.get(1))); 358 mergeParents = regionStateStore.getMergeRegions(mergedRegion1); 359 assertTrue(mergeParents.contains(regions.get(2))); 360 assertTrue(mergeParents.contains(regions.get(3))); 361 362 // Delete merge qualifiers for mergedRegion0, then cannot getMergeRegions again 363 regionStateStore.deleteMergeQualifiers(mergedRegion0); 364 mergeParents = regionStateStore.getMergeRegions(mergedRegion0); 365 assertNull(mergeParents); 366 367 mergeParents = regionStateStore.getMergeRegions(mergedRegion1); 368 assertTrue(mergeParents.contains(regions.get(2))); 369 assertTrue(mergeParents.contains(regions.get(3))); 370 } 371 372 @Test 373 public void testAddMergeRegions() throws IOException { 374 TableName tn = name.getTableName(); 375 Put put = new Put(Bytes.toBytes(name.getTableName().getNameAsString())); 376 List<RegionInfo> ris = new ArrayList<>(); 377 int limit = 10; 378 byte[] previous = HConstants.EMPTY_START_ROW; 379 for (int i = 0; i < limit; i++) { 380 RegionInfo ri = 381 RegionInfoBuilder.newBuilder(tn).setStartKey(previous).setEndKey(Bytes.toBytes(i)).build(); 382 ris.add(ri); 383 } 384 put = RegionStateStore.addMergeRegions(put, ris); 385 List<Cell> cells = put.getFamilyCellMap().get(HConstants.CATALOG_FAMILY); 386 String previousQualifier = null; 387 assertEquals(limit, cells.size()); 388 for (Cell cell : cells) { 389 String qualifier = Bytes.toString(cell.getQualifierArray()); 390 assertTrue(qualifier.startsWith(HConstants.MERGE_QUALIFIER_PREFIX_STR)); 391 assertNotEquals(qualifier, previousQualifier); 392 previousQualifier = qualifier; 393 } 394 } 395 396 @Test 397 public void testMetaLocationForRegionReplicasIsRemovedAtTableDeletion() throws IOException { 398 long regionId = EnvironmentEdgeManager.currentTime(); 399 TableName tableName = name.getTableName(); 400 RegionInfo primary = RegionInfoBuilder.newBuilder(tableName) 401 .setStartKey(HConstants.EMPTY_START_ROW).setEndKey(HConstants.EMPTY_END_ROW).setSplit(false) 402 .setRegionId(regionId).setReplicaId(0).build(); 403 404 try (Table meta = MetaTableAccessor.getMetaHTable(UTIL.getConnection())) { 405 List<RegionInfo> regionInfos = Lists.newArrayList(primary); 406 MetaTableAccessor.addRegionsToMeta(UTIL.getConnection(), regionInfos, 3); 407 final RegionStateStore regionStateStore = 408 UTIL.getHBaseCluster().getMaster().getAssignmentManager().getRegionStateStore(); 409 regionStateStore.removeRegionReplicas(tableName, 3, 1); 410 Get get = new Get(primary.getRegionName()); 411 Result result = meta.get(get); 412 for (int replicaId = 0; replicaId < 3; replicaId++) { 413 Cell serverCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 414 CatalogFamilyFormat.getServerColumn(replicaId)); 415 Cell startCodeCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 416 CatalogFamilyFormat.getStartCodeColumn(replicaId)); 417 Cell stateCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 418 CatalogFamilyFormat.getRegionStateColumn(replicaId)); 419 Cell snCell = result.getColumnLatestCell(HConstants.CATALOG_FAMILY, 420 CatalogFamilyFormat.getServerNameColumn(replicaId)); 421 if (replicaId == 0) { 422 assertNotNull(stateCell); 423 } else { 424 assertNull(serverCell); 425 assertNull(startCodeCell); 426 assertNull(stateCell); 427 assertNull(snCell); 428 } 429 } 430 } 431 } 432}