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.Arrays; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.LinkedHashMap; 029import java.util.List; 030import java.util.Map; 031import java.util.NavigableMap; 032import java.util.Set; 033import java.util.SortedMap; 034import java.util.TreeMap; 035import java.util.regex.Matcher; 036import java.util.regex.Pattern; 037import java.util.stream.Collectors; 038 039import org.apache.hadoop.conf.Configuration; 040import org.apache.hadoop.hbase.Cell.Type; 041import org.apache.hadoop.hbase.client.Connection; 042import org.apache.hadoop.hbase.client.ConnectionFactory; 043import org.apache.hadoop.hbase.client.Consistency; 044import org.apache.hadoop.hbase.client.Delete; 045import org.apache.hadoop.hbase.client.Get; 046import org.apache.hadoop.hbase.client.Mutation; 047import org.apache.hadoop.hbase.client.Put; 048import org.apache.hadoop.hbase.client.RegionInfo; 049import org.apache.hadoop.hbase.client.RegionInfoBuilder; 050import org.apache.hadoop.hbase.client.RegionLocator; 051import org.apache.hadoop.hbase.client.RegionReplicaUtil; 052import org.apache.hadoop.hbase.client.RegionServerCallable; 053import org.apache.hadoop.hbase.client.Result; 054import org.apache.hadoop.hbase.client.ResultScanner; 055import org.apache.hadoop.hbase.client.Scan; 056import org.apache.hadoop.hbase.client.Table; 057import org.apache.hadoop.hbase.client.TableState; 058import org.apache.hadoop.hbase.exceptions.DeserializationException; 059import org.apache.hadoop.hbase.filter.QualifierFilter; 060import org.apache.hadoop.hbase.filter.RegexStringComparator; 061import org.apache.hadoop.hbase.filter.RowFilter; 062import org.apache.hadoop.hbase.filter.SubstringComparator; 063import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel; 064import org.apache.hadoop.hbase.master.RegionState; 065import org.apache.hadoop.hbase.protobuf.ProtobufUtil; 066import org.apache.hadoop.hbase.protobuf.generated.ClientProtos; 067import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier; 068import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType; 069import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos; 070import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest; 071import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsResponse; 072import org.apache.hadoop.hbase.util.Bytes; 073import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 074import org.apache.hadoop.hbase.util.ExceptionUtil; 075import org.apache.hadoop.hbase.util.Pair; 076import org.apache.hadoop.hbase.util.PairOfSameType; 077import org.apache.yetus.audience.InterfaceAudience; 078import org.slf4j.Logger; 079import org.slf4j.LoggerFactory; 080 081import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting; 082 083/** 084 * <p> 085 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored 086 * to <code>hbase:meta</code>. 087 * </p> 088 * <p> 089 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is 090 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection 091 * (opened before each operation, closed right after), while when used on HM or HRS (like in 092 * AssignmentManager) we want permanent connection. 093 * </p> 094 * <p> 095 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table 096 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is 097 * called default replica. 098 * </p> 099 * <p> 100 * <h2>Meta layout</h2> 101 * 102 * <pre> 103 * For each table there is single row named for the table with a 'table' column family. 104 * The column family currently has one column in it, the 'state' column: 105 * 106 * table:state => contains table state 107 * 108 * Then for each table range ('Region'), there is a single row, formatted as: 109 * <tableName>,<startKey>,<regionId>,<encodedRegionName>. 110 * This row is the serialized regionName of the default region replica. 111 * Columns are: 112 * info:regioninfo => contains serialized HRI for the default region replica 113 * info:server => contains hostname:port (in string form) for the server hosting 114 * the default regionInfo replica 115 * info:server_<replicaId> => contains hostname:port (in string form) for the server hosting 116 * the regionInfo replica with replicaId 117 * info:serverstartcode => contains server start code (in binary long form) for the server 118 * hosting the default regionInfo replica 119 * info:serverstartcode_<replicaId> => contains server start code (in binary long form) for 120 * the server hosting the regionInfo replica with 121 * replicaId 122 * info:seqnumDuringOpen => contains seqNum (in binary long form) for the region at the time 123 * the server opened the region with default replicaId 124 * info:seqnumDuringOpen_<replicaId> => contains seqNum (in binary long form) for the region 125 * at the time the server opened the region with 126 * replicaId 127 * info:splitA => contains a serialized HRI for the first daughter region if the 128 * region is split 129 * info:splitB => contains a serialized HRI for the second daughter region if the 130 * region is split 131 * info:merge* => contains a serialized HRI for a merge parent region. There will be two 132 * or more of these columns in a row. A row that has these columns is 133 * undergoing a merge and is the result of the merge. Columns listed 134 * in marge* columns are the parents of this merged region. Example 135 * columns: info:merge0001, info:merge0002. You make also see 'mergeA', 136 * and 'mergeB'. This is old form replaced by the new format that allows 137 * for more than two parents to be merged at a time. 138 * TODO: Add rep_barrier for serial replication explaination. 139 * </pre> 140 * </p> 141 * <p> 142 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not 143 * leak out of it (through Result objects, etc) 144 * </p> 145 */ 146@InterfaceAudience.Private 147public class MetaTableAccessor { 148 149 private static final Logger LOG = LoggerFactory.getLogger(MetaTableAccessor.class); 150 private static final Logger METALOG = LoggerFactory.getLogger("org.apache.hadoop.hbase.META"); 151 152 /** 153 * Lists all of the table regions currently in META. 154 * Deprecated, keep there until some test use this. 155 * @param connection what we will use 156 * @param tableName table to list 157 * @return Map of all user-space regions to servers 158 * @deprecated use {@link #getTableRegionsAndLocations}, region can have multiple locations 159 */ 160 @Deprecated 161 public static NavigableMap<RegionInfo, ServerName> allTableRegions( 162 Connection connection, final TableName tableName) throws IOException { 163 final NavigableMap<RegionInfo, ServerName> regions = new TreeMap<>(); 164 Visitor visitor = new TableVisitorBase(tableName) { 165 @Override 166 public boolean visitInternal(Result result) throws IOException { 167 RegionLocations locations = getRegionLocations(result); 168 if (locations == null) return true; 169 for (HRegionLocation loc : locations.getRegionLocations()) { 170 if (loc != null) { 171 RegionInfo regionInfo = loc.getRegionInfo(); 172 regions.put(regionInfo, loc.getServerName()); 173 } 174 } 175 return true; 176 } 177 }; 178 scanMetaForTableRegions(connection, visitor, tableName); 179 return regions; 180 } 181 @VisibleForTesting 182 public static final byte[] REPLICATION_PARENT_QUALIFIER = Bytes.toBytes("parent"); 183 184 private static final byte ESCAPE_BYTE = (byte) 0xFF; 185 186 private static final byte SEPARATED_BYTE = 0x00; 187 188 @InterfaceAudience.Private 189 public enum QueryType { 190 ALL(HConstants.TABLE_FAMILY, HConstants.CATALOG_FAMILY), 191 REGION(HConstants.CATALOG_FAMILY), 192 TABLE(HConstants.TABLE_FAMILY); 193 194 private final byte[][] families; 195 196 QueryType(byte[]... families) { 197 this.families = families; 198 } 199 200 byte[][] getFamilies() { 201 return this.families; 202 } 203 } 204 205 /** The delimiter for meta columns for replicaIds > 0 */ 206 static final char META_REPLICA_ID_DELIMITER = '_'; 207 208 /** A regex for parsing server columns from meta. See above javadoc for meta layout */ 209 private static final Pattern SERVER_COLUMN_PATTERN 210 = Pattern.compile("^server(_[0-9a-fA-F]{4})?$"); 211 212 //////////////////////// 213 // Reading operations // 214 //////////////////////// 215 216 /** 217 * Performs a full scan of <code>hbase:meta</code> for regions. 218 * @param connection connection we're using 219 * @param visitor Visitor invoked against each row in regions family. 220 */ 221 public static void fullScanRegions(Connection connection, final Visitor visitor) 222 throws IOException { 223 scanMeta(connection, null, null, QueryType.REGION, visitor); 224 } 225 226 /** 227 * Performs a full scan of <code>hbase:meta</code> for regions. 228 * @param connection connection we're using 229 */ 230 public static List<Result> fullScanRegions(Connection connection) throws IOException { 231 return fullScan(connection, QueryType.REGION); 232 } 233 234 /** 235 * Performs a full scan of <code>hbase:meta</code> for tables. 236 * @param connection connection we're using 237 * @param visitor Visitor invoked against each row in tables family. 238 */ 239 public static void fullScanTables(Connection connection, final Visitor visitor) 240 throws IOException { 241 scanMeta(connection, null, null, QueryType.TABLE, visitor); 242 } 243 244 /** 245 * Performs a full scan of <code>hbase:meta</code>. 246 * @param connection connection we're using 247 * @param type scanned part of meta 248 * @return List of {@link Result} 249 */ 250 private static List<Result> fullScan(Connection connection, QueryType type) throws IOException { 251 CollectAllVisitor v = new CollectAllVisitor(); 252 scanMeta(connection, null, null, type, v); 253 return v.getResults(); 254 } 255 256 /** 257 * Callers should call close on the returned {@link Table} instance. 258 * @param connection connection we're using to access Meta 259 * @return An {@link Table} for <code>hbase:meta</code> 260 */ 261 public static Table getMetaHTable(final Connection connection) 262 throws IOException { 263 // We used to pass whole CatalogTracker in here, now we just pass in Connection 264 if (connection == null) { 265 throw new NullPointerException("No connection"); 266 } else if (connection.isClosed()) { 267 throw new IOException("connection is closed"); 268 } 269 return connection.getTable(TableName.META_TABLE_NAME); 270 } 271 272 /** 273 * @param t Table to use (will be closed when done). 274 * @param g Get to run 275 */ 276 private static Result get(final Table t, final Get g) throws IOException { 277 if (t == null) return null; 278 try { 279 return t.get(g); 280 } finally { 281 t.close(); 282 } 283 } 284 285 /** 286 * Gets the region info and assignment for the specified region. 287 * @param connection connection we're using 288 * @param regionName Region to lookup. 289 * @return Location and RegionInfo for <code>regionName</code> 290 * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead 291 */ 292 @Deprecated 293 public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte [] regionName) 294 throws IOException { 295 HRegionLocation location = getRegionLocation(connection, regionName); 296 return location == null 297 ? null 298 : new Pair<>(location.getRegionInfo(), location.getServerName()); 299 } 300 301 /** 302 * Returns the HRegionLocation from meta for the given region 303 * @param connection connection we're using 304 * @param regionName region we're looking for 305 * @return HRegionLocation for the given region 306 */ 307 public static HRegionLocation getRegionLocation(Connection connection, byte[] regionName) 308 throws IOException { 309 byte[] row = regionName; 310 RegionInfo parsedInfo = null; 311 try { 312 parsedInfo = parseRegionInfoFromRegionName(regionName); 313 row = getMetaKeyForRegion(parsedInfo); 314 } catch (Exception parseEx) { 315 // Ignore. This is used with tableName passed as regionName. 316 } 317 Get get = new Get(row); 318 get.addFamily(HConstants.CATALOG_FAMILY); 319 Result r = get(getMetaHTable(connection), get); 320 RegionLocations locations = getRegionLocations(r); 321 return locations == null ? null 322 : locations.getRegionLocation(parsedInfo == null ? 0 : parsedInfo.getReplicaId()); 323 } 324 325 /** 326 * Returns the HRegionLocation from meta for the given region 327 * @param connection connection we're using 328 * @param regionInfo region information 329 * @return HRegionLocation for the given region 330 */ 331 public static HRegionLocation getRegionLocation(Connection connection, RegionInfo regionInfo) 332 throws IOException { 333 byte[] row = getMetaKeyForRegion(regionInfo); 334 Get get = new Get(row); 335 get.addFamily(HConstants.CATALOG_FAMILY); 336 Result r = get(getMetaHTable(connection), get); 337 return getRegionLocation(r, regionInfo, regionInfo.getReplicaId()); 338 } 339 340 /** Returns the row key to use for this regionInfo */ 341 public static byte[] getMetaKeyForRegion(RegionInfo regionInfo) { 342 return RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo).getRegionName(); 343 } 344 345 /** Returns an HRI parsed from this regionName. Not all the fields of the HRI 346 * is stored in the name, so the returned object should only be used for the fields 347 * in the regionName. 348 */ 349 public static RegionInfo parseRegionInfoFromRegionName(byte[] regionName) throws IOException { 350 byte[][] fields = RegionInfo.parseRegionName(regionName); 351 long regionId = Long.parseLong(Bytes.toString(fields[2])); 352 int replicaId = fields.length > 3 ? Integer.parseInt(Bytes.toString(fields[3]), 16) : 0; 353 return RegionInfoBuilder.newBuilder(TableName.valueOf(fields[0])) 354 .setStartKey(fields[1]) 355 .setEndKey(fields[2]) 356 .setSplit(false) 357 .setRegionId(regionId) 358 .setReplicaId(replicaId) 359 .build(); 360 } 361 362 /** 363 * Gets the result in hbase:meta for the specified region. 364 * @param connection connection we're using 365 * @param regionName region we're looking for 366 * @return result of the specified region 367 */ 368 public static Result getRegionResult(Connection connection, 369 byte[] regionName) throws IOException { 370 Get get = new Get(regionName); 371 get.addFamily(HConstants.CATALOG_FAMILY); 372 return get(getMetaHTable(connection), get); 373 } 374 375 /** 376 * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>, 377 * returning a single related <code>Result</code> instance if any row is found, null otherwise. 378 * 379 * @param connection the connection to query META table. 380 * @param regionEncodedName the region encoded name to look for at META. 381 * @return <code>Result</code> instance with the row related info in META, null otherwise. 382 * @throws IOException if any errors occur while querying META. 383 */ 384 public static Result scanByRegionEncodedName(Connection connection, 385 String regionEncodedName) throws IOException { 386 RowFilter rowFilter = new RowFilter(CompareOperator.EQUAL, 387 new SubstringComparator(regionEncodedName)); 388 Scan scan = getMetaScan(connection, 1); 389 scan.setFilter(rowFilter); 390 ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan); 391 return resultScanner.next(); 392 } 393 394 /** 395 * @return Return all regioninfos listed in the 'info:merge*' columns of 396 * the <code>regionName</code> row. 397 */ 398 @Nullable 399 public static List<RegionInfo> getMergeRegions(Connection connection, byte[] regionName) 400 throws IOException { 401 return getMergeRegions(getMergeRegionsRaw(connection, regionName)); 402 } 403 404 /** 405 * @return Deserialized regioninfo values taken from column values that match 406 * the regex 'info:merge.*' in array of <code>cells</code>. 407 */ 408 @Nullable 409 public static List<RegionInfo> getMergeRegions(Cell [] cells) { 410 if (cells == null) { 411 return null; 412 } 413 List<RegionInfo> regionsToMerge = null; 414 for (Cell cell: cells) { 415 if (!isMergeQualifierPrefix(cell)) { 416 continue; 417 } 418 // Ok. This cell is that of a info:merge* column. 419 RegionInfo ri = RegionInfo.parseFromOrNull(cell.getValueArray(), cell.getValueOffset(), 420 cell.getValueLength()); 421 if (ri != null) { 422 if (regionsToMerge == null) { 423 regionsToMerge = new ArrayList<>(); 424 } 425 regionsToMerge.add(ri); 426 } 427 } 428 return regionsToMerge; 429 } 430 431 /** 432 * @return True if any merge regions present in <code>cells</code>; i.e. 433 * the column in <code>cell</code> matches the regex 'info:merge.*'. 434 */ 435 public static boolean hasMergeRegions(Cell [] cells) { 436 for (Cell cell: cells) { 437 if (!isMergeQualifierPrefix(cell)) { 438 continue; 439 } 440 return true; 441 } 442 return false; 443 } 444 445 /** 446 * @return True if the column in <code>cell</code> matches the regex 'info:merge.*'. 447 */ 448 private static boolean isMergeQualifierPrefix(Cell cell) { 449 // Check to see if has family and that qualifier starts with the merge qualifier 'merge' 450 return CellUtil.matchingFamily(cell, HConstants.CATALOG_FAMILY) && 451 PrivateCellUtil.qualifierStartsWith(cell, HConstants.MERGE_QUALIFIER_PREFIX); 452 } 453 454 /** 455 * @return Array of Cells made from all columns on the <code>regionName</code> row 456 * that match the regex 'info:merge.*'. 457 */ 458 @Nullable 459 private static Cell [] getMergeRegionsRaw(Connection connection, byte [] regionName) 460 throws IOException { 461 Scan scan = new Scan().withStartRow(regionName). 462 setOneRowLimit(). 463 readVersions(1). 464 addFamily(HConstants.CATALOG_FAMILY). 465 setFilter(new QualifierFilter(CompareOperator.EQUAL, 466 new RegexStringComparator(HConstants.MERGE_QUALIFIER_PREFIX_STR+ ".*"))); 467 try (Table m = getMetaHTable(connection); ResultScanner scanner = m.getScanner(scan)) { 468 // Should be only one result in this scanner if any. 469 Result result = scanner.next(); 470 if (result == null) { 471 return null; 472 } 473 // Should be safe to just return all Cells found since we had filter in place. 474 // All values should be RegionInfos or something wrong. 475 return result.rawCells(); 476 } 477 } 478 479 /** 480 * Checks if the specified table exists. Looks at the hbase:meta table hosted on 481 * the specified server. 482 * @param connection connection we're using 483 * @param tableName table to check 484 * @return true if the table exists in meta, false if not 485 */ 486 public static boolean tableExists(Connection connection, 487 final TableName tableName) 488 throws IOException { 489 // Catalog tables always exist. 490 return tableName.equals(TableName.META_TABLE_NAME) || 491 getTableState(connection, tableName) != null; 492 } 493 494 /** 495 * Lists all of the regions currently in META. 496 * 497 * @param connection to connect with 498 * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions, 499 * true and we'll leave out offlined regions from returned list 500 * @return List of all user-space regions. 501 */ 502 @VisibleForTesting 503 public static List<RegionInfo> getAllRegions(Connection connection, 504 boolean excludeOfflinedSplitParents) 505 throws IOException { 506 List<Pair<RegionInfo, ServerName>> result; 507 508 result = getTableRegionsAndLocations(connection, null, 509 excludeOfflinedSplitParents); 510 511 return getListOfRegionInfos(result); 512 513 } 514 515 /** 516 * Gets all of the regions of the specified table. Do not use this method 517 * to get meta table regions, use methods in MetaTableLocator instead. 518 * @param connection connection we're using 519 * @param tableName table we're looking for 520 * @return Ordered list of {@link RegionInfo}. 521 */ 522 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName) 523 throws IOException { 524 return getTableRegions(connection, tableName, false); 525 } 526 527 /** 528 * Gets all of the regions of the specified table. Do not use this method 529 * to get meta table regions, use methods in MetaTableLocator instead. 530 * @param connection connection we're using 531 * @param tableName table we're looking for 532 * @param excludeOfflinedSplitParents If true, do not include offlined split 533 * parents in the return. 534 * @return Ordered list of {@link RegionInfo}. 535 */ 536 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName, 537 final boolean excludeOfflinedSplitParents) throws IOException { 538 List<Pair<RegionInfo, ServerName>> result = 539 getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents); 540 return getListOfRegionInfos(result); 541 } 542 543 private static List<RegionInfo> getListOfRegionInfos( 544 final List<Pair<RegionInfo, ServerName>> pairs) { 545 if (pairs == null || pairs.isEmpty()) { 546 return Collections.emptyList(); 547 } 548 List<RegionInfo> result = new ArrayList<>(pairs.size()); 549 for (Pair<RegionInfo, ServerName> pair : pairs) { 550 result.add(pair.getFirst()); 551 } 552 return result; 553 } 554 555 /** 556 * @param tableName table we're working with 557 * @return start row for scanning META according to query type 558 */ 559 public static byte[] getTableStartRowForMeta(TableName tableName, QueryType type) { 560 if (tableName == null) { 561 return null; 562 } 563 switch (type) { 564 case REGION: 565 byte[] startRow = new byte[tableName.getName().length + 2]; 566 System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length); 567 startRow[startRow.length - 2] = HConstants.DELIMITER; 568 startRow[startRow.length - 1] = HConstants.DELIMITER; 569 return startRow; 570 case ALL: 571 case TABLE: 572 default: 573 return tableName.getName(); 574 } 575 } 576 577 /** 578 * @param tableName table we're working with 579 * @return stop row for scanning META according to query type 580 */ 581 public static byte[] getTableStopRowForMeta(TableName tableName, QueryType type) { 582 if (tableName == null) { 583 return null; 584 } 585 final byte[] stopRow; 586 switch (type) { 587 case REGION: 588 stopRow = new byte[tableName.getName().length + 3]; 589 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length); 590 stopRow[stopRow.length - 3] = ' '; 591 stopRow[stopRow.length - 2] = HConstants.DELIMITER; 592 stopRow[stopRow.length - 1] = HConstants.DELIMITER; 593 break; 594 case ALL: 595 case TABLE: 596 default: 597 stopRow = new byte[tableName.getName().length + 1]; 598 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length); 599 stopRow[stopRow.length - 1] = ' '; 600 break; 601 } 602 return stopRow; 603 } 604 605 /** 606 * This method creates a Scan object that will only scan catalog rows that 607 * belong to the specified table. It doesn't specify any columns. 608 * This is a better alternative to just using a start row and scan until 609 * it hits a new table since that requires parsing the HRI to get the table 610 * name. 611 * @param tableName bytes of table's name 612 * @return configured Scan object 613 */ 614 @Deprecated 615 public static Scan getScanForTableName(Connection connection, TableName tableName) { 616 // Start key is just the table name with delimiters 617 byte[] startKey = getTableStartRowForMeta(tableName, QueryType.REGION); 618 // Stop key appends the smallest possible char to the table name 619 byte[] stopKey = getTableStopRowForMeta(tableName, QueryType.REGION); 620 621 Scan scan = getMetaScan(connection, -1); 622 scan.setStartRow(startKey); 623 scan.setStopRow(stopKey); 624 return scan; 625 } 626 627 private static Scan getMetaScan(Connection connection, int rowUpperLimit) { 628 Scan scan = new Scan(); 629 int scannerCaching = connection.getConfiguration() 630 .getInt(HConstants.HBASE_META_SCANNER_CACHING, 631 HConstants.DEFAULT_HBASE_META_SCANNER_CACHING); 632 if (connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS, 633 HConstants.DEFAULT_USE_META_REPLICAS)) { 634 scan.setConsistency(Consistency.TIMELINE); 635 } 636 if (rowUpperLimit > 0) { 637 scan.setLimit(rowUpperLimit); 638 scan.setReadType(Scan.ReadType.PREAD); 639 } 640 scan.setCaching(scannerCaching); 641 return scan; 642 } 643 /** 644 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead. 645 * @param connection connection we're using 646 * @param tableName table we're looking for 647 * @return Return list of regioninfos and server. 648 */ 649 public static List<Pair<RegionInfo, ServerName>> 650 getTableRegionsAndLocations(Connection connection, TableName tableName) 651 throws IOException { 652 return getTableRegionsAndLocations(connection, tableName, true); 653 } 654 655 /** 656 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead. 657 * @param connection connection we're using 658 * @param tableName table to work with, can be null for getting all regions 659 * @param excludeOfflinedSplitParents don't return split parents 660 * @return Return list of regioninfos and server addresses. 661 */ 662 public static List<Pair<RegionInfo, ServerName>> getTableRegionsAndLocations( 663 Connection connection, @Nullable final TableName tableName, 664 final boolean excludeOfflinedSplitParents) throws IOException { 665 if (tableName != null && tableName.equals(TableName.META_TABLE_NAME)) { 666 throw new IOException("This method can't be used to locate meta regions;" 667 + " use MetaTableLocator instead"); 668 } 669 // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress 670 CollectingVisitor<Pair<RegionInfo, ServerName>> visitor = 671 new CollectingVisitor<Pair<RegionInfo, ServerName>>() { 672 private RegionLocations current = null; 673 674 @Override 675 public boolean visit(Result r) throws IOException { 676 current = getRegionLocations(r); 677 if (current == null || current.getRegionLocation().getRegion() == null) { 678 LOG.warn("No serialized RegionInfo in " + r); 679 return true; 680 } 681 RegionInfo hri = current.getRegionLocation().getRegion(); 682 if (excludeOfflinedSplitParents && hri.isSplitParent()) return true; 683 // Else call super and add this Result to the collection. 684 return super.visit(r); 685 } 686 687 @Override 688 void add(Result r) { 689 if (current == null) { 690 return; 691 } 692 for (HRegionLocation loc : current.getRegionLocations()) { 693 if (loc != null) { 694 this.results.add(new Pair<>(loc.getRegion(), loc.getServerName())); 695 } 696 } 697 } 698 }; 699 scanMeta(connection, 700 getTableStartRowForMeta(tableName, QueryType.REGION), 701 getTableStopRowForMeta(tableName, QueryType.REGION), 702 QueryType.REGION, visitor); 703 return visitor.getResults(); 704 } 705 706 /** 707 * @param connection connection we're using 708 * @param serverName server whose regions we're interested in 709 * @return List of user regions installed on this server (does not include 710 * catalog regions). 711 * @throws IOException 712 */ 713 public static NavigableMap<RegionInfo, Result> 714 getServerUserRegions(Connection connection, final ServerName serverName) 715 throws IOException { 716 final NavigableMap<RegionInfo, Result> hris = new TreeMap<>(); 717 // Fill the above hris map with entries from hbase:meta that have the passed 718 // servername. 719 CollectingVisitor<Result> v = new CollectingVisitor<Result>() { 720 @Override 721 void add(Result r) { 722 if (r == null || r.isEmpty()) return; 723 RegionLocations locations = getRegionLocations(r); 724 if (locations == null) return; 725 for (HRegionLocation loc : locations.getRegionLocations()) { 726 if (loc != null) { 727 if (loc.getServerName() != null && loc.getServerName().equals(serverName)) { 728 hris.put(loc.getRegion(), r); 729 } 730 } 731 } 732 } 733 }; 734 scanMeta(connection, null, null, QueryType.REGION, v); 735 return hris; 736 } 737 738 public static void fullScanMetaAndPrint(Connection connection) 739 throws IOException { 740 Visitor v = r -> { 741 if (r == null || r.isEmpty()) { 742 return true; 743 } 744 LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r); 745 TableState state = getTableState(r); 746 if (state != null) { 747 LOG.info("fullScanMetaAndPrint.Table State={}" + state); 748 } else { 749 RegionLocations locations = getRegionLocations(r); 750 if (locations == null) { 751 return true; 752 } 753 for (HRegionLocation loc : locations.getRegionLocations()) { 754 if (loc != null) { 755 LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion()); 756 } 757 } 758 } 759 return true; 760 }; 761 scanMeta(connection, null, null, QueryType.ALL, v); 762 } 763 764 public static void scanMetaForTableRegions(Connection connection, 765 Visitor visitor, TableName tableName) throws IOException { 766 scanMeta(connection, tableName, QueryType.REGION, Integer.MAX_VALUE, visitor); 767 } 768 769 private static void scanMeta(Connection connection, TableName table, QueryType type, int maxRows, 770 final Visitor visitor) throws IOException { 771 scanMeta(connection, getTableStartRowForMeta(table, type), getTableStopRowForMeta(table, type), 772 type, maxRows, visitor); 773 } 774 775 private static void scanMeta(Connection connection, @Nullable final byte[] startRow, 776 @Nullable final byte[] stopRow, QueryType type, final Visitor visitor) throws IOException { 777 scanMeta(connection, startRow, stopRow, type, Integer.MAX_VALUE, visitor); 778 } 779 780 /** 781 * Performs a scan of META table for given table starting from given row. 782 * @param connection connection we're using 783 * @param visitor visitor to call 784 * @param tableName table withing we scan 785 * @param row start scan from this row 786 * @param rowLimit max number of rows to return 787 * @throws IOException 788 */ 789 public static void scanMeta(Connection connection, 790 final Visitor visitor, final TableName tableName, 791 final byte[] row, final int rowLimit) 792 throws IOException { 793 794 byte[] startRow = null; 795 byte[] stopRow = null; 796 if (tableName != null) { 797 startRow = 798 getTableStartRowForMeta(tableName, QueryType.REGION); 799 if (row != null) { 800 RegionInfo closestRi = 801 getClosestRegionInfo(connection, tableName, row); 802 startRow = RegionInfo 803 .createRegionName(tableName, closestRi.getStartKey(), HConstants.ZEROES, false); 804 } 805 stopRow = 806 getTableStopRowForMeta(tableName, QueryType.REGION); 807 } 808 scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor); 809 } 810 811 /** 812 * Performs a scan of META table. 813 * @param connection connection we're using 814 * @param startRow Where to start the scan. Pass null if want to begin scan 815 * at first row. 816 * @param stopRow Where to stop the scan. Pass null if want to scan all rows 817 * from the start one 818 * @param type scanned part of meta 819 * @param maxRows maximum rows to return 820 * @param visitor Visitor invoked against each row. 821 * @throws IOException 822 */ 823 static void scanMeta(Connection connection, @Nullable final byte[] startRow, 824 @Nullable final byte[] stopRow, QueryType type, int maxRows, final Visitor visitor) 825 throws IOException { 826 int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE; 827 Scan scan = getMetaScan(connection, rowUpperLimit); 828 829 for (byte[] family : type.getFamilies()) { 830 scan.addFamily(family); 831 } 832 if (startRow != null) { 833 scan.withStartRow(startRow); 834 } 835 if (stopRow != null) { 836 scan.withStopRow(stopRow); 837 } 838 839 if (LOG.isTraceEnabled()) { 840 LOG.trace("Scanning META" 841 + " starting at row=" + Bytes.toStringBinary(startRow) 842 + " stopping at row=" + Bytes.toStringBinary(stopRow) 843 + " for max=" + rowUpperLimit 844 + " with caching=" + scan.getCaching()); 845 } 846 847 int currentRow = 0; 848 try (Table metaTable = getMetaHTable(connection)) { 849 try (ResultScanner scanner = metaTable.getScanner(scan)) { 850 Result data; 851 while ((data = scanner.next()) != null) { 852 if (data.isEmpty()) continue; 853 // Break if visit returns false. 854 if (!visitor.visit(data)) break; 855 if (++currentRow >= rowUpperLimit) break; 856 } 857 } 858 } 859 if (visitor instanceof Closeable) { 860 try { 861 ((Closeable) visitor).close(); 862 } catch (Throwable t) { 863 ExceptionUtil.rethrowIfInterrupt(t); 864 LOG.debug("Got exception in closing the meta scanner visitor", t); 865 } 866 } 867 } 868 869 /** 870 * @return Get closest metatable region row to passed <code>row</code> 871 */ 872 @NonNull 873 private static RegionInfo getClosestRegionInfo(Connection connection, 874 @NonNull final TableName tableName, @NonNull final byte[] row) throws IOException { 875 byte[] searchRow = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false); 876 Scan scan = getMetaScan(connection, 1); 877 scan.setReversed(true); 878 scan.withStartRow(searchRow); 879 try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) { 880 Result result = resultScanner.next(); 881 if (result == null) { 882 throw new TableNotFoundException("Cannot find row in META " + 883 " for table: " + tableName + ", row=" + Bytes.toStringBinary(row)); 884 } 885 RegionInfo regionInfo = getRegionInfo(result); 886 if (regionInfo == null) { 887 throw new IOException("RegionInfo was null or empty in Meta for " + 888 tableName + ", row=" + Bytes.toStringBinary(row)); 889 } 890 return regionInfo; 891 } 892 } 893 894 /** 895 * Returns the column family used for meta columns. 896 * @return HConstants.CATALOG_FAMILY. 897 */ 898 public static byte[] getCatalogFamily() { 899 return HConstants.CATALOG_FAMILY; 900 } 901 902 /** 903 * Returns the column family used for table columns. 904 * @return HConstants.TABLE_FAMILY. 905 */ 906 private static byte[] getTableFamily() { 907 return HConstants.TABLE_FAMILY; 908 } 909 910 /** 911 * Returns the column qualifier for serialized region info 912 * @return HConstants.REGIONINFO_QUALIFIER 913 */ 914 public static byte[] getRegionInfoColumn() { 915 return HConstants.REGIONINFO_QUALIFIER; 916 } 917 918 /** 919 * Returns the column qualifier for serialized table state 920 * @return HConstants.TABLE_STATE_QUALIFIER 921 */ 922 private static byte[] getTableStateColumn() { 923 return HConstants.TABLE_STATE_QUALIFIER; 924 } 925 926 /** 927 * Returns the column qualifier for serialized region state 928 * @return HConstants.TABLE_STATE_QUALIFIER 929 */ 930 private static byte[] getRegionStateColumn() { 931 return HConstants.STATE_QUALIFIER; 932 } 933 934 /** 935 * Returns the column qualifier for serialized region state 936 * @param replicaId the replicaId of the region 937 * @return a byte[] for state qualifier 938 */ 939 @VisibleForTesting 940 static byte[] getRegionStateColumn(int replicaId) { 941 return replicaId == 0 ? HConstants.STATE_QUALIFIER 942 : Bytes.toBytes(HConstants.STATE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 943 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 944 } 945 946 /** 947 * Returns the column qualifier for serialized region state 948 * @param replicaId the replicaId of the region 949 * @return a byte[] for sn column qualifier 950 */ 951 @VisibleForTesting 952 static byte[] getServerNameColumn(int replicaId) { 953 return replicaId == 0 ? HConstants.SERVERNAME_QUALIFIER 954 : Bytes.toBytes(HConstants.SERVERNAME_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 955 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 956 } 957 958 /** 959 * Returns the column qualifier for server column for replicaId 960 * @param replicaId the replicaId of the region 961 * @return a byte[] for server column qualifier 962 */ 963 @VisibleForTesting 964 public static byte[] getServerColumn(int replicaId) { 965 return replicaId == 0 966 ? HConstants.SERVER_QUALIFIER 967 : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 968 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 969 } 970 971 /** 972 * Returns the column qualifier for server start code column for replicaId 973 * @param replicaId the replicaId of the region 974 * @return a byte[] for server start code column qualifier 975 */ 976 @VisibleForTesting 977 public static byte[] getStartCodeColumn(int replicaId) { 978 return replicaId == 0 979 ? HConstants.STARTCODE_QUALIFIER 980 : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 981 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 982 } 983 984 /** 985 * Returns the column qualifier for seqNum column for replicaId 986 * @param replicaId the replicaId of the region 987 * @return a byte[] for seqNum column qualifier 988 */ 989 @VisibleForTesting 990 public static byte[] getSeqNumColumn(int replicaId) { 991 return replicaId == 0 992 ? HConstants.SEQNUM_QUALIFIER 993 : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 994 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 995 } 996 997 /** 998 * Parses the replicaId from the server column qualifier. See top of the class javadoc 999 * for the actual meta layout 1000 * @param serverColumn the column qualifier 1001 * @return an int for the replicaId 1002 */ 1003 @VisibleForTesting 1004 static int parseReplicaIdFromServerColumn(byte[] serverColumn) { 1005 String serverStr = Bytes.toString(serverColumn); 1006 1007 Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr); 1008 if (matcher.matches() && matcher.groupCount() > 0) { 1009 String group = matcher.group(1); 1010 if (group != null && group.length() > 0) { 1011 return Integer.parseInt(group.substring(1), 16); 1012 } else { 1013 return 0; 1014 } 1015 } 1016 return -1; 1017 } 1018 1019 /** 1020 * Returns a {@link ServerName} from catalog table {@link Result}. 1021 * @param r Result to pull from 1022 * @return A ServerName instance or null if necessary fields not found or empty. 1023 */ 1024 @Nullable 1025 @InterfaceAudience.Private // for use by HMaster#getTableRegionRow which is used for testing only 1026 public static ServerName getServerName(final Result r, final int replicaId) { 1027 byte[] serverColumn = getServerColumn(replicaId); 1028 Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn); 1029 if (cell == null || cell.getValueLength() == 0) return null; 1030 String hostAndPort = Bytes.toString( 1031 cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); 1032 byte[] startcodeColumn = getStartCodeColumn(replicaId); 1033 cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn); 1034 if (cell == null || cell.getValueLength() == 0) return null; 1035 try { 1036 return ServerName.valueOf(hostAndPort, 1037 Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength())); 1038 } catch (IllegalArgumentException e) { 1039 LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e); 1040 return null; 1041 } 1042 } 1043 1044 /** 1045 * The latest seqnum that the server writing to meta observed when opening the region. 1046 * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written. 1047 * @param r Result to pull the seqNum from 1048 * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written. 1049 */ 1050 private static long getSeqNumDuringOpen(final Result r, final int replicaId) { 1051 Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId)); 1052 if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM; 1053 return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); 1054 } 1055 1056 /** 1057 * Returns the daughter regions by reading the corresponding columns of the catalog table 1058 * Result. 1059 * @param data a Result object from the catalog table scan 1060 * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent 1061 */ 1062 public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) { 1063 RegionInfo splitA = getRegionInfo(data, HConstants.SPLITA_QUALIFIER); 1064 RegionInfo splitB = getRegionInfo(data, HConstants.SPLITB_QUALIFIER); 1065 return new PairOfSameType<>(splitA, splitB); 1066 } 1067 1068 /** 1069 * Returns an HRegionLocationList extracted from the result. 1070 * @return an HRegionLocationList containing all locations for the region range or null if 1071 * we can't deserialize the result. 1072 */ 1073 @Nullable 1074 public static RegionLocations getRegionLocations(final Result r) { 1075 if (r == null) return null; 1076 RegionInfo regionInfo = getRegionInfo(r, getRegionInfoColumn()); 1077 if (regionInfo == null) return null; 1078 1079 List<HRegionLocation> locations = new ArrayList<>(1); 1080 NavigableMap<byte[],NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap(); 1081 1082 locations.add(getRegionLocation(r, regionInfo, 0)); 1083 1084 NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily()); 1085 if (infoMap == null) return new RegionLocations(locations); 1086 1087 // iterate until all serverName columns are seen 1088 int replicaId = 0; 1089 byte[] serverColumn = getServerColumn(replicaId); 1090 SortedMap<byte[], byte[]> serverMap; 1091 serverMap = infoMap.tailMap(serverColumn, false); 1092 1093 if (serverMap.isEmpty()) return new RegionLocations(locations); 1094 1095 for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) { 1096 replicaId = parseReplicaIdFromServerColumn(entry.getKey()); 1097 if (replicaId < 0) { 1098 break; 1099 } 1100 HRegionLocation location = getRegionLocation(r, regionInfo, replicaId); 1101 // In case the region replica is newly created, it's location might be null. We usually do not 1102 // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs. 1103 if (location.getServerName() == null) { 1104 locations.add(null); 1105 } else { 1106 locations.add(location); 1107 } 1108 } 1109 1110 return new RegionLocations(locations); 1111 } 1112 1113 /** 1114 * Returns the HRegionLocation parsed from the given meta row Result 1115 * for the given regionInfo and replicaId. The regionInfo can be the default region info 1116 * for the replica. 1117 * @param r the meta row result 1118 * @param regionInfo RegionInfo for default replica 1119 * @param replicaId the replicaId for the HRegionLocation 1120 * @return HRegionLocation parsed from the given meta row Result for the given replicaId 1121 */ 1122 private static HRegionLocation getRegionLocation(final Result r, final RegionInfo regionInfo, 1123 final int replicaId) { 1124 ServerName serverName = getServerName(r, replicaId); 1125 long seqNum = getSeqNumDuringOpen(r, replicaId); 1126 RegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId); 1127 return new HRegionLocation(replicaInfo, serverName, seqNum); 1128 } 1129 1130 /** 1131 * Returns RegionInfo object from the column 1132 * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog 1133 * table Result. 1134 * @param data a Result object from the catalog table scan 1135 * @return RegionInfo or null 1136 */ 1137 public static RegionInfo getRegionInfo(Result data) { 1138 return getRegionInfo(data, HConstants.REGIONINFO_QUALIFIER); 1139 } 1140 1141 /** 1142 * Returns the RegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and 1143 * <code>qualifier</code> of the catalog table result. 1144 * @param r a Result object from the catalog table scan 1145 * @param qualifier Column family qualifier 1146 * @return An RegionInfo instance or null. 1147 */ 1148 @Nullable 1149 public static RegionInfo getRegionInfo(final Result r, byte [] qualifier) { 1150 Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier); 1151 if (cell == null) return null; 1152 return RegionInfo.parseFromOrNull(cell.getValueArray(), 1153 cell.getValueOffset(), cell.getValueLength()); 1154 } 1155 1156 /** 1157 * Fetch table state for given table from META table 1158 * @param conn connection to use 1159 * @param tableName table to fetch state for 1160 */ 1161 @Nullable 1162 public static TableState getTableState(Connection conn, TableName tableName) 1163 throws IOException { 1164 if (tableName.equals(TableName.META_TABLE_NAME)) { 1165 return new TableState(tableName, TableState.State.ENABLED); 1166 } 1167 Table metaHTable = getMetaHTable(conn); 1168 Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getTableStateColumn()); 1169 Result result = metaHTable.get(get); 1170 return getTableState(result); 1171 } 1172 1173 /** 1174 * Fetch table states from META table 1175 * @param conn connection to use 1176 * @return map {tableName -> state} 1177 */ 1178 public static Map<TableName, TableState> getTableStates(Connection conn) 1179 throws IOException { 1180 final Map<TableName, TableState> states = new LinkedHashMap<>(); 1181 Visitor collector = r -> { 1182 TableState state = getTableState(r); 1183 if (state != null) { 1184 states.put(state.getTableName(), state); 1185 } 1186 return true; 1187 }; 1188 fullScanTables(conn, collector); 1189 return states; 1190 } 1191 1192 /** 1193 * Updates state in META 1194 * @param conn connection to use 1195 * @param tableName table to look for 1196 */ 1197 public static void updateTableState(Connection conn, TableName tableName, 1198 TableState.State actual) throws IOException { 1199 updateTableState(conn, new TableState(tableName, actual)); 1200 } 1201 1202 /** 1203 * Decode table state from META Result. 1204 * Should contain cell from HConstants.TABLE_FAMILY 1205 * @return null if not found 1206 */ 1207 @Nullable 1208 public static TableState getTableState(Result r) throws IOException { 1209 Cell cell = r.getColumnLatestCell(getTableFamily(), getTableStateColumn()); 1210 if (cell == null) { 1211 return null; 1212 } 1213 try { 1214 return TableState.parseFrom(TableName.valueOf(r.getRow()), 1215 Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(), 1216 cell.getValueOffset() + cell.getValueLength())); 1217 } catch (DeserializationException e) { 1218 throw new IOException(e); 1219 } 1220 } 1221 1222 /** 1223 * Implementations 'visit' a catalog table row. 1224 */ 1225 public interface Visitor { 1226 /** 1227 * Visit the catalog table row. 1228 * @param r A row from catalog table 1229 * @return True if we are to proceed scanning the table, else false if 1230 * we are to stop now. 1231 */ 1232 boolean visit(final Result r) throws IOException; 1233 } 1234 1235 /** 1236 * Implementations 'visit' a catalog table row but with close() at the end. 1237 */ 1238 public interface CloseableVisitor extends Visitor, Closeable { 1239 } 1240 1241 /** 1242 * A {@link Visitor} that collects content out of passed {@link Result}. 1243 */ 1244 static abstract class CollectingVisitor<T> implements Visitor { 1245 final List<T> results = new ArrayList<>(); 1246 @Override 1247 public boolean visit(Result r) throws IOException { 1248 if (r == null || r.isEmpty()) return true; 1249 add(r); 1250 return true; 1251 } 1252 1253 abstract void add(Result r); 1254 1255 /** 1256 * @return Collected results; wait till visits complete to collect all 1257 * possible results 1258 */ 1259 List<T> getResults() { 1260 return this.results; 1261 } 1262 } 1263 1264 /** 1265 * Collects all returned. 1266 */ 1267 static class CollectAllVisitor extends CollectingVisitor<Result> { 1268 @Override 1269 void add(Result r) { 1270 this.results.add(r); 1271 } 1272 } 1273 1274 /** 1275 * A Visitor that skips offline regions and split parents 1276 */ 1277 public static abstract class DefaultVisitorBase implements Visitor { 1278 1279 public DefaultVisitorBase() { 1280 super(); 1281 } 1282 1283 public abstract boolean visitInternal(Result rowResult) throws IOException; 1284 1285 @Override 1286 public boolean visit(Result rowResult) throws IOException { 1287 RegionInfo info = getRegionInfo(rowResult); 1288 if (info == null) { 1289 return true; 1290 } 1291 1292 //skip over offline and split regions 1293 if (!(info.isOffline() || info.isSplit())) { 1294 return visitInternal(rowResult); 1295 } 1296 return true; 1297 } 1298 } 1299 1300 /** 1301 * A Visitor for a table. Provides a consistent view of the table's 1302 * hbase:meta entries during concurrent splits (see HBASE-5986 for details). This class 1303 * does not guarantee ordered traversal of meta entries, and can block until the 1304 * hbase:meta entries for daughters are available during splits. 1305 */ 1306 public static abstract class TableVisitorBase extends DefaultVisitorBase { 1307 private TableName tableName; 1308 1309 public TableVisitorBase(TableName tableName) { 1310 super(); 1311 this.tableName = tableName; 1312 } 1313 1314 @Override 1315 public final boolean visit(Result rowResult) throws IOException { 1316 RegionInfo info = getRegionInfo(rowResult); 1317 if (info == null) { 1318 return true; 1319 } 1320 if (!(info.getTable().equals(tableName))) { 1321 return false; 1322 } 1323 return super.visit(rowResult); 1324 } 1325 } 1326 1327 /** 1328 * Count regions in <code>hbase:meta</code> for passed table. 1329 * @param c Configuration object 1330 * @param tableName table name to count regions for 1331 * @return Count or regions in table <code>tableName</code> 1332 */ 1333 public static int getRegionCount(final Configuration c, final TableName tableName) 1334 throws IOException { 1335 try (Connection connection = ConnectionFactory.createConnection(c)) { 1336 return getRegionCount(connection, tableName); 1337 } 1338 } 1339 1340 /** 1341 * Count regions in <code>hbase:meta</code> for passed table. 1342 * @param connection Connection object 1343 * @param tableName table name to count regions for 1344 * @return Count or regions in table <code>tableName</code> 1345 */ 1346 public static int getRegionCount(final Connection connection, final TableName tableName) 1347 throws IOException { 1348 try (RegionLocator locator = connection.getRegionLocator(tableName)) { 1349 List<HRegionLocation> locations = locator.getAllRegionLocations(); 1350 return locations == null ? 0 : locations.size(); 1351 } 1352 } 1353 1354 //////////////////////// 1355 // Editing operations // 1356 //////////////////////// 1357 1358 /** 1359 * Generates and returns a Put containing the region into for the catalog table 1360 */ 1361 public static Put makePutFromRegionInfo(RegionInfo regionInfo, long ts) throws IOException { 1362 Put put = new Put(regionInfo.getRegionName(), ts); 1363 addRegionInfo(put, regionInfo); 1364 return put; 1365 } 1366 1367 /** 1368 * Generates and returns a Delete containing the region info for the catalog table 1369 */ 1370 private static Delete makeDeleteFromRegionInfo(RegionInfo regionInfo, long ts) { 1371 if (regionInfo == null) { 1372 throw new IllegalArgumentException("Can't make a delete for null region"); 1373 } 1374 Delete delete = new Delete(regionInfo.getRegionName()); 1375 delete.addFamily(getCatalogFamily(), ts); 1376 return delete; 1377 } 1378 1379 /** 1380 * Adds split daughters to the Put 1381 */ 1382 private static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB) 1383 throws IOException { 1384 if (splitA != null) { 1385 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 1386 .setRow(put.getRow()) 1387 .setFamily(HConstants.CATALOG_FAMILY) 1388 .setQualifier(HConstants.SPLITA_QUALIFIER) 1389 .setTimestamp(put.getTimestamp()) 1390 .setType(Type.Put) 1391 .setValue(RegionInfo.toByteArray(splitA)) 1392 .build()); 1393 } 1394 if (splitB != null) { 1395 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 1396 .setRow(put.getRow()) 1397 .setFamily(HConstants.CATALOG_FAMILY) 1398 .setQualifier(HConstants.SPLITB_QUALIFIER) 1399 .setTimestamp(put.getTimestamp()) 1400 .setType(Type.Put) 1401 .setValue(RegionInfo.toByteArray(splitB)) 1402 .build()); 1403 } 1404 return put; 1405 } 1406 1407 /** 1408 * Put the passed <code>p</code> to the <code>hbase:meta</code> table. 1409 * @param connection connection we're using 1410 * @param p Put to add to hbase:meta 1411 */ 1412 private static void putToMetaTable(Connection connection, Put p) throws IOException { 1413 try (Table table = getMetaHTable(connection)) { 1414 put(table, p); 1415 } 1416 } 1417 1418 /** 1419 * @param t Table to use 1420 * @param p put to make 1421 */ 1422 private static void put(Table t, Put p) throws IOException { 1423 debugLogMutation(p); 1424 t.put(p); 1425 } 1426 1427 /** 1428 * Put the passed <code>ps</code> to the <code>hbase:meta</code> table. 1429 * @param connection connection we're using 1430 * @param ps Put to add to hbase:meta 1431 */ 1432 public static void putsToMetaTable(final Connection connection, final List<Put> ps) 1433 throws IOException { 1434 if (ps.isEmpty()) { 1435 return; 1436 } 1437 try (Table t = getMetaHTable(connection)) { 1438 debugLogMutations(ps); 1439 // the implementation for putting a single Put is much simpler so here we do a check first. 1440 if (ps.size() == 1) { 1441 t.put(ps.get(0)); 1442 } else { 1443 t.put(ps); 1444 } 1445 } 1446 } 1447 1448 /** 1449 * Delete the passed <code>d</code> from the <code>hbase:meta</code> table. 1450 * @param connection connection we're using 1451 * @param d Delete to add to hbase:meta 1452 */ 1453 private static void deleteFromMetaTable(final Connection connection, final Delete d) 1454 throws IOException { 1455 List<Delete> dels = new ArrayList<>(1); 1456 dels.add(d); 1457 deleteFromMetaTable(connection, dels); 1458 } 1459 1460 /** 1461 * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table. 1462 * @param connection connection we're using 1463 * @param deletes Deletes to add to hbase:meta This list should support #remove. 1464 */ 1465 private static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes) 1466 throws IOException { 1467 try (Table t = getMetaHTable(connection)) { 1468 debugLogMutations(deletes); 1469 t.delete(deletes); 1470 } 1471 } 1472 1473 /** 1474 * Deletes some replica columns corresponding to replicas for the passed rows 1475 * @param metaRows rows in hbase:meta 1476 * @param replicaIndexToDeleteFrom the replica ID we would start deleting from 1477 * @param numReplicasToRemove how many replicas to remove 1478 * @param connection connection we're using to access meta table 1479 */ 1480 public static void removeRegionReplicasFromMeta(Set<byte[]> metaRows, 1481 int replicaIndexToDeleteFrom, int numReplicasToRemove, Connection connection) 1482 throws IOException { 1483 int absoluteIndex = replicaIndexToDeleteFrom + numReplicasToRemove; 1484 for (byte[] row : metaRows) { 1485 long now = EnvironmentEdgeManager.currentTime(); 1486 Delete deleteReplicaLocations = new Delete(row); 1487 for (int i = replicaIndexToDeleteFrom; i < absoluteIndex; i++) { 1488 deleteReplicaLocations.addColumns(getCatalogFamily(), 1489 getServerColumn(i), now); 1490 deleteReplicaLocations.addColumns(getCatalogFamily(), 1491 getSeqNumColumn(i), now); 1492 deleteReplicaLocations.addColumns(getCatalogFamily(), 1493 getStartCodeColumn(i), now); 1494 } 1495 deleteFromMetaTable(connection, deleteReplicaLocations); 1496 } 1497 } 1498 1499 private static void addRegionStateToPut(Put put, RegionState.State state) throws IOException { 1500 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 1501 .setRow(put.getRow()) 1502 .setFamily(HConstants.CATALOG_FAMILY) 1503 .setQualifier(getRegionStateColumn()) 1504 .setTimestamp(put.getTimestamp()) 1505 .setType(Cell.Type.Put) 1506 .setValue(Bytes.toBytes(state.name())) 1507 .build()); 1508 } 1509 1510 /** 1511 * Adds daughter region infos to hbase:meta row for the specified region. Note that this does not 1512 * add its daughter's as different rows, but adds information about the daughters in the same row 1513 * as the parent. Use 1514 * {@link #splitRegion(Connection, RegionInfo, RegionInfo, RegionInfo, ServerName,int)} 1515 * if you want to do that. 1516 * @param connection connection we're using 1517 * @param regionInfo RegionInfo of parent region 1518 * @param splitA first split daughter of the parent regionInfo 1519 * @param splitB second split daughter of the parent regionInfo 1520 * @throws IOException if problem connecting or updating meta 1521 */ 1522 public static void addSplitsToParent(Connection connection, RegionInfo regionInfo, 1523 RegionInfo splitA, RegionInfo splitB) throws IOException { 1524 try (Table meta = getMetaHTable(connection)) { 1525 Put put = makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime()); 1526 addDaughtersToPut(put, splitA, splitB); 1527 meta.put(put); 1528 debugLogMutation(put); 1529 LOG.debug("Added region {}", regionInfo.getRegionNameAsString()); 1530 } 1531 } 1532 1533 /** 1534 * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this 1535 * does not add its daughter's as different rows, but adds information about the daughters 1536 * in the same row as the parent. Use 1537 * {@link #splitRegion(Connection, RegionInfo, RegionInfo, RegionInfo, ServerName, int)} 1538 * if you want to do that. 1539 * @param connection connection we're using 1540 * @param regionInfo region information 1541 * @throws IOException if problem connecting or updating meta 1542 */ 1543 @VisibleForTesting 1544 public static void addRegionToMeta(Connection connection, RegionInfo regionInfo) 1545 throws IOException { 1546 addRegionsToMeta(connection, Collections.singletonList(regionInfo), 1); 1547 } 1548 1549 /** 1550 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions 1551 * is CLOSED. 1552 * @param connection connection we're using 1553 * @param regionInfos region information list 1554 * @throws IOException if problem connecting or updating meta 1555 */ 1556 public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos, 1557 int regionReplication) throws IOException { 1558 addRegionsToMeta(connection, regionInfos, regionReplication, 1559 EnvironmentEdgeManager.currentTime()); 1560 } 1561 1562 /** 1563 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions 1564 * is CLOSED. 1565 * @param connection connection we're using 1566 * @param regionInfos region information list 1567 * @param ts desired timestamp 1568 * @throws IOException if problem connecting or updating meta 1569 */ 1570 private static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos, 1571 int regionReplication, long ts) throws IOException { 1572 List<Put> puts = new ArrayList<>(); 1573 for (RegionInfo regionInfo : regionInfos) { 1574 if (RegionReplicaUtil.isDefaultReplica(regionInfo)) { 1575 Put put = makePutFromRegionInfo(regionInfo, ts); 1576 // New regions are added with initial state of CLOSED. 1577 addRegionStateToPut(put, RegionState.State.CLOSED); 1578 // Add empty locations for region replicas so that number of replicas can be cached 1579 // whenever the primary region is looked up from meta 1580 for (int i = 1; i < regionReplication; i++) { 1581 addEmptyLocation(put, i); 1582 } 1583 puts.add(put); 1584 } 1585 } 1586 putsToMetaTable(connection, puts); 1587 LOG.info("Added {} regions to meta.", puts.size()); 1588 } 1589 1590 static Put addMergeRegions(Put put, Collection<RegionInfo> mergeRegions) throws IOException { 1591 int limit = 10000; // Arbitrary limit. No room in our formatted 'task0000' below for more. 1592 int max = mergeRegions.size(); 1593 if (max > limit) { 1594 // Should never happen!!!!! But just in case. 1595 throw new RuntimeException("Can't merge " + max + " regions in one go; " + limit + 1596 " is upper-limit."); 1597 } 1598 int counter = 0; 1599 for (RegionInfo ri: mergeRegions) { 1600 String qualifier = String.format(HConstants.MERGE_QUALIFIER_PREFIX_STR + "%04d", counter++); 1601 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY). 1602 setRow(put.getRow()). 1603 setFamily(HConstants.CATALOG_FAMILY). 1604 setQualifier(Bytes.toBytes(qualifier)). 1605 setTimestamp(put.getTimestamp()). 1606 setType(Type.Put). 1607 setValue(RegionInfo.toByteArray(ri)). 1608 build()); 1609 } 1610 return put; 1611 } 1612 1613 /** 1614 * Merge regions into one in an atomic operation. Deletes the merging regions in 1615 * hbase:meta and adds the merged region. 1616 * @param connection connection we're using 1617 * @param mergedRegion the merged region 1618 * @param sn the location of the region 1619 */ 1620 public static void mergeRegions(Connection connection, RegionInfo mergedRegion, 1621 RegionInfo [] parents, ServerName sn, int regionReplication) 1622 throws IOException { 1623 try (Table meta = getMetaHTable(connection)) { 1624 long time = HConstants.LATEST_TIMESTAMP; 1625 List<Mutation> mutations = new ArrayList<>(); 1626 for (RegionInfo ri: parents) { 1627 // Deletes for merging regions 1628 mutations.add(makeDeleteFromRegionInfo(ri, time)); 1629 } 1630 // Put for parent 1631 Put putOfMerged = makePutFromRegionInfo(mergedRegion, time); 1632 putOfMerged = addMergeRegions(putOfMerged, Arrays.asList(parents)); 1633 // Set initial state to CLOSED. 1634 // NOTE: If initial state is not set to CLOSED then merged region gets added with the 1635 // default OFFLINE state. If Master gets restarted after this step, start up sequence of 1636 // master tries to assign this offline region. This is followed by re-assignments of the 1637 // merged region from resumed {@link MergeTableRegionsProcedure} 1638 addRegionStateToPut(putOfMerged, RegionState.State.CLOSED); 1639 mutations.add(putOfMerged); 1640 // The merged is a new region, openSeqNum = 1 is fine. ServerName may be null 1641 // if crash after merge happened but before we got to here.. means in-memory 1642 // locations of offlined merged, now-closed, regions is lost. Should be ok. We 1643 // assign the merged region later. 1644 if (sn != null) { 1645 addLocation(putOfMerged, sn, 1, mergedRegion.getReplicaId()); 1646 } 1647 1648 // Add empty locations for region replicas of the merged region so that number of replicas 1649 // can be cached whenever the primary region is looked up from meta 1650 for (int i = 1; i < regionReplication; i++) { 1651 addEmptyLocation(putOfMerged, i); 1652 } 1653 1654 byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString() 1655 + HConstants.DELIMITER); 1656 multiMutate(connection, meta, tableRow, mutations); 1657 } 1658 } 1659 1660 /** 1661 * Splits the region into two in an atomic operation. Offlines the parent 1662 * region with the information that it is split into two, and also adds 1663 * the daughter regions. Does not add the location information to the daughter 1664 * regions since they are not open yet. 1665 * @param connection connection we're using 1666 * @param parent the parent region which is split 1667 * @param splitA Split daughter region A 1668 * @param splitB Split daughter region B 1669 * @param sn the location of the region 1670 */ 1671 public static void splitRegion(final Connection connection, RegionInfo parent, RegionInfo splitA, 1672 RegionInfo splitB, ServerName sn, int regionReplication) throws IOException { 1673 try (Table meta = getMetaHTable(connection)) { 1674 long time = EnvironmentEdgeManager.currentTime(); 1675 // Put for parent 1676 Put putParent = makePutFromRegionInfo(RegionInfoBuilder.newBuilder(parent) 1677 .setOffline(true) 1678 .setSplit(true).build(), time); 1679 addDaughtersToPut(putParent, splitA, splitB); 1680 1681 // Puts for daughters 1682 Put putA = makePutFromRegionInfo(splitA, time); 1683 Put putB = makePutFromRegionInfo(splitB, time); 1684 1685 // Set initial state to CLOSED 1686 // NOTE: If initial state is not set to CLOSED then daughter regions get added with the 1687 // default OFFLINE state. If Master gets restarted after this step, start up sequence of 1688 // master tries to assign these offline regions. This is followed by re-assignments of the 1689 // daughter regions from resumed {@link SplitTableRegionProcedure} 1690 addRegionStateToPut(putA, RegionState.State.CLOSED); 1691 addRegionStateToPut(putB, RegionState.State.CLOSED); 1692 1693 addSequenceNum(putA, 1, splitA.getReplicaId()); // new regions, openSeqNum = 1 is fine. 1694 addSequenceNum(putB, 1, splitB.getReplicaId()); 1695 1696 // Add empty locations for region replicas of daughters so that number of replicas can be 1697 // cached whenever the primary region is looked up from meta 1698 for (int i = 1; i < regionReplication; i++) { 1699 addEmptyLocation(putA, i); 1700 addEmptyLocation(putB, i); 1701 } 1702 1703 byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER); 1704 multiMutate(connection, meta, tableRow, putParent, putA, putB); 1705 } 1706 } 1707 1708 /** 1709 * Update state of the table in meta. 1710 * @param connection what we use for update 1711 * @param state new state 1712 */ 1713 private static void updateTableState(Connection connection, TableState state) throws IOException { 1714 Put put = makePutFromTableState(state, EnvironmentEdgeManager.currentTime()); 1715 putToMetaTable(connection, put); 1716 LOG.info("Updated {} in hbase:meta", state); 1717 } 1718 1719 /** 1720 * Construct PUT for given state 1721 * @param state new state 1722 */ 1723 public static Put makePutFromTableState(TableState state, long ts) { 1724 Put put = new Put(state.getTableName().getName(), ts); 1725 put.addColumn(getTableFamily(), getTableStateColumn(), state.convert().toByteArray()); 1726 return put; 1727 } 1728 1729 /** 1730 * Remove state for table from meta 1731 * @param connection to use for deletion 1732 * @param table to delete state for 1733 */ 1734 public static void deleteTableState(Connection connection, TableName table) 1735 throws IOException { 1736 long time = EnvironmentEdgeManager.currentTime(); 1737 Delete delete = new Delete(table.getName()); 1738 delete.addColumns(getTableFamily(), getTableStateColumn(), time); 1739 deleteFromMetaTable(connection, delete); 1740 LOG.info("Deleted table " + table + " state from META"); 1741 } 1742 1743 private static void multiMutate(Connection connection, Table table, byte[] row, 1744 Mutation... mutations) 1745 throws IOException { 1746 multiMutate(connection, table, row, Arrays.asList(mutations)); 1747 } 1748 1749 /** 1750 * Performs an atomic multi-mutate operation against the given table. 1751 */ 1752 // Used by the RSGroup Coprocessor Endpoint. It had a copy/paste of the below. Need to reveal 1753 // this facility for CPEP use or at least those CPEPs that are on their way to becoming part of 1754 // core as is the intent for RSGroup eventually. 1755 public static void multiMutate(Connection connection, final Table table, byte[] row, 1756 final List<Mutation> mutations) 1757 throws IOException { 1758 debugLogMutations(mutations); 1759 // TODO: Need rollback!!!! 1760 // TODO: Need Retry!!! 1761 // TODO: What for a timeout? Default write timeout? GET FROM HTABLE? 1762 // TODO: Review when we come through with ProcedureV2. 1763 RegionServerCallable<MutateRowsResponse, 1764 MultiRowMutationProtos.MultiRowMutationService.BlockingInterface> callable = 1765 new RegionServerCallable<MutateRowsResponse, 1766 MultiRowMutationProtos.MultiRowMutationService.BlockingInterface>( 1767 connection, table.getName(), row, null/*RpcController not used in this CPEP!*/) { 1768 @Override 1769 protected MutateRowsResponse rpcCall() throws Exception { 1770 final MutateRowsRequest.Builder builder = MutateRowsRequest.newBuilder(); 1771 for (Mutation mutation : mutations) { 1772 if (mutation instanceof Put) { 1773 builder.addMutationRequest(ProtobufUtil.toMutation( 1774 ClientProtos.MutationProto.MutationType.PUT, mutation)); 1775 } else if (mutation instanceof Delete) { 1776 builder.addMutationRequest(ProtobufUtil.toMutation( 1777 ClientProtos.MutationProto.MutationType.DELETE, mutation)); 1778 } else { 1779 throw new DoNotRetryIOException("multi in MetaEditor doesn't support " 1780 + mutation.getClass().getName()); 1781 } 1782 } 1783 // The call to #prepare that ran before this invocation will have populated HRegionLocation. 1784 HRegionLocation hrl = getLocation(); 1785 RegionSpecifier region = ProtobufUtil.buildRegionSpecifier( 1786 RegionSpecifierType.REGION_NAME, hrl.getRegion().getRegionName()); 1787 builder.setRegion(region); 1788 // The rpcController here is awkward. The Coprocessor Endpoint wants an instance of a 1789 // com.google.protobuf but we are going over an rpc that is all shaded protobuf so it 1790 // wants a org.apache.h.h.shaded.com.google.protobuf.RpcController. Set up a factory 1791 // that makes com.google.protobuf.RpcController and then copy into it configs. 1792 return getStub().mutateRows(null, builder.build()); 1793 } 1794 1795 @Override 1796 // Called on the end of the super.prepare call. Set the stub. 1797 protected void setStubByServiceName(ServerName serviceName/*Ignored*/) throws IOException { 1798 CoprocessorRpcChannel channel = table.coprocessorService(getRow()); 1799 setStub(MultiRowMutationProtos.MultiRowMutationService.newBlockingStub(channel)); 1800 } 1801 }; 1802 int writeTimeout = connection.getConfiguration().getInt(HConstants.HBASE_RPC_WRITE_TIMEOUT_KEY, 1803 connection.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY, 1804 HConstants.DEFAULT_HBASE_RPC_TIMEOUT)); 1805 // The region location should be cached in connection. Call prepare so this callable picks 1806 // up the region location (see super.prepare method). 1807 callable.prepare(false); 1808 callable.call(writeTimeout); 1809 } 1810 1811 /** 1812 * Updates the location of the specified region in hbase:meta to be the specified server hostname 1813 * and startcode. 1814 * <p> 1815 * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes 1816 * edits to that region. 1817 * @param connection connection we're using 1818 * @param regionInfo region to update location of 1819 * @param openSeqNum the latest sequence number obtained when the region was open 1820 * @param sn Server name 1821 * @param masterSystemTime wall clock time from master if passed in the open region RPC 1822 */ 1823 @VisibleForTesting 1824 public static void updateRegionLocation(Connection connection, RegionInfo regionInfo, 1825 ServerName sn, long openSeqNum, long masterSystemTime) throws IOException { 1826 updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime); 1827 } 1828 1829 /** 1830 * Updates the location of the specified region to be the specified server. 1831 * <p> 1832 * Connects to the specified server which should be hosting the specified catalog region name to 1833 * perform the edit. 1834 * @param connection connection we're using 1835 * @param regionInfo region to update location of 1836 * @param sn Server name 1837 * @param openSeqNum the latest sequence number obtained when the region was open 1838 * @param masterSystemTime wall clock time from master if passed in the open region RPC 1839 * @throws IOException In particular could throw {@link java.net.ConnectException} if the server 1840 * is down on other end. 1841 */ 1842 private static void updateLocation(Connection connection, RegionInfo regionInfo, ServerName sn, 1843 long openSeqNum, long masterSystemTime) throws IOException { 1844 // region replicas are kept in the primary region's row 1845 Put put = new Put(getMetaKeyForRegion(regionInfo), masterSystemTime); 1846 addRegionInfo(put, regionInfo); 1847 addLocation(put, sn, openSeqNum, regionInfo.getReplicaId()); 1848 putToMetaTable(connection, put); 1849 LOG.info("Updated row {} with server=", regionInfo.getRegionNameAsString(), sn); 1850 } 1851 1852 /** 1853 * Deletes the specified region from META. 1854 * @param connection connection we're using 1855 * @param regionInfo region to be deleted from META 1856 */ 1857 public static void deleteRegionInfo(Connection connection, RegionInfo regionInfo) 1858 throws IOException { 1859 Delete delete = new Delete(regionInfo.getRegionName()); 1860 delete.addFamily(getCatalogFamily(), HConstants.LATEST_TIMESTAMP); 1861 deleteFromMetaTable(connection, delete); 1862 LOG.info("Deleted " + regionInfo.getRegionNameAsString()); 1863 } 1864 1865 /** 1866 * Deletes the specified regions from META. 1867 * @param connection connection we're using 1868 * @param regionsInfo list of regions to be deleted from META 1869 */ 1870 public static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo) 1871 throws IOException { 1872 deleteRegionInfos(connection, regionsInfo, EnvironmentEdgeManager.currentTime()); 1873 } 1874 1875 /** 1876 * Deletes the specified regions from META. 1877 * @param connection connection we're using 1878 * @param regionsInfo list of regions to be deleted from META 1879 */ 1880 private static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo, 1881 long ts) 1882 throws IOException { 1883 List<Delete> deletes = new ArrayList<>(regionsInfo.size()); 1884 for (RegionInfo hri : regionsInfo) { 1885 Delete e = new Delete(hri.getRegionName()); 1886 e.addFamily(getCatalogFamily(), ts); 1887 deletes.add(e); 1888 } 1889 deleteFromMetaTable(connection, deletes); 1890 LOG.info("Deleted {} regions from META", regionsInfo.size()); 1891 LOG.debug("Deleted regions: {}", regionsInfo); 1892 } 1893 1894 /** 1895 * Overwrites the specified regions from hbase:meta. Deletes old rows for the given regions and 1896 * adds new ones. Regions added back have state CLOSED. 1897 * @param connection connection we're using 1898 * @param regionInfos list of regions to be added to META 1899 */ 1900 public static void overwriteRegions(Connection connection, List<RegionInfo> regionInfos, 1901 int regionReplication) throws IOException { 1902 // use master time for delete marker and the Put 1903 long now = EnvironmentEdgeManager.currentTime(); 1904 deleteRegionInfos(connection, regionInfos, now); 1905 // Why sleep? This is the easiest way to ensure that the previous deletes does not 1906 // eclipse the following puts, that might happen in the same ts from the server. 1907 // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed, 1908 // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep. 1909 // 1910 // HBASE-13875 uses master timestamp for the mutations. The 20ms sleep is not needed 1911 addRegionsToMeta(connection, regionInfos, regionReplication, now + 1); 1912 LOG.info("Overwritten " + regionInfos.size() + " regions to Meta"); 1913 LOG.debug("Overwritten regions: {} ", regionInfos); 1914 } 1915 1916 /** 1917 * Deletes merge qualifiers for the specified merge region. 1918 * @param connection connection we're using 1919 * @param mergeRegion the merged region 1920 */ 1921 public static void deleteMergeQualifiers(Connection connection, final RegionInfo mergeRegion) 1922 throws IOException { 1923 Delete delete = new Delete(mergeRegion.getRegionName()); 1924 // NOTE: We are doing a new hbase:meta read here. 1925 Cell [] cells = getMergeRegionsRaw(connection, mergeRegion.getRegionName()); 1926 if (cells == null || cells.length == 0) { 1927 return; 1928 } 1929 List<byte[]> qualifiers = new ArrayList<>(cells.length); 1930 for (Cell cell : cells) { 1931 byte[] qualifier = CellUtil.cloneQualifier(cell); 1932 qualifiers.add(qualifier); 1933 delete.addColumns(getCatalogFamily(), qualifier, HConstants.LATEST_TIMESTAMP); 1934 } 1935 deleteFromMetaTable(connection, delete); 1936 LOG.info("Deleted merge references in " + mergeRegion.getRegionNameAsString() + 1937 ", deleted qualifiers " + 1938 qualifiers.stream().map(Bytes::toStringBinary). 1939 collect(Collectors.joining(", "))); 1940 } 1941 1942 public static Put addRegionInfo(final Put p, final RegionInfo hri) 1943 throws IOException { 1944 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 1945 .setRow(p.getRow()) 1946 .setFamily(getCatalogFamily()) 1947 .setQualifier(HConstants.REGIONINFO_QUALIFIER) 1948 .setTimestamp(p.getTimestamp()) 1949 .setType(Type.Put) 1950 .setValue(RegionInfo.toByteArray(hri)) 1951 .build()); 1952 return p; 1953 } 1954 1955 public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId) 1956 throws IOException { 1957 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 1958 return p.add(builder.clear() 1959 .setRow(p.getRow()) 1960 .setFamily(getCatalogFamily()) 1961 .setQualifier(getServerColumn(replicaId)) 1962 .setTimestamp(p.getTimestamp()) 1963 .setType(Cell.Type.Put) 1964 .setValue(Bytes.toBytes(sn.getAddress().toString())) 1965 .build()) 1966 .add(builder.clear() 1967 .setRow(p.getRow()) 1968 .setFamily(getCatalogFamily()) 1969 .setQualifier(getStartCodeColumn(replicaId)) 1970 .setTimestamp(p.getTimestamp()) 1971 .setType(Cell.Type.Put) 1972 .setValue(Bytes.toBytes(sn.getStartcode())) 1973 .build()) 1974 .add(builder.clear() 1975 .setRow(p.getRow()) 1976 .setFamily(getCatalogFamily()) 1977 .setQualifier(getSeqNumColumn(replicaId)) 1978 .setTimestamp(p.getTimestamp()) 1979 .setType(Type.Put) 1980 .setValue(Bytes.toBytes(openSeqNum)) 1981 .build()); 1982 } 1983 1984 private static Put addEmptyLocation(Put p, int replicaId) throws IOException { 1985 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY); 1986 return p.add(builder.clear() 1987 .setRow(p.getRow()) 1988 .setFamily(getCatalogFamily()) 1989 .setQualifier(getServerColumn(replicaId)) 1990 .setTimestamp(p.getTimestamp()) 1991 .setType(Type.Put) 1992 .build()) 1993 .add(builder.clear() 1994 .setRow(p.getRow()) 1995 .setFamily(getCatalogFamily()) 1996 .setQualifier(getStartCodeColumn(replicaId)) 1997 .setTimestamp(p.getTimestamp()) 1998 .setType(Cell.Type.Put) 1999 .build()) 2000 .add(builder.clear() 2001 .setRow(p.getRow()) 2002 .setFamily(getCatalogFamily()) 2003 .setQualifier(getSeqNumColumn(replicaId)) 2004 .setTimestamp(p.getTimestamp()) 2005 .setType(Cell.Type.Put) 2006 .build()); 2007 } 2008 2009 2010 private static void debugLogMutations(List<? extends Mutation> mutations) throws IOException { 2011 if (!METALOG.isDebugEnabled()) { 2012 return; 2013 } 2014 // Logging each mutation in separate line makes it easier to see diff between them visually 2015 // because of common starting indentation. 2016 for (Mutation mutation : mutations) { 2017 debugLogMutation(mutation); 2018 } 2019 } 2020 2021 private static void debugLogMutation(Mutation p) throws IOException { 2022 METALOG.debug("{} {}", p.getClass().getSimpleName(), p.toJSON()); 2023 } 2024 2025 private static Put addSequenceNum(Put p, long openSeqNum, int replicaId) throws IOException { 2026 return p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY) 2027 .setRow(p.getRow()) 2028 .setFamily(HConstants.CATALOG_FAMILY) 2029 .setQualifier(getSeqNumColumn(replicaId)) 2030 .setTimestamp(p.getTimestamp()) 2031 .setType(Type.Put) 2032 .setValue(Bytes.toBytes(openSeqNum)) 2033 .build()); 2034 } 2035}