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 static org.apache.hadoop.hbase.util.FutureUtils.addListener; 021 022import java.io.IOException; 023import java.util.ArrayList; 024import java.util.Arrays; 025import java.util.Collections; 026import java.util.List; 027import java.util.Map; 028import java.util.NavigableMap; 029import java.util.Optional; 030import java.util.SortedMap; 031import java.util.concurrent.CompletableFuture; 032import java.util.regex.Matcher; 033import java.util.regex.Pattern; 034import java.util.stream.Collectors; 035import org.apache.hadoop.hbase.MetaTableAccessor.CollectingVisitor; 036import org.apache.hadoop.hbase.MetaTableAccessor.QueryType; 037import org.apache.hadoop.hbase.MetaTableAccessor.Visitor; 038import org.apache.hadoop.hbase.client.AdvancedScanResultConsumer; 039import org.apache.hadoop.hbase.client.AsyncTable; 040import org.apache.hadoop.hbase.client.Consistency; 041import org.apache.hadoop.hbase.client.Get; 042import org.apache.hadoop.hbase.client.RegionInfo; 043import org.apache.hadoop.hbase.client.RegionReplicaUtil; 044import org.apache.hadoop.hbase.client.Result; 045import org.apache.hadoop.hbase.client.Scan; 046import org.apache.hadoop.hbase.client.Scan.ReadType; 047import org.apache.hadoop.hbase.client.TableState; 048import org.apache.hadoop.hbase.exceptions.DeserializationException; 049import org.apache.hadoop.hbase.util.Bytes; 050import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 051import org.apache.hadoop.hbase.util.Pair; 052import org.apache.yetus.audience.InterfaceAudience; 053import org.slf4j.Logger; 054import org.slf4j.LoggerFactory; 055 056/** 057 * The asynchronous meta table accessor. Used to read/write region and assignment information store 058 * in <code>hbase:meta</code>. 059 * @since 2.0.0 060 */ 061@InterfaceAudience.Private 062public class AsyncMetaTableAccessor { 063 064 private static final Logger LOG = LoggerFactory.getLogger(AsyncMetaTableAccessor.class); 065 066 067 /** The delimiter for meta columns for replicaIds > 0 */ 068 private static final char META_REPLICA_ID_DELIMITER = '_'; 069 070 /** A regex for parsing server columns from meta. See above javadoc for meta layout */ 071 private static final Pattern SERVER_COLUMN_PATTERN = Pattern 072 .compile("^server(_[0-9a-fA-F]{4})?$"); 073 074 public static CompletableFuture<Boolean> tableExists(AsyncTable<?> metaTable, 075 TableName tableName) { 076 return getTableState(metaTable, tableName).thenApply(Optional::isPresent); 077 } 078 079 public static CompletableFuture<Optional<TableState>> getTableState(AsyncTable<?> metaTable, 080 TableName tableName) { 081 CompletableFuture<Optional<TableState>> future = new CompletableFuture<>(); 082 Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getStateColumn()); 083 long time = EnvironmentEdgeManager.currentTime(); 084 try { 085 get.setTimeRange(0, time); 086 addListener(metaTable.get(get), (result, error) -> { 087 if (error != null) { 088 future.completeExceptionally(error); 089 return; 090 } 091 try { 092 future.complete(getTableState(result)); 093 } catch (IOException e) { 094 future.completeExceptionally(e); 095 } 096 }); 097 } catch (IOException ioe) { 098 future.completeExceptionally(ioe); 099 } 100 return future; 101 } 102 103 /** 104 * Returns the HRegionLocation from meta for the given region 105 * @param metaTable 106 * @param regionName region we're looking for 107 * @return HRegionLocation for the given region 108 */ 109 public static CompletableFuture<Optional<HRegionLocation>> getRegionLocation( 110 AsyncTable<?> metaTable, byte[] regionName) { 111 CompletableFuture<Optional<HRegionLocation>> future = new CompletableFuture<>(); 112 try { 113 RegionInfo parsedRegionInfo = MetaTableAccessor.parseRegionInfoFromRegionName(regionName); 114 addListener(metaTable.get(new Get(MetaTableAccessor.getMetaKeyForRegion(parsedRegionInfo)) 115 .addFamily(HConstants.CATALOG_FAMILY)), (r, err) -> { 116 if (err != null) { 117 future.completeExceptionally(err); 118 return; 119 } 120 future.complete(getRegionLocations(r) 121 .map(locations -> locations.getRegionLocation(parsedRegionInfo.getReplicaId()))); 122 }); 123 } catch (IOException parseEx) { 124 LOG.warn("Failed to parse the passed region name: " + Bytes.toStringBinary(regionName)); 125 future.completeExceptionally(parseEx); 126 } 127 return future; 128 } 129 130 /** 131 * Returns the HRegionLocation from meta for the given encoded region name 132 * @param metaTable 133 * @param encodedRegionName region we're looking for 134 * @return HRegionLocation for the given region 135 */ 136 public static CompletableFuture<Optional<HRegionLocation>> getRegionLocationWithEncodedName( 137 AsyncTable<?> metaTable, byte[] encodedRegionName) { 138 CompletableFuture<Optional<HRegionLocation>> future = new CompletableFuture<>(); 139 addListener( 140 metaTable 141 .scanAll(new Scan().setReadType(ReadType.PREAD).addFamily(HConstants.CATALOG_FAMILY)), 142 (results, err) -> { 143 if (err != null) { 144 future.completeExceptionally(err); 145 return; 146 } 147 String encodedRegionNameStr = Bytes.toString(encodedRegionName); 148 results.stream().filter(result -> !result.isEmpty()) 149 .filter(result -> MetaTableAccessor.getRegionInfo(result) != null).forEach(result -> { 150 getRegionLocations(result).ifPresent(locations -> { 151 for (HRegionLocation location : locations.getRegionLocations()) { 152 if (location != null && 153 encodedRegionNameStr.equals(location.getRegion().getEncodedName())) { 154 future.complete(Optional.of(location)); 155 return; 156 } 157 } 158 }); 159 }); 160 future.complete(Optional.empty()); 161 }); 162 return future; 163 } 164 165 private static Optional<TableState> getTableState(Result r) throws IOException { 166 Cell cell = r.getColumnLatestCell(getTableFamily(), getStateColumn()); 167 if (cell == null) return Optional.empty(); 168 try { 169 return Optional.of(TableState.parseFrom( 170 TableName.valueOf(r.getRow()), 171 Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(), cell.getValueOffset() 172 + cell.getValueLength()))); 173 } catch (DeserializationException e) { 174 throw new IOException("Failed to parse table state from result: " + r, e); 175 } 176 } 177 178 /** 179 * Used to get all region locations for the specific table. 180 * @param metaTable 181 * @param tableName table we're looking for, can be null for getting all regions 182 * @return the list of region locations. The return value will be wrapped by a 183 * {@link CompletableFuture}. 184 */ 185 public static CompletableFuture<List<HRegionLocation>> getTableHRegionLocations( 186 AsyncTable<AdvancedScanResultConsumer> metaTable, TableName tableName) { 187 CompletableFuture<List<HRegionLocation>> future = new CompletableFuture<>(); 188 addListener(getTableRegionsAndLocations(metaTable, tableName, true), (locations, err) -> { 189 if (err != null) { 190 future.completeExceptionally(err); 191 } else if (locations == null || locations.isEmpty()) { 192 future.complete(Collections.emptyList()); 193 } else { 194 List<HRegionLocation> regionLocations = 195 locations.stream().map(loc -> new HRegionLocation(loc.getFirst(), loc.getSecond())) 196 .collect(Collectors.toList()); 197 future.complete(regionLocations); 198 } 199 }); 200 return future; 201 } 202 203 /** 204 * Used to get table regions' info and server. 205 * @param metaTable 206 * @param tableName table we're looking for, can be null for getting all regions 207 * @param excludeOfflinedSplitParents don't return split parents 208 * @return the list of regioninfos and server. The return value will be wrapped by a 209 * {@link CompletableFuture}. 210 */ 211 private static CompletableFuture<List<Pair<RegionInfo, ServerName>>> getTableRegionsAndLocations( 212 final AsyncTable<AdvancedScanResultConsumer> metaTable, 213 final TableName tableName, final boolean excludeOfflinedSplitParents) { 214 CompletableFuture<List<Pair<RegionInfo, ServerName>>> future = new CompletableFuture<>(); 215 if (TableName.META_TABLE_NAME.equals(tableName)) { 216 future.completeExceptionally(new IOException( 217 "This method can't be used to locate meta regions;" + " use MetaTableLocator instead")); 218 } 219 220 // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress 221 CollectingVisitor<Pair<RegionInfo, ServerName>> visitor = 222 new CollectingVisitor<Pair<RegionInfo, ServerName>>() { 223 private RegionLocations current = null; 224 225 @Override 226 public boolean visit(Result r) throws IOException { 227 Optional<RegionLocations> currentRegionLocations = getRegionLocations(r); 228 current = currentRegionLocations.orElse(null); 229 if (current == null || current.getRegionLocation().getRegion() == null) { 230 LOG.warn("No serialized RegionInfo in " + r); 231 return true; 232 } 233 RegionInfo hri = current.getRegionLocation().getRegion(); 234 if (excludeOfflinedSplitParents && hri.isSplitParent()) return true; 235 // Else call super and add this Result to the collection. 236 return super.visit(r); 237 } 238 239 @Override 240 void add(Result r) { 241 if (current == null) { 242 return; 243 } 244 for (HRegionLocation loc : current.getRegionLocations()) { 245 if (loc != null) { 246 this.results.add(new Pair<RegionInfo, ServerName>(loc.getRegion(), loc 247 .getServerName())); 248 } 249 } 250 } 251 }; 252 253 addListener(scanMeta(metaTable, tableName, QueryType.REGION, visitor), (v, error) -> { 254 if (error != null) { 255 future.completeExceptionally(error); 256 return; 257 } 258 future.complete(visitor.getResults()); 259 }); 260 return future; 261 } 262 263 /** 264 * Performs a scan of META table for given table. 265 * @param metaTable 266 * @param tableName table withing we scan 267 * @param type scanned part of meta 268 * @param visitor Visitor invoked against each row 269 */ 270 private static CompletableFuture<Void> scanMeta(AsyncTable<AdvancedScanResultConsumer> metaTable, 271 TableName tableName, QueryType type, final Visitor visitor) { 272 return scanMeta(metaTable, getTableStartRowForMeta(tableName, type), 273 getTableStopRowForMeta(tableName, type), type, Integer.MAX_VALUE, visitor); 274 } 275 276 /** 277 * Performs a scan of META table for given table. 278 * @param metaTable 279 * @param startRow Where to start the scan 280 * @param stopRow Where to stop the scan 281 * @param type scanned part of meta 282 * @param maxRows maximum rows to return 283 * @param visitor Visitor invoked against each row 284 */ 285 private static CompletableFuture<Void> scanMeta(AsyncTable<AdvancedScanResultConsumer> metaTable, 286 byte[] startRow, byte[] stopRow, QueryType type, int maxRows, final Visitor visitor) { 287 int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE; 288 Scan scan = getMetaScan(metaTable, rowUpperLimit); 289 for (byte[] family : type.getFamilies()) { 290 scan.addFamily(family); 291 } 292 if (startRow != null) { 293 scan.withStartRow(startRow); 294 } 295 if (stopRow != null) { 296 scan.withStopRow(stopRow); 297 } 298 299 if (LOG.isDebugEnabled()) { 300 LOG.debug("Scanning META" + " starting at row=" + Bytes.toStringBinary(scan.getStartRow()) 301 + " stopping at row=" + Bytes.toStringBinary(scan.getStopRow()) + " for max=" 302 + rowUpperLimit + " with caching=" + scan.getCaching()); 303 } 304 305 CompletableFuture<Void> future = new CompletableFuture<Void>(); 306 metaTable.scan(scan, new MetaTableScanResultConsumer(rowUpperLimit, visitor, future)); 307 return future; 308 } 309 310 private static final class MetaTableScanResultConsumer implements AdvancedScanResultConsumer { 311 312 private int currentRowCount; 313 314 private final int rowUpperLimit; 315 316 private final Visitor visitor; 317 318 private final CompletableFuture<Void> future; 319 320 MetaTableScanResultConsumer(int rowUpperLimit, Visitor visitor, 321 CompletableFuture<Void> future) { 322 this.rowUpperLimit = rowUpperLimit; 323 this.visitor = visitor; 324 this.future = future; 325 this.currentRowCount = 0; 326 } 327 328 @Override 329 public void onError(Throwable error) { 330 future.completeExceptionally(error); 331 } 332 333 @Override 334 @edu.umd.cs.findbugs.annotations.SuppressWarnings(value = "NP_NONNULL_PARAM_VIOLATION", 335 justification = "https://github.com/findbugsproject/findbugs/issues/79") 336 public void onComplete() { 337 future.complete(null); 338 } 339 340 @Override 341 public void onNext(Result[] results, ScanController controller) { 342 boolean terminateScan = false; 343 for (Result result : results) { 344 try { 345 if (!visitor.visit(result)) { 346 terminateScan = true; 347 break; 348 } 349 } catch (Exception e) { 350 future.completeExceptionally(e); 351 terminateScan = true; 352 break; 353 } 354 if (++currentRowCount >= rowUpperLimit) { 355 terminateScan = true; 356 break; 357 } 358 } 359 if (terminateScan) { 360 controller.terminate(); 361 } 362 } 363 } 364 365 private static Scan getMetaScan(AsyncTable<?> metaTable, int rowUpperLimit) { 366 Scan scan = new Scan(); 367 int scannerCaching = metaTable.getConfiguration().getInt(HConstants.HBASE_META_SCANNER_CACHING, 368 HConstants.DEFAULT_HBASE_META_SCANNER_CACHING); 369 if (metaTable.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS, 370 HConstants.DEFAULT_USE_META_REPLICAS)) { 371 scan.setConsistency(Consistency.TIMELINE); 372 } 373 if (rowUpperLimit <= scannerCaching) { 374 scan.setLimit(rowUpperLimit); 375 } 376 int rows = Math.min(rowUpperLimit, scannerCaching); 377 scan.setCaching(rows); 378 return scan; 379 } 380 381 /** 382 * Returns an HRegionLocationList extracted from the result. 383 * @return an HRegionLocationList containing all locations for the region range or null if we 384 * can't deserialize the result. 385 */ 386 private static Optional<RegionLocations> getRegionLocations(final Result r) { 387 if (r == null) return Optional.empty(); 388 Optional<RegionInfo> regionInfo = getHRegionInfo(r, getRegionInfoColumn()); 389 if (!regionInfo.isPresent()) return Optional.empty(); 390 391 List<HRegionLocation> locations = new ArrayList<HRegionLocation>(1); 392 NavigableMap<byte[], NavigableMap<byte[], byte[]>> familyMap = r.getNoVersionMap(); 393 394 locations.add(getRegionLocation(r, regionInfo.get(), 0)); 395 396 NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily()); 397 if (infoMap == null) return Optional.of(new RegionLocations(locations)); 398 399 // iterate until all serverName columns are seen 400 int replicaId = 0; 401 byte[] serverColumn = getServerColumn(replicaId); 402 SortedMap<byte[], byte[]> serverMap = null; 403 serverMap = infoMap.tailMap(serverColumn, false); 404 405 if (serverMap.isEmpty()) return Optional.of(new RegionLocations(locations)); 406 407 for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) { 408 replicaId = parseReplicaIdFromServerColumn(entry.getKey()); 409 if (replicaId < 0) { 410 break; 411 } 412 HRegionLocation location = getRegionLocation(r, regionInfo.get(), replicaId); 413 // In case the region replica is newly created, it's location might be null. We usually do not 414 // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs. 415 if (location == null || location.getServerName() == null) { 416 locations.add(null); 417 } else { 418 locations.add(location); 419 } 420 } 421 422 return Optional.of(new RegionLocations(locations)); 423 } 424 425 /** 426 * Returns the HRegionLocation parsed from the given meta row Result 427 * for the given regionInfo and replicaId. The regionInfo can be the default region info 428 * for the replica. 429 * @param r the meta row result 430 * @param regionInfo RegionInfo for default replica 431 * @param replicaId the replicaId for the HRegionLocation 432 * @return HRegionLocation parsed from the given meta row Result for the given replicaId 433 */ 434 private static HRegionLocation getRegionLocation(final Result r, final RegionInfo regionInfo, 435 final int replicaId) { 436 Optional<ServerName> serverName = getServerName(r, replicaId); 437 long seqNum = getSeqNumDuringOpen(r, replicaId); 438 RegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId); 439 return new HRegionLocation(replicaInfo, serverName.orElse(null), seqNum); 440 } 441 442 /** 443 * Returns a {@link ServerName} from catalog table {@link Result}. 444 * @param r Result to pull from 445 * @return A ServerName instance. 446 */ 447 private static Optional<ServerName> getServerName(final Result r, final int replicaId) { 448 byte[] serverColumn = getServerColumn(replicaId); 449 Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn); 450 if (cell == null || cell.getValueLength() == 0) return Optional.empty(); 451 String hostAndPort = Bytes.toString(cell.getValueArray(), cell.getValueOffset(), 452 cell.getValueLength()); 453 byte[] startcodeColumn = getStartCodeColumn(replicaId); 454 cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn); 455 if (cell == null || cell.getValueLength() == 0) return Optional.empty(); 456 try { 457 return Optional.of(ServerName.valueOf(hostAndPort, 458 Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()))); 459 } catch (IllegalArgumentException e) { 460 LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e); 461 return Optional.empty(); 462 } 463 } 464 465 /** 466 * The latest seqnum that the server writing to meta observed when opening the region. 467 * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written. 468 * @param r Result to pull the seqNum from 469 * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written. 470 */ 471 private static long getSeqNumDuringOpen(final Result r, final int replicaId) { 472 Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId)); 473 if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM; 474 return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()); 475 } 476 477 /** 478 * @param tableName table we're working with 479 * @return start row for scanning META according to query type 480 */ 481 private static byte[] getTableStartRowForMeta(TableName tableName, QueryType type) { 482 if (tableName == null) { 483 return null; 484 } 485 switch (type) { 486 case REGION: 487 case REPLICATION: { 488 byte[] startRow = new byte[tableName.getName().length + 2]; 489 System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length); 490 startRow[startRow.length - 2] = HConstants.DELIMITER; 491 startRow[startRow.length - 1] = HConstants.DELIMITER; 492 return startRow; 493 } 494 case ALL: 495 case TABLE: 496 default: { 497 return tableName.getName(); 498 } 499 } 500 } 501 502 /** 503 * @param tableName table we're working with 504 * @return stop row for scanning META according to query type 505 */ 506 private static byte[] getTableStopRowForMeta(TableName tableName, QueryType type) { 507 if (tableName == null) { 508 return null; 509 } 510 final byte[] stopRow; 511 switch (type) { 512 case REGION: 513 case REPLICATION: { 514 stopRow = new byte[tableName.getName().length + 3]; 515 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length); 516 stopRow[stopRow.length - 3] = ' '; 517 stopRow[stopRow.length - 2] = HConstants.DELIMITER; 518 stopRow[stopRow.length - 1] = HConstants.DELIMITER; 519 break; 520 } 521 case ALL: 522 case TABLE: 523 default: { 524 stopRow = new byte[tableName.getName().length + 1]; 525 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length); 526 stopRow[stopRow.length - 1] = ' '; 527 break; 528 } 529 } 530 return stopRow; 531 } 532 533 /** 534 * Returns the RegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and 535 * <code>qualifier</code> of the catalog table result. 536 * @param r a Result object from the catalog table scan 537 * @param qualifier Column family qualifier 538 * @return An RegionInfo instance. 539 */ 540 private static Optional<RegionInfo> getHRegionInfo(final Result r, byte[] qualifier) { 541 Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier); 542 if (cell == null) return Optional.empty(); 543 return Optional.ofNullable(RegionInfo.parseFromOrNull(cell.getValueArray(), 544 cell.getValueOffset(), cell.getValueLength())); 545 } 546 547 /** 548 * Returns the column family used for meta columns. 549 * @return HConstants.CATALOG_FAMILY. 550 */ 551 private static byte[] getCatalogFamily() { 552 return HConstants.CATALOG_FAMILY; 553 } 554 555 /** 556 * Returns the column family used for table columns. 557 * @return HConstants.TABLE_FAMILY. 558 */ 559 private static byte[] getTableFamily() { 560 return HConstants.TABLE_FAMILY; 561 } 562 563 /** 564 * Returns the column qualifier for serialized region info 565 * @return HConstants.REGIONINFO_QUALIFIER 566 */ 567 private static byte[] getRegionInfoColumn() { 568 return HConstants.REGIONINFO_QUALIFIER; 569 } 570 571 /** 572 * Returns the column qualifier for serialized table state 573 * @return HConstants.TABLE_STATE_QUALIFIER 574 */ 575 private static byte[] getStateColumn() { 576 return HConstants.TABLE_STATE_QUALIFIER; 577 } 578 579 /** 580 * Returns the column qualifier for server column for replicaId 581 * @param replicaId the replicaId of the region 582 * @return a byte[] for server column qualifier 583 */ 584 private static byte[] getServerColumn(int replicaId) { 585 return replicaId == 0 586 ? HConstants.SERVER_QUALIFIER 587 : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 588 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 589 } 590 591 /** 592 * Returns the column qualifier for server start code column for replicaId 593 * @param replicaId the replicaId of the region 594 * @return a byte[] for server start code column qualifier 595 */ 596 private static byte[] getStartCodeColumn(int replicaId) { 597 return replicaId == 0 598 ? HConstants.STARTCODE_QUALIFIER 599 : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 600 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 601 } 602 603 /** 604 * Returns the column qualifier for seqNum column for replicaId 605 * @param replicaId the replicaId of the region 606 * @return a byte[] for seqNum column qualifier 607 */ 608 private static byte[] getSeqNumColumn(int replicaId) { 609 return replicaId == 0 610 ? HConstants.SEQNUM_QUALIFIER 611 : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER 612 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId)); 613 } 614 615 /** 616 * Parses the replicaId from the server column qualifier. See top of the class javadoc 617 * for the actual meta layout 618 * @param serverColumn the column qualifier 619 * @return an int for the replicaId 620 */ 621 private static int parseReplicaIdFromServerColumn(byte[] serverColumn) { 622 String serverStr = Bytes.toString(serverColumn); 623 624 Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr); 625 if (matcher.matches() && matcher.groupCount() > 0) { 626 String group = matcher.group(1); 627 if (group != null && group.length() > 0) { 628 return Integer.parseInt(group.substring(1), 16); 629 } else { 630 return 0; 631 } 632 } 633 return -1; 634 } 635}