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; 019 020import edu.umd.cs.findbugs.annotations.NonNull; 021import edu.umd.cs.findbugs.annotations.Nullable; 022import java.io.Closeable; 023import java.io.IOException; 024import java.util.ArrayList; 025import java.util.Collections; 026import java.util.LinkedHashMap; 027import java.util.List; 028import java.util.Map; 029import java.util.Objects; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.Cell.Type; 032import org.apache.hadoop.hbase.ClientMetaTableAccessor.QueryType; 033import org.apache.hadoop.hbase.client.Connection; 034import org.apache.hadoop.hbase.client.Consistency; 035import org.apache.hadoop.hbase.client.Delete; 036import org.apache.hadoop.hbase.client.Get; 037import org.apache.hadoop.hbase.client.Mutation; 038import org.apache.hadoop.hbase.client.Put; 039import org.apache.hadoop.hbase.client.RegionInfo; 040import org.apache.hadoop.hbase.client.RegionReplicaUtil; 041import org.apache.hadoop.hbase.client.Result; 042import org.apache.hadoop.hbase.client.ResultScanner; 043import org.apache.hadoop.hbase.client.Scan; 044import org.apache.hadoop.hbase.client.Table; 045import org.apache.hadoop.hbase.client.TableState; 046import org.apache.hadoop.hbase.filter.Filter; 047import org.apache.hadoop.hbase.filter.RowFilter; 048import org.apache.hadoop.hbase.filter.SubstringComparator; 049import org.apache.hadoop.hbase.master.RegionState; 050import org.apache.hadoop.hbase.regionserver.RSAnnotationReadingPriorityFunction; 051import org.apache.hadoop.hbase.util.Bytes; 052import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 053import org.apache.hadoop.hbase.util.ExceptionUtil; 054import org.apache.hadoop.hbase.util.Pair; 055import org.apache.hadoop.hbase.util.PairOfSameType; 056import org.apache.yetus.audience.InterfaceAudience; 057import org.slf4j.Logger; 058import org.slf4j.LoggerFactory; 059 060/** 061 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored 062 * to <code>hbase:meta</code>. 063 * <p/> 064 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is 065 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection 066 * (opened before each operation, closed right after), while when used on HM or HRS (like in 067 * AssignmentManager) we want permanent connection. 068 * <p/> 069 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table 070 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is 071 * called default replica. 072 * <p/> 073 * <h2>Meta layout</h2> For each table there is single row named for the table with a 'table' column 074 * family. The column family currently has one column in it, the 'state' column: 075 * 076 * <pre> 077 * table:state => contains table state 078 * </pre> 079 * 080 * For the catalog family, see the comments of {@link CatalogFamilyFormat} for more details. 081 * <p/> 082 * TODO: Add rep_barrier for serial replication explanation. See SerialReplicationChecker. 083 * <p/> 084 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not 085 * leak out of it (through Result objects, etc) 086 * @see CatalogFamilyFormat 087 * @see ClientMetaTableAccessor 088 */ 089@InterfaceAudience.Private 090public final class MetaTableAccessor { 091 092 private static final Logger LOG = LoggerFactory.getLogger(MetaTableAccessor.class); 093 private static final Logger METALOG = LoggerFactory.getLogger("org.apache.hadoop.hbase.META"); 094 095 private MetaTableAccessor() { 096 } 097 098 //////////////////////// 099 // Reading operations // 100 //////////////////////// 101 102 /** 103 * Performs a full scan of <code>hbase:meta</code> for regions. 104 * @param connection connection we're using 105 * @param visitor Visitor invoked against each row in regions family. 106 */ 107 public static void fullScanRegions(Connection connection, 108 final ClientMetaTableAccessor.Visitor visitor) throws IOException { 109 scanMeta(connection, null, null, QueryType.REGION, visitor); 110 } 111 112 /** 113 * Performs a full scan of <code>hbase:meta</code> for regions. 114 * @param connection connection we're using 115 */ 116 public static List<Result> fullScanRegions(Connection connection) throws IOException { 117 return fullScan(connection, QueryType.REGION); 118 } 119 120 /** 121 * Performs a full scan of <code>hbase:meta</code> for tables. 122 * @param connection connection we're using 123 * @param visitor Visitor invoked against each row in tables family. 124 */ 125 public static void fullScanTables(Connection connection, 126 final ClientMetaTableAccessor.Visitor visitor) throws IOException { 127 scanMeta(connection, null, null, QueryType.TABLE, visitor); 128 } 129 130 /** 131 * Performs a full scan of <code>hbase:meta</code>. 132 * @param connection connection we're using 133 * @param type scanned part of meta 134 * @return List of {@link Result} 135 */ 136 private static List<Result> fullScan(Connection connection, QueryType type) throws IOException { 137 ClientMetaTableAccessor.CollectAllVisitor v = new ClientMetaTableAccessor.CollectAllVisitor(); 138 scanMeta(connection, null, null, type, v); 139 return v.getResults(); 140 } 141 142 /** 143 * Callers should call close on the returned {@link Table} instance. 144 * @param connection connection we're using to access Meta 145 * @return An {@link Table} for <code>hbase:meta</code> 146 * @throws NullPointerException if {@code connection} is {@code null} 147 */ 148 public static Table getMetaHTable(final Connection connection) throws IOException { 149 // We used to pass whole CatalogTracker in here, now we just pass in Connection 150 Objects.requireNonNull(connection, "Connection cannot be null"); 151 if (connection.isClosed()) { 152 throw new IOException("connection is closed"); 153 } 154 return connection.getTable(TableName.META_TABLE_NAME); 155 } 156 157 /** 158 * Gets the region info and assignment for the specified region. 159 * @param connection connection we're using 160 * @param regionName Region to lookup. 161 * @return Location and RegionInfo for <code>regionName</code> 162 * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead 163 */ 164 @Deprecated 165 public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte[] regionName) 166 throws IOException { 167 HRegionLocation location = getRegionLocation(connection, regionName); 168 return location == null ? null : new Pair<>(location.getRegion(), location.getServerName()); 169 } 170 171 /** 172 * Returns the HRegionLocation from meta for the given region 173 * @param connection connection we're using 174 * @param regionName region we're looking for 175 * @return HRegionLocation for the given region 176 */ 177 public static HRegionLocation getRegionLocation(Connection connection, byte[] regionName) 178 throws IOException { 179 byte[] row = regionName; 180 RegionInfo parsedInfo = null; 181 try { 182 parsedInfo = CatalogFamilyFormat.parseRegionInfoFromRegionName(regionName); 183 row = CatalogFamilyFormat.getMetaKeyForRegion(parsedInfo); 184 } catch (Exception parseEx) { 185 // Ignore. This is used with tableName passed as regionName. 186 } 187 Get get = new Get(row); 188 get.addFamily(HConstants.CATALOG_FAMILY); 189 get.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS); 190 Result r; 191 try (Table t = getMetaHTable(connection)) { 192 r = t.get(get); 193 } 194 RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r); 195 return locations == null 196 ? null 197 : locations.getRegionLocation( 198 parsedInfo == null ? RegionInfo.DEFAULT_REPLICA_ID : parsedInfo.getReplicaId()); 199 } 200 201 /** 202 * Returns the HRegionLocation from meta for the given region 203 * @param connection connection we're using 204 * @param regionInfo region information 205 * @return HRegionLocation for the given region 206 */ 207 public static HRegionLocation getRegionLocation(Connection connection, RegionInfo regionInfo) 208 throws IOException { 209 return CatalogFamilyFormat.getRegionLocation(getCatalogFamilyRow(connection, regionInfo), 210 regionInfo, regionInfo.getReplicaId()); 211 } 212 213 /** Returns Return the {@link HConstants#CATALOG_FAMILY} row from hbase:meta. */ 214 public static Result getCatalogFamilyRow(Connection connection, RegionInfo ri) 215 throws IOException { 216 Get get = new Get(CatalogFamilyFormat.getMetaKeyForRegion(ri)); 217 get.addFamily(HConstants.CATALOG_FAMILY); 218 get.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS); 219 try (Table t = getMetaHTable(connection)) { 220 return t.get(get); 221 } 222 } 223 224 /** 225 * Gets the result in hbase:meta for the specified region. 226 * @param connection connection we're using 227 * @param regionInfo region we're looking for 228 * @return result of the specified region 229 */ 230 public static Result getRegionResult(Connection connection, RegionInfo regionInfo) 231 throws IOException { 232 return getCatalogFamilyRow(connection, regionInfo); 233 } 234 235 /** 236 * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>, returning 237 * a single related <code>Result</code> instance if any row is found, null otherwise. 238 * @param connection the connection to query META table. 239 * @param regionEncodedName the region encoded name to look for at META. 240 * @return <code>Result</code> instance with the row related info in META, null otherwise. 241 * @throws IOException if any errors occur while querying META. 242 */ 243 public static Result scanByRegionEncodedName(Connection connection, String regionEncodedName) 244 throws IOException { 245 RowFilter rowFilter = 246 new RowFilter(CompareOperator.EQUAL, new SubstringComparator(regionEncodedName)); 247 Scan scan = getMetaScan(connection.getConfiguration(), 1); 248 scan.setFilter(rowFilter); 249 try (Table table = getMetaHTable(connection); 250 ResultScanner resultScanner = table.getScanner(scan)) { 251 return resultScanner.next(); 252 } 253 } 254 255 /** 256 * Lists all of the regions currently in META. 257 * @param connection to connect with 258 * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions, 259 * true and we'll leave out offlined regions from returned list 260 * @return List of all user-space regions. 261 */ 262 public static List<RegionInfo> getAllRegions(Connection connection, 263 boolean excludeOfflinedSplitParents) throws IOException { 264 List<Pair<RegionInfo, ServerName>> result; 265 266 result = getTableRegionsAndLocations(connection, null, excludeOfflinedSplitParents); 267 268 return getListOfRegionInfos(result); 269 270 } 271 272 /** 273 * Gets all of the regions of the specified table. Do not use this method to get meta table 274 * regions, use methods in MetaTableLocator instead. 275 * @param connection connection we're using 276 * @param tableName table we're looking for 277 * @return Ordered list of {@link RegionInfo}. 278 */ 279 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName) 280 throws IOException { 281 return getTableRegions(connection, tableName, false); 282 } 283 284 /** 285 * Gets all of the regions of the specified table. Do not use this method to get meta table 286 * regions, use methods in MetaTableLocator instead. 287 * @param connection connection we're using 288 * @param tableName table we're looking for 289 * @param excludeOfflinedSplitParents If true, do not include offlined split parents in the 290 * return. 291 * @return Ordered list of {@link RegionInfo}. 292 */ 293 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName, 294 final boolean excludeOfflinedSplitParents) throws IOException { 295 List<Pair<RegionInfo, ServerName>> result = 296 getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents); 297 return getListOfRegionInfos(result); 298 } 299 300 private static List<RegionInfo> 301 getListOfRegionInfos(final List<Pair<RegionInfo, ServerName>> pairs) { 302 if (pairs == null || pairs.isEmpty()) { 303 return Collections.emptyList(); 304 } 305 List<RegionInfo> result = new ArrayList<>(pairs.size()); 306 for (Pair<RegionInfo, ServerName> pair : pairs) { 307 result.add(pair.getFirst()); 308 } 309 return result; 310 } 311 312 /** 313 * This method creates a Scan object that will only scan catalog rows that belong to the specified 314 * table. It doesn't specify any columns. This is a better alternative to just using a start row 315 * and scan until it hits a new table since that requires parsing the HRI to get the table name. 316 * @param tableName bytes of table's name 317 * @return configured Scan object 318 */ 319 public static Scan getScanForTableName(Configuration conf, TableName tableName) { 320 // Start key is just the table name with delimiters 321 byte[] startKey = ClientMetaTableAccessor.getTableStartRowForMeta(tableName, QueryType.REGION); 322 // Stop key appends the smallest possible char to the table name 323 byte[] stopKey = ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION); 324 325 Scan scan = getMetaScan(conf, -1); 326 scan.withStartRow(startKey); 327 scan.withStopRow(stopKey); 328 return scan; 329 } 330 331 private static Scan getMetaScan(Configuration conf, int rowUpperLimit) { 332 Scan scan = new Scan(); 333 int scannerCaching = conf.getInt(HConstants.HBASE_META_SCANNER_CACHING, 334 HConstants.DEFAULT_HBASE_META_SCANNER_CACHING); 335 if (conf.getBoolean(HConstants.USE_META_REPLICAS, HConstants.DEFAULT_USE_META_REPLICAS)) { 336 scan.setConsistency(Consistency.TIMELINE); 337 } 338 if (rowUpperLimit > 0) { 339 scan.setLimit(rowUpperLimit); 340 scan.setReadType(Scan.ReadType.PREAD); 341 } 342 scan.setCaching(scannerCaching); 343 scan.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS); 344 return scan; 345 } 346 347 /** 348 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead. 349 * @param connection connection we're using 350 * @param tableName table we're looking for 351 * @return Return list of regioninfos and server. 352 */ 353 public static List<Pair<RegionInfo, ServerName>> 354 getTableRegionsAndLocations(Connection connection, TableName tableName) throws IOException { 355 return getTableRegionsAndLocations(connection, tableName, true); 356 } 357 358 /** 359 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead. 360 * @param connection connection we're using 361 * @param tableName table to work with, can be null for getting all regions 362 * @param excludeOfflinedSplitParents don't return split parents 363 * @return Return list of regioninfos and server addresses. 364 */ 365 // What happens here when 1M regions in hbase:meta? This won't scale? 366 public static List<Pair<RegionInfo, ServerName>> getTableRegionsAndLocations( 367 Connection connection, @Nullable final TableName tableName, 368 final boolean excludeOfflinedSplitParents) throws IOException { 369 if (tableName != null && tableName.equals(TableName.META_TABLE_NAME)) { 370 throw new IOException( 371 "This method can't be used to locate meta regions; use MetaTableLocator instead"); 372 } 373 // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress 374 ClientMetaTableAccessor.CollectRegionLocationsVisitor visitor = 375 new ClientMetaTableAccessor.CollectRegionLocationsVisitor(excludeOfflinedSplitParents); 376 scanMeta(connection, 377 ClientMetaTableAccessor.getTableStartRowForMeta(tableName, QueryType.REGION), 378 ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION), QueryType.REGION, 379 visitor); 380 return visitor.getResults(); 381 } 382 383 public static void fullScanMetaAndPrint(Connection connection) throws IOException { 384 ClientMetaTableAccessor.Visitor v = r -> { 385 if (r == null || r.isEmpty()) { 386 return true; 387 } 388 LOG.info("fullScanMetaAndPrint.Current Meta Row: {}", r); 389 TableState state = CatalogFamilyFormat.getTableState(r); 390 if (state != null) { 391 LOG.info("fullScanMetaAndPrint.Table State={}", state); 392 } else { 393 RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r); 394 if (locations == null) { 395 return true; 396 } 397 for (HRegionLocation loc : locations.getRegionLocations()) { 398 if (loc != null) { 399 LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion()); 400 } 401 } 402 } 403 return true; 404 }; 405 scanMeta(connection, null, null, QueryType.ALL, v); 406 } 407 408 public static void scanMetaForTableRegions(Connection connection, 409 ClientMetaTableAccessor.Visitor visitor, TableName tableName) throws IOException { 410 scanMeta(connection, tableName, QueryType.REGION, Integer.MAX_VALUE, visitor); 411 } 412 413 private static void scanMeta(Connection connection, TableName table, QueryType type, int maxRows, 414 final ClientMetaTableAccessor.Visitor visitor) throws IOException { 415 scanMeta(connection, ClientMetaTableAccessor.getTableStartRowForMeta(table, type), 416 ClientMetaTableAccessor.getTableStopRowForMeta(table, type), type, maxRows, visitor); 417 } 418 419 public static void scanMeta(Connection connection, @Nullable final byte[] startRow, 420 @Nullable final byte[] stopRow, QueryType type, final ClientMetaTableAccessor.Visitor visitor) 421 throws IOException { 422 scanMeta(connection, startRow, stopRow, type, Integer.MAX_VALUE, visitor); 423 } 424 425 /** 426 * Performs a scan of META table for given table starting from given row. 427 * @param connection connection we're using 428 * @param visitor visitor to call 429 * @param tableName table withing we scan 430 * @param row start scan from this row 431 * @param rowLimit max number of rows to return 432 */ 433 public static void scanMeta(Connection connection, final ClientMetaTableAccessor.Visitor visitor, 434 final TableName tableName, final byte[] row, final int rowLimit) throws IOException { 435 byte[] startRow = null; 436 byte[] stopRow = null; 437 if (tableName != null) { 438 startRow = ClientMetaTableAccessor.getTableStartRowForMeta(tableName, QueryType.REGION); 439 if (row != null) { 440 RegionInfo closestRi = getClosestRegionInfo(connection, tableName, row); 441 startRow = 442 RegionInfo.createRegionName(tableName, closestRi.getStartKey(), HConstants.ZEROES, false); 443 } 444 stopRow = ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION); 445 } 446 scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor); 447 } 448 449 /** 450 * Performs a scan of META table. 451 * @param connection connection we're using 452 * @param startRow Where to start the scan. Pass null if want to begin scan at first row. 453 * @param stopRow Where to stop the scan. Pass null if want to scan all rows from the start one 454 * @param type scanned part of meta 455 * @param maxRows maximum rows to return 456 * @param visitor Visitor invoked against each row. 457 */ 458 public static void scanMeta(Connection connection, @Nullable final byte[] startRow, 459 @Nullable final byte[] stopRow, QueryType type, int maxRows, 460 final ClientMetaTableAccessor.Visitor visitor) throws IOException { 461 scanMeta(connection, startRow, stopRow, type, null, maxRows, visitor); 462 } 463 464 /** 465 * Performs a scan of META table. 466 * @param connection connection we're using 467 * @param startRow Where to start the scan. Pass null if want to begin scan at first row. 468 * @param stopRow Where to stop the scan. Pass null if want to scan all rows from the start one 469 * @param type scanned part of meta 470 * @param maxRows maximum rows to return 471 * @param visitor Visitor invoked against each row. 472 */ 473 public static void scanMeta(Connection connection, @Nullable final byte[] startRow, 474 @Nullable final byte[] stopRow, QueryType type, @Nullable Filter filter, int maxRows, 475 final ClientMetaTableAccessor.Visitor visitor) throws IOException { 476 int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE; 477 Scan scan = getMetaScan(connection.getConfiguration(), rowUpperLimit); 478 479 for (byte[] family : type.getFamilies()) { 480 scan.addFamily(family); 481 } 482 if (startRow != null) { 483 scan.withStartRow(startRow); 484 } 485 if (stopRow != null) { 486 scan.withStopRow(stopRow); 487 } 488 if (filter != null) { 489 scan.setFilter(filter); 490 } 491 492 if (LOG.isTraceEnabled()) { 493 LOG.trace( 494 "Scanning META starting at row={} stopping at row={} for max={} with caching={} " 495 + "priority={}", 496 Bytes.toStringBinary(startRow), Bytes.toStringBinary(stopRow), rowUpperLimit, 497 scan.getCaching(), scan.getPriority()); 498 } 499 500 int currentRow = 0; 501 try (Table metaTable = getMetaHTable(connection)) { 502 try (ResultScanner scanner = metaTable.getScanner(scan)) { 503 Result data; 504 while ((data = scanner.next()) != null) { 505 if (data.isEmpty()) { 506 continue; 507 } 508 // Break if visit returns false. 509 if (!visitor.visit(data)) { 510 break; 511 } 512 if (++currentRow >= rowUpperLimit) { 513 break; 514 } 515 } 516 } 517 } 518 if (visitor instanceof Closeable) { 519 try { 520 ((Closeable) visitor).close(); 521 } catch (Throwable t) { 522 ExceptionUtil.rethrowIfInterrupt(t); 523 LOG.debug("Got exception in closing the meta scanner visitor", t); 524 } 525 } 526 } 527 528 /** Returns Get closest metatable region row to passed <code>row</code> */ 529 @NonNull 530 private static RegionInfo getClosestRegionInfo(Connection connection, 531 @NonNull final TableName tableName, @NonNull final byte[] row) throws IOException { 532 byte[] searchRow = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false); 533 Scan scan = getMetaScan(connection.getConfiguration(), 1); 534 scan.setReversed(true); 535 scan.withStartRow(searchRow); 536 try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) { 537 Result result = resultScanner.next(); 538 if (result == null) { 539 throw new TableNotFoundException("Cannot find row in META " + " for table: " + tableName 540 + ", row=" + Bytes.toStringBinary(row)); 541 } 542 RegionInfo regionInfo = CatalogFamilyFormat.getRegionInfo(result); 543 if (regionInfo == null) { 544 throw new IOException("RegionInfo was null or empty in Meta for " + tableName + ", row=" 545 + Bytes.toStringBinary(row)); 546 } 547 return regionInfo; 548 } 549 } 550 551 /** 552 * Returns the {@link ServerName} from catalog table {@link Result} where the region is 553 * transitioning on. It should be the same as 554 * {@link CatalogFamilyFormat#getServerName(Result,int)} if the server is at OPEN state. 555 * @param r Result to pull the transitioning server name from 556 * @return A ServerName instance or {@link CatalogFamilyFormat#getServerName(Result,int)} if 557 * necessary fields not found or empty. 558 */ 559 @Nullable 560 public static ServerName getTargetServerName(final Result r, final int replicaId) { 561 final Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY, 562 CatalogFamilyFormat.getServerNameColumn(replicaId)); 563 if (cell == null || cell.getValueLength() == 0) { 564 RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r); 565 if (locations != null) { 566 HRegionLocation location = locations.getRegionLocation(replicaId); 567 if (location != null) { 568 return location.getServerName(); 569 } 570 } 571 return null; 572 } 573 return ServerName.parseServerName( 574 Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())); 575 } 576 577 /** 578 * Returns the daughter regions by reading the corresponding columns of the catalog table Result. 579 * @param data a Result object from the catalog table scan 580 * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent 581 */ 582 public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) { 583 RegionInfo splitA = CatalogFamilyFormat.getRegionInfo(data, HConstants.SPLITA_QUALIFIER); 584 RegionInfo splitB = CatalogFamilyFormat.getRegionInfo(data, HConstants.SPLITB_QUALIFIER); 585 return new PairOfSameType<>(splitA, splitB); 586 } 587 588 /** 589 * Fetch table state for given table from META table 590 * @param conn connection to use 591 * @param tableName table to fetch state for 592 */ 593 @Nullable 594 public static TableState getTableState(Connection conn, TableName tableName) throws IOException { 595 if (tableName.equals(TableName.META_TABLE_NAME)) { 596 return new TableState(tableName, TableState.State.ENABLED); 597 } 598 Table metaHTable = getMetaHTable(conn); 599 Get get = new Get(tableName.getName()).addColumn(HConstants.TABLE_FAMILY, 600 HConstants.TABLE_STATE_QUALIFIER); 601 Result result = metaHTable.get(get); 602 return CatalogFamilyFormat.getTableState(result); 603 } 604 605 /** 606 * Fetch table states from META table 607 * @param conn connection to use 608 * @return map {tableName -> state} 609 */ 610 public static Map<TableName, TableState> getTableStates(Connection conn) throws IOException { 611 final Map<TableName, TableState> states = new LinkedHashMap<>(); 612 ClientMetaTableAccessor.Visitor collector = r -> { 613 TableState state = CatalogFamilyFormat.getTableState(r); 614 if (state != null) { 615 states.put(state.getTableName(), state); 616 } 617 return true; 618 }; 619 fullScanTables(conn, collector); 620 return states; 621 } 622 623 /** 624 * Updates state in META Do not use. For internal use only. 625 * @param conn connection to use 626 * @param tableName table to look for 627 */ 628 public static void updateTableState(Connection conn, TableName tableName, TableState.State actual) 629 throws IOException { 630 updateTableState(conn, new TableState(tableName, actual)); 631 } 632 633 //////////////////////// 634 // Editing operations // 635 //////////////////////// 636 637 /** 638 * Generates and returns a {@link Put} containing the {@link RegionInfo} for the catalog table. 639 * @throws IllegalArgumentException when the provided RegionInfo is not the default replica. 640 */ 641 public static Put makePutFromRegionInfo(RegionInfo regionInfo) throws IOException { 642 return makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime()); 643 } 644 645 /** 646 * Generates and returns a {@link Put} containing the {@link RegionInfo} for the catalog table. 647 * @throws IllegalArgumentException when the provided RegionInfo is not the default replica. 648 */ 649 public static Put makePutFromRegionInfo(RegionInfo regionInfo, long ts) throws IOException { 650 byte[] metaKeyForRegion = CatalogFamilyFormat.getMetaKeyForRegion(regionInfo); 651 try { 652 Put put = new Put(metaKeyForRegion, ts); 653 return addRegionInfo(put, regionInfo); 654 } catch (IllegalArgumentException ex) { 655 LOG.error( 656 "Got exception while creating put for regioninfo {}." + "meta key for regioninfo is {}", 657 regionInfo.getRegionNameAsString(), metaKeyForRegion); 658 throw ex; 659 } 660 } 661 662 /** 663 * Generates and returns a Delete containing the region info for the catalog table 664 */ 665 public static Delete makeDeleteFromRegionInfo(RegionInfo regionInfo, long ts) { 666 if (regionInfo == null) { 667 throw new IllegalArgumentException("Can't make a delete for null region"); 668 } 669 if (regionInfo.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) { 670 throw new IllegalArgumentException( 671 "Can't make delete for a replica region. Operate on the primary"); 672 } 673 Delete delete = new Delete(CatalogFamilyFormat.getMetaKeyForRegion(regionInfo)); 674 delete.addFamily(HConstants.CATALOG_FAMILY, ts); 675 return delete; 676 } 677 678 /** 679 * Adds split daughters to the Put 680 */ 681 public static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB) 682 throws IOException { 683 if (splitA != null) { 684 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow()) 685 .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.SPLITA_QUALIFIER) 686 .setTimestamp(put.getTimestamp()).setType(Type.Put).setValue(RegionInfo.toByteArray(splitA)) 687 .build()); 688 } 689 if (splitB != null) { 690 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow()) 691 .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.SPLITB_QUALIFIER) 692 .setTimestamp(put.getTimestamp()).setType(Type.Put).setValue(RegionInfo.toByteArray(splitB)) 693 .build()); 694 } 695 return put; 696 } 697 698 /** 699 * Put the passed <code>p</code> to the <code>hbase:meta</code> table. 700 * @param connection connection we're using 701 * @param p Put to add to hbase:meta 702 */ 703 private static void putToMetaTable(Connection connection, Put p) throws IOException { 704 try (Table table = getMetaHTable(connection)) { 705 put(table, p); 706 } 707 } 708 709 /** 710 * @param t Table to use 711 * @param p put to make 712 */ 713 private static void put(Table t, Put p) throws IOException { 714 debugLogMutation(p); 715 t.put(p); 716 } 717 718 /** 719 * Put the passed <code>ps</code> to the <code>hbase:meta</code> table. 720 * @param connection connection we're using 721 * @param ps Put to add to hbase:meta 722 */ 723 public static void putsToMetaTable(final Connection connection, final List<Put> ps) 724 throws IOException { 725 if (ps.isEmpty()) { 726 return; 727 } 728 try (Table t = getMetaHTable(connection)) { 729 debugLogMutations(ps); 730 // the implementation for putting a single Put is much simpler so here we do a check first. 731 if (ps.size() == 1) { 732 t.put(ps.get(0)); 733 } else { 734 t.put(ps); 735 } 736 } 737 } 738 739 /** 740 * Delete the passed <code>d</code> from the <code>hbase:meta</code> table. 741 * @param connection connection we're using 742 * @param d Delete to add to hbase:meta 743 */ 744 private static void deleteFromMetaTable(final Connection connection, final Delete d) 745 throws IOException { 746 List<Delete> dels = new ArrayList<>(1); 747 dels.add(d); 748 deleteFromMetaTable(connection, dels); 749 } 750 751 /** 752 * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table. 753 * @param connection connection we're using 754 * @param deletes Deletes to add to hbase:meta This list should support #remove. 755 */ 756 private static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes) 757 throws IOException { 758 try (Table t = getMetaHTable(connection)) { 759 debugLogMutations(deletes); 760 t.delete(deletes); 761 } 762 } 763 764 /** 765 * Set the column value corresponding to this {@code replicaId}'s {@link RegionState} to the 766 * provided {@code state}. Mutates the provided {@link Put}. 767 */ 768 public static Put addRegionStateToPut(Put put, int replicaId, RegionState.State state) 769 throws IOException { 770 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow()) 771 .setFamily(HConstants.CATALOG_FAMILY) 772 .setQualifier(CatalogFamilyFormat.getRegionStateColumn(replicaId)) 773 .setTimestamp(put.getTimestamp()).setType(Cell.Type.Put).setValue(Bytes.toBytes(state.name())) 774 .build()); 775 return put; 776 } 777 778 /** 779 * Update state column in hbase:meta. 780 */ 781 public static void updateRegionState(Connection connection, RegionInfo ri, 782 RegionState.State state) throws IOException { 783 final Put put = makePutFromRegionInfo(ri); 784 addRegionStateToPut(put, ri.getReplicaId(), state); 785 putsToMetaTable(connection, Collections.singletonList(put)); 786 } 787 788 /** 789 * Adds daughter region infos to hbase:meta row for the specified region. 790 * <p/> 791 * Note that this does not add its daughter's as different rows, but adds information about the 792 * daughters in the same row as the parent. Now only used in snapshot. Use 793 * {@link org.apache.hadoop.hbase.master.assignment.RegionStateStore} if you want to split a 794 * region. 795 * @param connection connection we're using 796 * @param regionInfo RegionInfo of parent region 797 * @param splitA first split daughter of the parent regionInfo 798 * @param splitB second split daughter of the parent regionInfo 799 * @throws IOException if problem connecting or updating meta 800 */ 801 public static void addSplitsToParent(Connection connection, RegionInfo regionInfo, 802 RegionInfo splitA, RegionInfo splitB) throws IOException { 803 try (Table meta = getMetaHTable(connection)) { 804 Put put = makePutFromRegionInfo(regionInfo); 805 addDaughtersToPut(put, splitA, splitB); 806 meta.put(put); 807 debugLogMutation(put); 808 LOG.debug("Added region {}", regionInfo.getRegionNameAsString()); 809 } 810 } 811 812 /** 813 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is 814 * CLOSED. 815 * @param connection connection we're using 816 * @param regionInfos region information list 817 * @throws IOException if problem connecting or updating meta 818 */ 819 public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos, 820 int regionReplication) throws IOException { 821 addRegionsToMeta(connection, regionInfos, regionReplication, 822 EnvironmentEdgeManager.currentTime()); 823 } 824 825 /** 826 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is 827 * CLOSED. 828 * @param connection connection we're using 829 * @param regionInfos region information list 830 * @param ts desired timestamp 831 * @throws IOException if problem connecting or updating meta 832 */ 833 public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos, 834 int regionReplication, long ts) throws IOException { 835 List<Put> puts = new ArrayList<>(); 836 for (RegionInfo regionInfo : regionInfos) { 837 if (!RegionReplicaUtil.isDefaultReplica(regionInfo)) { 838 continue; 839 } 840 Put put = makePutFromRegionInfo(regionInfo, ts); 841 // New regions are added with initial state of CLOSED. 842 addRegionStateToPut(put, regionInfo.getReplicaId(), RegionState.State.CLOSED); 843 // Add empty locations for region replicas so that number of replicas can be cached 844 // whenever the primary region is looked up from meta 845 for (int i = 1; i < regionReplication; i++) { 846 addEmptyLocation(put, i); 847 } 848 puts.add(put); 849 } 850 putsToMetaTable(connection, puts); 851 LOG.info("Added {} regions to meta.", puts.size()); 852 } 853 854 /** 855 * Update state of the table in meta. 856 * @param connection what we use for update 857 * @param state new state 858 */ 859 private static void updateTableState(Connection connection, TableState state) throws IOException { 860 Put put = makePutFromTableState(state, EnvironmentEdgeManager.currentTime()); 861 putToMetaTable(connection, put); 862 LOG.info("Updated {} in hbase:meta", state); 863 } 864 865 /** 866 * Construct PUT for given state 867 * @param state new state 868 */ 869 public static Put makePutFromTableState(TableState state, long ts) { 870 Put put = new Put(state.getTableName().getName(), ts); 871 put.addColumn(HConstants.TABLE_FAMILY, HConstants.TABLE_STATE_QUALIFIER, 872 state.convert().toByteArray()); 873 return put; 874 } 875 876 /** 877 * Remove state for table from meta 878 * @param connection to use for deletion 879 * @param table to delete state for 880 */ 881 public static void deleteTableState(Connection connection, TableName table) throws IOException { 882 long time = EnvironmentEdgeManager.currentTime(); 883 Delete delete = new Delete(table.getName()); 884 delete.addColumns(HConstants.TABLE_FAMILY, HConstants.TABLE_STATE_QUALIFIER, time); 885 deleteFromMetaTable(connection, delete); 886 LOG.info("Deleted table " + table + " state from META"); 887 } 888 889 /** 890 * Updates the location of the specified region in hbase:meta to be the specified server hostname 891 * and startcode. 892 * <p> 893 * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes 894 * edits to that region. 895 * @param connection connection we're using 896 * @param regionInfo region to update location of 897 * @param openSeqNum the latest sequence number obtained when the region was open 898 * @param sn Server name 899 * @param masterSystemTime wall clock time from master if passed in the open region RPC 900 */ 901 public static void updateRegionLocation(Connection connection, RegionInfo regionInfo, 902 ServerName sn, long openSeqNum, long masterSystemTime) throws IOException { 903 updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime); 904 } 905 906 /** 907 * Updates the location of the specified region to be the specified server. 908 * <p> 909 * Connects to the specified server which should be hosting the specified catalog region name to 910 * perform the edit. 911 * @param connection connection we're using 912 * @param regionInfo region to update location of 913 * @param sn Server name 914 * @param openSeqNum the latest sequence number obtained when the region was open 915 * @param masterSystemTime wall clock time from master if passed in the open region RPC 916 * @throws IOException In particular could throw {@link java.net.ConnectException} if the server 917 * is down on other end. 918 */ 919 private static void updateLocation(Connection connection, RegionInfo regionInfo, ServerName sn, 920 long openSeqNum, long masterSystemTime) throws IOException { 921 // region replicas are kept in the primary region's row 922 Put put = new Put(CatalogFamilyFormat.getMetaKeyForRegion(regionInfo), masterSystemTime); 923 addRegionInfo(put, regionInfo); 924 addLocation(put, sn, openSeqNum, regionInfo.getReplicaId()); 925 putToMetaTable(connection, put); 926 LOG.info("Updated row {} with server = {}", regionInfo.getRegionNameAsString(), sn); 927 } 928 929 public static Put addRegionInfo(final Put p, final RegionInfo hri) throws IOException { 930 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(p.getRow()) 931 .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.REGIONINFO_QUALIFIER) 932 .setTimestamp(p.getTimestamp()).setType(Type.Put) 933 // Serialize the Default Replica HRI otherwise scan of hbase:meta 934 // shows an info:regioninfo value with encoded name and region 935 // name that differs from that of the hbase;meta row. 936 .setValue(RegionInfo.toByteArray(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri))) 937 .build()); 938 return p; 939 } 940 941 public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId) 942 throws IOException { 943 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 944 return p 945 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 946 .setQualifier(CatalogFamilyFormat.getServerColumn(replicaId)).setTimestamp(p.getTimestamp()) 947 .setType(Cell.Type.Put).setValue(Bytes.toBytes(sn.getAddress().toString())).build()) 948 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 949 .setQualifier(CatalogFamilyFormat.getStartCodeColumn(replicaId)) 950 .setTimestamp(p.getTimestamp()).setType(Cell.Type.Put) 951 .setValue(Bytes.toBytes(sn.getStartCode())).build()) 952 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 953 .setQualifier(CatalogFamilyFormat.getSeqNumColumn(replicaId)).setTimestamp(p.getTimestamp()) 954 .setType(Type.Put).setValue(Bytes.toBytes(openSeqNum)).build()); 955 } 956 957 public static Put addEmptyLocation(Put p, int replicaId) throws IOException { 958 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 959 return p 960 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 961 .setQualifier(CatalogFamilyFormat.getServerColumn(replicaId)).setTimestamp(p.getTimestamp()) 962 .setType(Type.Put).build()) 963 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 964 .setQualifier(CatalogFamilyFormat.getStartCodeColumn(replicaId)) 965 .setTimestamp(p.getTimestamp()).setType(Cell.Type.Put).build()) 966 .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY) 967 .setQualifier(CatalogFamilyFormat.getSeqNumColumn(replicaId)).setTimestamp(p.getTimestamp()) 968 .setType(Cell.Type.Put).build()); 969 } 970 971 private static void debugLogMutations(List<? extends Mutation> mutations) throws IOException { 972 if (!METALOG.isDebugEnabled()) { 973 return; 974 } 975 // Logging each mutation in separate line makes it easier to see diff between them visually 976 // because of common starting indentation. 977 for (Mutation mutation : mutations) { 978 debugLogMutation(mutation); 979 } 980 } 981 982 private static void debugLogMutation(Mutation p) throws IOException { 983 METALOG.debug("{} {}", p.getClass().getSimpleName(), p.toJSON()); 984 } 985}