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