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