Revert " HBASE-23055 Alter hbase:meta (#667)"
[hbase.git] / hbase-client / src / main / java / org / apache / hadoop / hbase / MetaTableAccessor.java
blobad54324c7b3dde084b7ced3bf0eb6bebb0231990
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org.apache.hadoop.hbase;
20 import edu.umd.cs.findbugs.annotations.NonNull;
21 import edu.umd.cs.findbugs.annotations.Nullable;
22 import java.io.ByteArrayOutputStream;
23 import java.io.Closeable;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.Iterator;
30 import java.util.LinkedHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.NavigableMap;
34 import java.util.Set;
35 import java.util.SortedMap;
36 import java.util.regex.Matcher;
37 import java.util.regex.Pattern;
38 import java.util.stream.Collectors;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.hbase.Cell.Type;
41 import org.apache.hadoop.hbase.client.Connection;
42 import org.apache.hadoop.hbase.client.ConnectionFactory;
43 import org.apache.hadoop.hbase.client.Consistency;
44 import org.apache.hadoop.hbase.client.Delete;
45 import org.apache.hadoop.hbase.client.Get;
46 import org.apache.hadoop.hbase.client.Mutation;
47 import org.apache.hadoop.hbase.client.Put;
48 import org.apache.hadoop.hbase.client.RegionInfo;
49 import org.apache.hadoop.hbase.client.RegionInfoBuilder;
50 import org.apache.hadoop.hbase.client.RegionLocator;
51 import org.apache.hadoop.hbase.client.RegionReplicaUtil;
52 import org.apache.hadoop.hbase.client.Result;
53 import org.apache.hadoop.hbase.client.ResultScanner;
54 import org.apache.hadoop.hbase.client.Scan;
55 import org.apache.hadoop.hbase.client.Table;
56 import org.apache.hadoop.hbase.client.TableState;
57 import org.apache.hadoop.hbase.client.coprocessor.Batch;
58 import org.apache.hadoop.hbase.exceptions.DeserializationException;
59 import org.apache.hadoop.hbase.filter.Filter;
60 import org.apache.hadoop.hbase.filter.FirstKeyOnlyFilter;
61 import org.apache.hadoop.hbase.filter.RowFilter;
62 import org.apache.hadoop.hbase.filter.SubstringComparator;
63 import org.apache.hadoop.hbase.ipc.CoprocessorRpcUtils;
64 import org.apache.hadoop.hbase.ipc.ServerRpcController;
65 import org.apache.hadoop.hbase.master.RegionState;
66 import org.apache.hadoop.hbase.master.RegionState.State;
67 import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
68 import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
69 import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationService;
70 import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
71 import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsResponse;
72 import org.apache.hadoop.hbase.util.Bytes;
73 import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
74 import org.apache.hadoop.hbase.util.ExceptionUtil;
75 import org.apache.hadoop.hbase.util.Pair;
76 import org.apache.hadoop.hbase.util.PairOfSameType;
77 import org.apache.yetus.audience.InterfaceAudience;
78 import org.slf4j.Logger;
79 import org.slf4j.LoggerFactory;
81 import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
82 import org.apache.hbase.thirdparty.com.google.common.base.Throwables;
84 /**
85 * <p>
86 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
87 * to <code>hbase:meta</code>.
88 * </p>
89 * <p>
90 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is
91 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection
92 * (opened before each operation, closed right after), while when used on HM or HRS (like in
93 * AssignmentManager) we want permanent connection.
94 * </p>
95 * <p>
96 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table
97 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is
98 * called default replica.
99 * </p>
100 * <p>
101 * <h2>Meta layout</h2>
103 * <pre>
104 * For each table there is single row named for the table with a 'table' column family.
105 * The column family currently has one column in it, the 'state' column:
107 * table:state => contains table state
109 * Then for each table range ('Region'), there is a single row, formatted as:
110 * &lt;tableName&gt;,&lt;startKey&gt;,&lt;regionId&gt;,&lt;encodedRegionName&gt;.
111 * This row is the serialized regionName of the default region replica.
112 * Columns are:
113 * info:regioninfo => contains serialized HRI for the default region replica
114 * info:server => contains hostname:port (in string form) for the server hosting
115 * the default regionInfo replica
116 * info:server_&lt;replicaId&gt => contains hostname:port (in string form) for the server hosting
117 * the regionInfo replica with replicaId
118 * info:serverstartcode => contains server start code (in binary long form) for the server
119 * hosting the default regionInfo replica
120 * info:serverstartcode_&lt;replicaId&gt => contains server start code (in binary long form) for
121 * the server hosting the regionInfo replica with
122 * replicaId
123 * info:seqnumDuringOpen => contains seqNum (in binary long form) for the region at the time
124 * the server opened the region with default replicaId
125 * info:seqnumDuringOpen_&lt;replicaId&gt => contains seqNum (in binary long form) for the region
126 * at the time the server opened the region with
127 * replicaId
128 * info:splitA => contains a serialized HRI for the first daughter region if the
129 * region is split
130 * info:splitB => contains a serialized HRI for the second daughter region if the
131 * region is split
132 * info:merge* => contains a serialized HRI for a merge parent region. There will be two
133 * or more of these columns in a row. A row that has these columns is
134 * undergoing a merge and is the result of the merge. Columns listed
135 * in marge* columns are the parents of this merged region. Example
136 * columns: info:merge0001, info:merge0002. You make also see 'mergeA',
137 * and 'mergeB'. This is old form replaced by the new format that allows
138 * for more than two parents to be merged at a time.
139 * TODO: Add rep_barrier for serial replication explaination.
140 * </pre>
141 * </p>
142 * <p>
143 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not
144 * leak out of it (through Result objects, etc)
145 * </p>
147 @InterfaceAudience.Private
148 public class MetaTableAccessor {
150 private static final Logger LOG = LoggerFactory.getLogger(MetaTableAccessor.class);
151 private static final Logger METALOG = LoggerFactory.getLogger("org.apache.hadoop.hbase.META");
153 @VisibleForTesting
154 public static final byte[] REPLICATION_PARENT_QUALIFIER = Bytes.toBytes("parent");
156 private static final byte ESCAPE_BYTE = (byte) 0xFF;
158 private static final byte SEPARATED_BYTE = 0x00;
160 @InterfaceAudience.Private
161 public enum QueryType {
162 ALL(HConstants.TABLE_FAMILY, HConstants.CATALOG_FAMILY),
163 REGION(HConstants.CATALOG_FAMILY),
164 TABLE(HConstants.TABLE_FAMILY),
165 REPLICATION(HConstants.REPLICATION_BARRIER_FAMILY);
167 private final byte[][] families;
169 QueryType(byte[]... families) {
170 this.families = families;
173 byte[][] getFamilies() {
174 return this.families;
178 /** The delimiter for meta columns for replicaIds &gt; 0 */
179 static final char META_REPLICA_ID_DELIMITER = '_';
181 /** A regex for parsing server columns from meta. See above javadoc for meta layout */
182 private static final Pattern SERVER_COLUMN_PATTERN
183 = Pattern.compile("^server(_[0-9a-fA-F]{4})?$");
185 ////////////////////////
186 // Reading operations //
187 ////////////////////////
190 * Performs a full scan of <code>hbase:meta</code> for regions.
191 * @param connection connection we're using
192 * @param visitor Visitor invoked against each row in regions family.
194 public static void fullScanRegions(Connection connection, final Visitor visitor)
195 throws IOException {
196 scanMeta(connection, null, null, QueryType.REGION, visitor);
200 * Performs a full scan of <code>hbase:meta</code> for regions.
201 * @param connection connection we're using
203 public static List<Result> fullScanRegions(Connection connection) throws IOException {
204 return fullScan(connection, QueryType.REGION);
208 * Performs a full scan of <code>hbase:meta</code> for tables.
209 * @param connection connection we're using
210 * @param visitor Visitor invoked against each row in tables family.
212 public static void fullScanTables(Connection connection, final Visitor visitor)
213 throws IOException {
214 scanMeta(connection, null, null, QueryType.TABLE, visitor);
218 * Performs a full scan of <code>hbase:meta</code>.
219 * @param connection connection we're using
220 * @param type scanned part of meta
221 * @return List of {@link Result}
223 private static List<Result> fullScan(Connection connection, QueryType type) throws IOException {
224 CollectAllVisitor v = new CollectAllVisitor();
225 scanMeta(connection, null, null, type, v);
226 return v.getResults();
230 * Callers should call close on the returned {@link Table} instance.
231 * @param connection connection we're using to access Meta
232 * @return An {@link Table} for <code>hbase:meta</code>
234 public static Table getMetaHTable(final Connection connection)
235 throws IOException {
236 // We used to pass whole CatalogTracker in here, now we just pass in Connection
237 if (connection == null) {
238 throw new NullPointerException("No connection");
239 } else if (connection.isClosed()) {
240 throw new IOException("connection is closed");
242 return connection.getTable(TableName.META_TABLE_NAME);
246 * @param t Table to use (will be closed when done).
247 * @param g Get to run
249 private static Result get(final Table t, final Get g) throws IOException {
250 if (t == null) return null;
251 try {
252 return t.get(g);
253 } finally {
254 t.close();
259 * Gets the region info and assignment for the specified region.
260 * @param connection connection we're using
261 * @param regionName Region to lookup.
262 * @return Location and RegionInfo for <code>regionName</code>
263 * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
265 @Deprecated
266 public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte [] regionName)
267 throws IOException {
268 HRegionLocation location = getRegionLocation(connection, regionName);
269 return location == null
270 ? null
271 : new Pair<>(location.getRegion(), location.getServerName());
275 * Returns the HRegionLocation from meta for the given region
276 * @param connection connection we're using
277 * @param regionName region we're looking for
278 * @return HRegionLocation for the given region
280 public static HRegionLocation getRegionLocation(Connection connection, byte[] regionName)
281 throws IOException {
282 byte[] row = regionName;
283 RegionInfo parsedInfo = null;
284 try {
285 parsedInfo = parseRegionInfoFromRegionName(regionName);
286 row = getMetaKeyForRegion(parsedInfo);
287 } catch (Exception parseEx) {
288 // Ignore. This is used with tableName passed as regionName.
290 Get get = new Get(row);
291 get.addFamily(HConstants.CATALOG_FAMILY);
292 Result r = get(getMetaHTable(connection), get);
293 RegionLocations locations = getRegionLocations(r);
294 return locations == null ? null
295 : locations.getRegionLocation(parsedInfo == null ? 0 : parsedInfo.getReplicaId());
299 * Returns the HRegionLocation from meta for the given region
300 * @param connection connection we're using
301 * @param regionInfo region information
302 * @return HRegionLocation for the given region
304 public static HRegionLocation getRegionLocation(Connection connection, RegionInfo regionInfo)
305 throws IOException {
306 byte[] row = getMetaKeyForRegion(regionInfo);
307 Get get = new Get(row);
308 get.addFamily(HConstants.CATALOG_FAMILY);
309 Result r = get(getMetaHTable(connection), get);
310 return getRegionLocation(r, regionInfo, regionInfo.getReplicaId());
313 /** Returns the row key to use for this regionInfo */
314 public static byte[] getMetaKeyForRegion(RegionInfo regionInfo) {
315 return RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo).getRegionName();
318 /** Returns an HRI parsed from this regionName. Not all the fields of the HRI
319 * is stored in the name, so the returned object should only be used for the fields
320 * in the regionName.
322 public static RegionInfo parseRegionInfoFromRegionName(byte[] regionName) throws IOException {
323 byte[][] fields = RegionInfo.parseRegionName(regionName);
324 long regionId = Long.parseLong(Bytes.toString(fields[2]));
325 int replicaId = fields.length > 3 ? Integer.parseInt(Bytes.toString(fields[3]), 16) : 0;
326 return RegionInfoBuilder.newBuilder(TableName.valueOf(fields[0]))
327 .setStartKey(fields[1])
328 .setEndKey(fields[2])
329 .setSplit(false)
330 .setRegionId(regionId)
331 .setReplicaId(replicaId)
332 .build();
336 * Gets the result in hbase:meta for the specified region.
337 * @param connection connection we're using
338 * @param regionName region we're looking for
339 * @return result of the specified region
341 public static Result getRegionResult(Connection connection,
342 byte[] regionName) throws IOException {
343 Get get = new Get(regionName);
344 get.addFamily(HConstants.CATALOG_FAMILY);
345 return get(getMetaHTable(connection), get);
349 * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>,
350 * returning a single related <code>Result</code> instance if any row is found, null otherwise.
352 * @param connection the connection to query META table.
353 * @param regionEncodedName the region encoded name to look for at META.
354 * @return <code>Result</code> instance with the row related info in META, null otherwise.
355 * @throws IOException if any errors occur while querying META.
357 public static Result scanByRegionEncodedName(Connection connection,
358 String regionEncodedName) throws IOException {
359 RowFilter rowFilter = new RowFilter(CompareOperator.EQUAL,
360 new SubstringComparator(regionEncodedName));
361 Scan scan = getMetaScan(connection, 1);
362 scan.setFilter(rowFilter);
363 ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan);
364 return resultScanner.next();
368 * @return Return all regioninfos listed in the 'info:merge*' columns of
369 * the <code>regionName</code> row.
371 @Nullable
372 public static List<RegionInfo> getMergeRegions(Connection connection, byte[] regionName)
373 throws IOException {
374 return getMergeRegions(getRegionResult(connection, regionName).rawCells());
378 * @return Deserialized regioninfo values taken from column values that match
379 * the regex 'info:merge.*' in array of <code>cells</code>.
381 @Nullable
382 public static List<RegionInfo> getMergeRegions(Cell [] cells) {
383 if (cells == null) {
384 return null;
386 List<RegionInfo> regionsToMerge = null;
387 for (Cell cell: cells) {
388 if (!isMergeQualifierPrefix(cell)) {
389 continue;
391 // Ok. This cell is that of a info:merge* column.
392 RegionInfo ri = RegionInfo.parseFromOrNull(cell.getValueArray(), cell.getValueOffset(),
393 cell.getValueLength());
394 if (ri != null) {
395 if (regionsToMerge == null) {
396 regionsToMerge = new ArrayList<>();
398 regionsToMerge.add(ri);
401 return regionsToMerge;
405 * @return True if any merge regions present in <code>cells</code>; i.e.
406 * the column in <code>cell</code> matches the regex 'info:merge.*'.
408 public static boolean hasMergeRegions(Cell [] cells) {
409 for (Cell cell: cells) {
410 if (!isMergeQualifierPrefix(cell)) {
411 continue;
413 return true;
415 return false;
419 * @return True if the column in <code>cell</code> matches the regex 'info:merge.*'.
421 private static boolean isMergeQualifierPrefix(Cell cell) {
422 // Check to see if has family and that qualifier starts with the merge qualifier 'merge'
423 return CellUtil.matchingFamily(cell, HConstants.CATALOG_FAMILY) &&
424 PrivateCellUtil.qualifierStartsWith(cell, HConstants.MERGE_QUALIFIER_PREFIX);
428 * Checks if the specified table exists. Looks at the hbase:meta table hosted on
429 * the specified server.
430 * @param connection connection we're using
431 * @param tableName table to check
432 * @return true if the table exists in meta, false if not
434 public static boolean tableExists(Connection connection,
435 final TableName tableName)
436 throws IOException {
437 // Catalog tables always exist.
438 return tableName.equals(TableName.META_TABLE_NAME) ||
439 getTableState(connection, tableName) != null;
443 * Lists all of the regions currently in META.
445 * @param connection to connect with
446 * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions,
447 * true and we'll leave out offlined regions from returned list
448 * @return List of all user-space regions.
450 @VisibleForTesting
451 public static List<RegionInfo> getAllRegions(Connection connection,
452 boolean excludeOfflinedSplitParents)
453 throws IOException {
454 List<Pair<RegionInfo, ServerName>> result;
456 result = getTableRegionsAndLocations(connection, null,
457 excludeOfflinedSplitParents);
459 return getListOfRegionInfos(result);
464 * Gets all of the regions of the specified table. Do not use this method
465 * to get meta table regions, use methods in MetaTableLocator instead.
466 * @param connection connection we're using
467 * @param tableName table we're looking for
468 * @return Ordered list of {@link RegionInfo}.
470 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName)
471 throws IOException {
472 return getTableRegions(connection, tableName, false);
476 * Gets all of the regions of the specified table. Do not use this method
477 * to get meta table regions, use methods in MetaTableLocator instead.
478 * @param connection connection we're using
479 * @param tableName table we're looking for
480 * @param excludeOfflinedSplitParents If true, do not include offlined split
481 * parents in the return.
482 * @return Ordered list of {@link RegionInfo}.
484 public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName,
485 final boolean excludeOfflinedSplitParents) throws IOException {
486 List<Pair<RegionInfo, ServerName>> result =
487 getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents);
488 return getListOfRegionInfos(result);
491 private static List<RegionInfo> getListOfRegionInfos(
492 final List<Pair<RegionInfo, ServerName>> pairs) {
493 if (pairs == null || pairs.isEmpty()) {
494 return Collections.emptyList();
496 List<RegionInfo> result = new ArrayList<>(pairs.size());
497 for (Pair<RegionInfo, ServerName> pair : pairs) {
498 result.add(pair.getFirst());
500 return result;
504 * @param tableName table we're working with
505 * @return start row for scanning META according to query type
507 public static byte[] getTableStartRowForMeta(TableName tableName, QueryType type) {
508 if (tableName == null) {
509 return null;
511 switch (type) {
512 case REGION:
513 byte[] startRow = new byte[tableName.getName().length + 2];
514 System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length);
515 startRow[startRow.length - 2] = HConstants.DELIMITER;
516 startRow[startRow.length - 1] = HConstants.DELIMITER;
517 return startRow;
518 case ALL:
519 case TABLE:
520 default:
521 return tableName.getName();
526 * @param tableName table we're working with
527 * @return stop row for scanning META according to query type
529 public static byte[] getTableStopRowForMeta(TableName tableName, QueryType type) {
530 if (tableName == null) {
531 return null;
533 final byte[] stopRow;
534 switch (type) {
535 case REGION:
536 stopRow = new byte[tableName.getName().length + 3];
537 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
538 stopRow[stopRow.length - 3] = ' ';
539 stopRow[stopRow.length - 2] = HConstants.DELIMITER;
540 stopRow[stopRow.length - 1] = HConstants.DELIMITER;
541 break;
542 case ALL:
543 case TABLE:
544 default:
545 stopRow = new byte[tableName.getName().length + 1];
546 System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
547 stopRow[stopRow.length - 1] = ' ';
548 break;
550 return stopRow;
554 * This method creates a Scan object that will only scan catalog rows that
555 * belong to the specified table. It doesn't specify any columns.
556 * This is a better alternative to just using a start row and scan until
557 * it hits a new table since that requires parsing the HRI to get the table
558 * name.
559 * @param tableName bytes of table's name
560 * @return configured Scan object
562 @Deprecated
563 public static Scan getScanForTableName(Connection connection, TableName tableName) {
564 // Start key is just the table name with delimiters
565 byte[] startKey = getTableStartRowForMeta(tableName, QueryType.REGION);
566 // Stop key appends the smallest possible char to the table name
567 byte[] stopKey = getTableStopRowForMeta(tableName, QueryType.REGION);
569 Scan scan = getMetaScan(connection, -1);
570 scan.setStartRow(startKey);
571 scan.setStopRow(stopKey);
572 return scan;
575 private static Scan getMetaScan(Connection connection, int rowUpperLimit) {
576 Scan scan = new Scan();
577 int scannerCaching = connection.getConfiguration()
578 .getInt(HConstants.HBASE_META_SCANNER_CACHING,
579 HConstants.DEFAULT_HBASE_META_SCANNER_CACHING);
580 if (connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS,
581 HConstants.DEFAULT_USE_META_REPLICAS)) {
582 scan.setConsistency(Consistency.TIMELINE);
584 if (rowUpperLimit > 0) {
585 scan.setLimit(rowUpperLimit);
586 scan.setReadType(Scan.ReadType.PREAD);
588 scan.setCaching(scannerCaching);
589 return scan;
592 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
593 * @param connection connection we're using
594 * @param tableName table we're looking for
595 * @return Return list of regioninfos and server.
597 public static List<Pair<RegionInfo, ServerName>>
598 getTableRegionsAndLocations(Connection connection, TableName tableName)
599 throws IOException {
600 return getTableRegionsAndLocations(connection, tableName, true);
604 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
605 * @param connection connection we're using
606 * @param tableName table to work with, can be null for getting all regions
607 * @param excludeOfflinedSplitParents don't return split parents
608 * @return Return list of regioninfos and server addresses.
610 public static List<Pair<RegionInfo, ServerName>> getTableRegionsAndLocations(
611 Connection connection, @Nullable final TableName tableName,
612 final boolean excludeOfflinedSplitParents) throws IOException {
613 if (tableName != null && tableName.equals(TableName.META_TABLE_NAME)) {
614 throw new IOException("This method can't be used to locate meta regions;"
615 + " use MetaTableLocator instead");
617 // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress
618 CollectingVisitor<Pair<RegionInfo, ServerName>> visitor =
619 new CollectingVisitor<Pair<RegionInfo, ServerName>>() {
620 private RegionLocations current = null;
622 @Override
623 public boolean visit(Result r) throws IOException {
624 current = getRegionLocations(r);
625 if (current == null || current.getRegionLocation().getRegion() == null) {
626 LOG.warn("No serialized RegionInfo in " + r);
627 return true;
629 RegionInfo hri = current.getRegionLocation().getRegion();
630 if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
631 // Else call super and add this Result to the collection.
632 return super.visit(r);
635 @Override
636 void add(Result r) {
637 if (current == null) {
638 return;
640 for (HRegionLocation loc : current.getRegionLocations()) {
641 if (loc != null) {
642 this.results.add(new Pair<>(loc.getRegion(), loc.getServerName()));
647 scanMeta(connection,
648 getTableStartRowForMeta(tableName, QueryType.REGION),
649 getTableStopRowForMeta(tableName, QueryType.REGION),
650 QueryType.REGION, visitor);
651 return visitor.getResults();
654 public static void fullScanMetaAndPrint(Connection connection)
655 throws IOException {
656 Visitor v = r -> {
657 if (r == null || r.isEmpty()) {
658 return true;
660 LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
661 TableState state = getTableState(r);
662 if (state != null) {
663 LOG.info("fullScanMetaAndPrint.Table State={}" + state);
664 } else {
665 RegionLocations locations = getRegionLocations(r);
666 if (locations == null) {
667 return true;
669 for (HRegionLocation loc : locations.getRegionLocations()) {
670 if (loc != null) {
671 LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion());
675 return true;
677 scanMeta(connection, null, null, QueryType.ALL, v);
680 public static void scanMetaForTableRegions(Connection connection, Visitor visitor,
681 TableName tableName) throws IOException {
682 scanMeta(connection, tableName, QueryType.REGION, Integer.MAX_VALUE, visitor);
685 private static void scanMeta(Connection connection, TableName table, QueryType type, int maxRows,
686 final Visitor visitor) throws IOException {
687 scanMeta(connection, getTableStartRowForMeta(table, type), getTableStopRowForMeta(table, type),
688 type, maxRows, visitor);
691 private static void scanMeta(Connection connection, @Nullable final byte[] startRow,
692 @Nullable final byte[] stopRow, QueryType type, final Visitor visitor) throws IOException {
693 scanMeta(connection, startRow, stopRow, type, Integer.MAX_VALUE, visitor);
697 * Performs a scan of META table for given table starting from given row.
698 * @param connection connection we're using
699 * @param visitor visitor to call
700 * @param tableName table withing we scan
701 * @param row start scan from this row
702 * @param rowLimit max number of rows to return
704 public static void scanMeta(Connection connection, final Visitor visitor,
705 final TableName tableName, final byte[] row, final int rowLimit) throws IOException {
706 byte[] startRow = null;
707 byte[] stopRow = null;
708 if (tableName != null) {
709 startRow = getTableStartRowForMeta(tableName, QueryType.REGION);
710 if (row != null) {
711 RegionInfo closestRi = getClosestRegionInfo(connection, tableName, row);
712 startRow =
713 RegionInfo.createRegionName(tableName, closestRi.getStartKey(), HConstants.ZEROES, false);
715 stopRow = getTableStopRowForMeta(tableName, QueryType.REGION);
717 scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor);
721 * Performs a scan of META table.
722 * @param connection connection we're using
723 * @param startRow Where to start the scan. Pass null if want to begin scan
724 * at first row.
725 * @param stopRow Where to stop the scan. Pass null if want to scan all rows
726 * from the start one
727 * @param type scanned part of meta
728 * @param maxRows maximum rows to return
729 * @param visitor Visitor invoked against each row.
731 static void scanMeta(Connection connection, @Nullable final byte[] startRow,
732 @Nullable final byte[] stopRow, QueryType type, int maxRows, final Visitor visitor)
733 throws IOException {
734 scanMeta(connection, startRow, stopRow, type, null, maxRows, visitor);
737 private static void scanMeta(Connection connection, @Nullable final byte[] startRow,
738 @Nullable final byte[] stopRow, QueryType type, @Nullable Filter filter, int maxRows,
739 final Visitor visitor) throws IOException {
740 int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE;
741 Scan scan = getMetaScan(connection, rowUpperLimit);
743 for (byte[] family : type.getFamilies()) {
744 scan.addFamily(family);
746 if (startRow != null) {
747 scan.withStartRow(startRow);
749 if (stopRow != null) {
750 scan.withStopRow(stopRow);
752 if (filter != null) {
753 scan.setFilter(filter);
756 if (LOG.isTraceEnabled()) {
757 LOG.trace("Scanning META" + " starting at row=" + Bytes.toStringBinary(startRow) +
758 " stopping at row=" + Bytes.toStringBinary(stopRow) + " for max=" + rowUpperLimit +
759 " with caching=" + scan.getCaching());
762 int currentRow = 0;
763 try (Table metaTable = getMetaHTable(connection)) {
764 try (ResultScanner scanner = metaTable.getScanner(scan)) {
765 Result data;
766 while ((data = scanner.next()) != null) {
767 if (data.isEmpty()) continue;
768 // Break if visit returns false.
769 if (!visitor.visit(data)) break;
770 if (++currentRow >= rowUpperLimit) break;
774 if (visitor instanceof Closeable) {
775 try {
776 ((Closeable) visitor).close();
777 } catch (Throwable t) {
778 ExceptionUtil.rethrowIfInterrupt(t);
779 LOG.debug("Got exception in closing the meta scanner visitor", t);
785 * @return Get closest metatable region row to passed <code>row</code>
787 @NonNull
788 private static RegionInfo getClosestRegionInfo(Connection connection,
789 @NonNull final TableName tableName, @NonNull final byte[] row) throws IOException {
790 byte[] searchRow = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
791 Scan scan = getMetaScan(connection, 1);
792 scan.setReversed(true);
793 scan.withStartRow(searchRow);
794 try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) {
795 Result result = resultScanner.next();
796 if (result == null) {
797 throw new TableNotFoundException("Cannot find row in META " +
798 " for table: " + tableName + ", row=" + Bytes.toStringBinary(row));
800 RegionInfo regionInfo = getRegionInfo(result);
801 if (regionInfo == null) {
802 throw new IOException("RegionInfo was null or empty in Meta for " +
803 tableName + ", row=" + Bytes.toStringBinary(row));
805 return regionInfo;
810 * Returns the column family used for meta columns.
811 * @return HConstants.CATALOG_FAMILY.
813 public static byte[] getCatalogFamily() {
814 return HConstants.CATALOG_FAMILY;
818 * Returns the column family used for table columns.
819 * @return HConstants.TABLE_FAMILY.
821 private static byte[] getTableFamily() {
822 return HConstants.TABLE_FAMILY;
826 * Returns the column qualifier for serialized region info
827 * @return HConstants.REGIONINFO_QUALIFIER
829 public static byte[] getRegionInfoColumn() {
830 return HConstants.REGIONINFO_QUALIFIER;
834 * Returns the column qualifier for serialized table state
835 * @return HConstants.TABLE_STATE_QUALIFIER
837 private static byte[] getTableStateColumn() {
838 return HConstants.TABLE_STATE_QUALIFIER;
842 * Returns the column qualifier for serialized region state
843 * @return HConstants.STATE_QUALIFIER
845 private static byte[] getRegionStateColumn() {
846 return HConstants.STATE_QUALIFIER;
850 * Returns the column qualifier for serialized region state
851 * @param replicaId the replicaId of the region
852 * @return a byte[] for state qualifier
854 @VisibleForTesting
855 static byte[] getRegionStateColumn(int replicaId) {
856 return replicaId == 0 ? HConstants.STATE_QUALIFIER
857 : Bytes.toBytes(HConstants.STATE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
858 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
862 * Returns the column qualifier for serialized region state
863 * @param replicaId the replicaId of the region
864 * @return a byte[] for sn column qualifier
866 @VisibleForTesting
867 static byte[] getServerNameColumn(int replicaId) {
868 return replicaId == 0 ? HConstants.SERVERNAME_QUALIFIER
869 : Bytes.toBytes(HConstants.SERVERNAME_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
870 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
874 * Returns the column qualifier for server column for replicaId
875 * @param replicaId the replicaId of the region
876 * @return a byte[] for server column qualifier
878 @VisibleForTesting
879 public static byte[] getServerColumn(int replicaId) {
880 return replicaId == 0
881 ? HConstants.SERVER_QUALIFIER
882 : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
883 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
887 * Returns the column qualifier for server start code column for replicaId
888 * @param replicaId the replicaId of the region
889 * @return a byte[] for server start code column qualifier
891 @VisibleForTesting
892 public static byte[] getStartCodeColumn(int replicaId) {
893 return replicaId == 0
894 ? HConstants.STARTCODE_QUALIFIER
895 : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
896 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
900 * Returns the column qualifier for seqNum column for replicaId
901 * @param replicaId the replicaId of the region
902 * @return a byte[] for seqNum column qualifier
904 @VisibleForTesting
905 public static byte[] getSeqNumColumn(int replicaId) {
906 return replicaId == 0
907 ? HConstants.SEQNUM_QUALIFIER
908 : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
909 + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
913 * Parses the replicaId from the server column qualifier. See top of the class javadoc
914 * for the actual meta layout
915 * @param serverColumn the column qualifier
916 * @return an int for the replicaId
918 @VisibleForTesting
919 static int parseReplicaIdFromServerColumn(byte[] serverColumn) {
920 String serverStr = Bytes.toString(serverColumn);
922 Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr);
923 if (matcher.matches() && matcher.groupCount() > 0) {
924 String group = matcher.group(1);
925 if (group != null && group.length() > 0) {
926 return Integer.parseInt(group.substring(1), 16);
927 } else {
928 return 0;
931 return -1;
935 * Returns a {@link ServerName} from catalog table {@link Result}.
936 * @param r Result to pull from
937 * @return A ServerName instance or null if necessary fields not found or empty.
939 @Nullable
940 @InterfaceAudience.Private // for use by HMaster#getTableRegionRow which is used for testing only
941 public static ServerName getServerName(final Result r, final int replicaId) {
942 byte[] serverColumn = getServerColumn(replicaId);
943 Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn);
944 if (cell == null || cell.getValueLength() == 0) return null;
945 String hostAndPort = Bytes.toString(
946 cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
947 byte[] startcodeColumn = getStartCodeColumn(replicaId);
948 cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn);
949 if (cell == null || cell.getValueLength() == 0) return null;
950 try {
951 return ServerName.valueOf(hostAndPort,
952 Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
953 } catch (IllegalArgumentException e) {
954 LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e);
955 return null;
960 * The latest seqnum that the server writing to meta observed when opening the region.
961 * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written.
962 * @param r Result to pull the seqNum from
963 * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
965 private static long getSeqNumDuringOpen(final Result r, final int replicaId) {
966 Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId));
967 if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM;
968 return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
972 * Returns the daughter regions by reading the corresponding columns of the catalog table
973 * Result.
974 * @param data a Result object from the catalog table scan
975 * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent
977 public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) {
978 RegionInfo splitA = getRegionInfo(data, HConstants.SPLITA_QUALIFIER);
979 RegionInfo splitB = getRegionInfo(data, HConstants.SPLITB_QUALIFIER);
980 return new PairOfSameType<>(splitA, splitB);
984 * Returns an HRegionLocationList extracted from the result.
985 * @return an HRegionLocationList containing all locations for the region range or null if
986 * we can't deserialize the result.
988 @Nullable
989 public static RegionLocations getRegionLocations(final Result r) {
990 if (r == null) return null;
991 RegionInfo regionInfo = getRegionInfo(r, getRegionInfoColumn());
992 if (regionInfo == null) return null;
994 List<HRegionLocation> locations = new ArrayList<>(1);
995 NavigableMap<byte[],NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap();
997 locations.add(getRegionLocation(r, regionInfo, 0));
999 NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily());
1000 if (infoMap == null) return new RegionLocations(locations);
1002 // iterate until all serverName columns are seen
1003 int replicaId = 0;
1004 byte[] serverColumn = getServerColumn(replicaId);
1005 SortedMap<byte[], byte[]> serverMap;
1006 serverMap = infoMap.tailMap(serverColumn, false);
1008 if (serverMap.isEmpty()) return new RegionLocations(locations);
1010 for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) {
1011 replicaId = parseReplicaIdFromServerColumn(entry.getKey());
1012 if (replicaId < 0) {
1013 break;
1015 HRegionLocation location = getRegionLocation(r, regionInfo, replicaId);
1016 // In case the region replica is newly created, it's location might be null. We usually do not
1017 // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs.
1018 if (location.getServerName() == null) {
1019 locations.add(null);
1020 } else {
1021 locations.add(location);
1025 return new RegionLocations(locations);
1029 * Returns the HRegionLocation parsed from the given meta row Result
1030 * for the given regionInfo and replicaId. The regionInfo can be the default region info
1031 * for the replica.
1032 * @param r the meta row result
1033 * @param regionInfo RegionInfo for default replica
1034 * @param replicaId the replicaId for the HRegionLocation
1035 * @return HRegionLocation parsed from the given meta row Result for the given replicaId
1037 private static HRegionLocation getRegionLocation(final Result r, final RegionInfo regionInfo,
1038 final int replicaId) {
1039 ServerName serverName = getServerName(r, replicaId);
1040 long seqNum = getSeqNumDuringOpen(r, replicaId);
1041 RegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId);
1042 return new HRegionLocation(replicaInfo, serverName, seqNum);
1046 * Returns RegionInfo object from the column
1047 * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
1048 * table Result.
1049 * @param data a Result object from the catalog table scan
1050 * @return RegionInfo or null
1052 public static RegionInfo getRegionInfo(Result data) {
1053 return getRegionInfo(data, HConstants.REGIONINFO_QUALIFIER);
1057 * Returns the RegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
1058 * <code>qualifier</code> of the catalog table result.
1059 * @param r a Result object from the catalog table scan
1060 * @param qualifier Column family qualifier
1061 * @return An RegionInfo instance or null.
1063 @Nullable
1064 public static RegionInfo getRegionInfo(final Result r, byte [] qualifier) {
1065 Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier);
1066 if (cell == null) return null;
1067 return RegionInfo.parseFromOrNull(cell.getValueArray(),
1068 cell.getValueOffset(), cell.getValueLength());
1072 * Fetch table state for given table from META table
1073 * @param conn connection to use
1074 * @param tableName table to fetch state for
1076 @Nullable
1077 public static TableState getTableState(Connection conn, TableName tableName)
1078 throws IOException {
1079 if (tableName.equals(TableName.META_TABLE_NAME)) {
1080 return new TableState(tableName, TableState.State.ENABLED);
1082 Table metaHTable = getMetaHTable(conn);
1083 Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getTableStateColumn());
1084 Result result = metaHTable.get(get);
1085 return getTableState(result);
1089 * Fetch table states from META table
1090 * @param conn connection to use
1091 * @return map {tableName -&gt; state}
1093 public static Map<TableName, TableState> getTableStates(Connection conn)
1094 throws IOException {
1095 final Map<TableName, TableState> states = new LinkedHashMap<>();
1096 Visitor collector = r -> {
1097 TableState state = getTableState(r);
1098 if (state != null) {
1099 states.put(state.getTableName(), state);
1101 return true;
1103 fullScanTables(conn, collector);
1104 return states;
1108 * Updates state in META
1109 * @param conn connection to use
1110 * @param tableName table to look for
1112 public static void updateTableState(Connection conn, TableName tableName,
1113 TableState.State actual) throws IOException {
1114 updateTableState(conn, new TableState(tableName, actual));
1118 * Decode table state from META Result.
1119 * Should contain cell from HConstants.TABLE_FAMILY
1120 * @return null if not found
1122 @Nullable
1123 public static TableState getTableState(Result r) throws IOException {
1124 Cell cell = r.getColumnLatestCell(getTableFamily(), getTableStateColumn());
1125 if (cell == null) {
1126 return null;
1128 try {
1129 return TableState.parseFrom(TableName.valueOf(r.getRow()),
1130 Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(),
1131 cell.getValueOffset() + cell.getValueLength()));
1132 } catch (DeserializationException e) {
1133 throw new IOException(e);
1138 * Implementations 'visit' a catalog table row.
1140 public interface Visitor {
1142 * Visit the catalog table row.
1143 * @param r A row from catalog table
1144 * @return True if we are to proceed scanning the table, else false if
1145 * we are to stop now.
1147 boolean visit(final Result r) throws IOException;
1151 * Implementations 'visit' a catalog table row but with close() at the end.
1153 public interface CloseableVisitor extends Visitor, Closeable {
1157 * A {@link Visitor} that collects content out of passed {@link Result}.
1159 static abstract class CollectingVisitor<T> implements Visitor {
1160 final List<T> results = new ArrayList<>();
1161 @Override
1162 public boolean visit(Result r) throws IOException {
1163 if (r != null && !r.isEmpty()) {
1164 add(r);
1166 return true;
1169 abstract void add(Result r);
1172 * @return Collected results; wait till visits complete to collect all
1173 * possible results
1175 List<T> getResults() {
1176 return this.results;
1181 * Collects all returned.
1183 static class CollectAllVisitor extends CollectingVisitor<Result> {
1184 @Override
1185 void add(Result r) {
1186 this.results.add(r);
1191 * A Visitor that skips offline regions and split parents
1193 public static abstract class DefaultVisitorBase implements Visitor {
1195 DefaultVisitorBase() {
1196 super();
1199 public abstract boolean visitInternal(Result rowResult) throws IOException;
1201 @Override
1202 public boolean visit(Result rowResult) throws IOException {
1203 RegionInfo info = getRegionInfo(rowResult);
1204 if (info == null) {
1205 return true;
1208 //skip over offline and split regions
1209 if (!(info.isOffline() || info.isSplit())) {
1210 return visitInternal(rowResult);
1212 return true;
1217 * Count regions in <code>hbase:meta</code> for passed table.
1218 * @param c Configuration object
1219 * @param tableName table name to count regions for
1220 * @return Count or regions in table <code>tableName</code>
1222 public static int getRegionCount(final Configuration c, final TableName tableName)
1223 throws IOException {
1224 try (Connection connection = ConnectionFactory.createConnection(c)) {
1225 return getRegionCount(connection, tableName);
1230 * Count regions in <code>hbase:meta</code> for passed table.
1231 * @param connection Connection object
1232 * @param tableName table name to count regions for
1233 * @return Count or regions in table <code>tableName</code>
1235 public static int getRegionCount(final Connection connection, final TableName tableName)
1236 throws IOException {
1237 try (RegionLocator locator = connection.getRegionLocator(tableName)) {
1238 List<HRegionLocation> locations = locator.getAllRegionLocations();
1239 return locations == null ? 0 : locations.size();
1243 ////////////////////////
1244 // Editing operations //
1245 ////////////////////////
1247 * Generates and returns a Put containing the region into for the catalog table
1249 public static Put makePutFromRegionInfo(RegionInfo regionInfo, long ts) throws IOException {
1250 Put put = new Put(regionInfo.getRegionName(), ts);
1251 addRegionInfo(put, regionInfo);
1252 return put;
1256 * Generates and returns a Delete containing the region info for the catalog table
1258 private static Delete makeDeleteFromRegionInfo(RegionInfo regionInfo, long ts) {
1259 if (regionInfo == null) {
1260 throw new IllegalArgumentException("Can't make a delete for null region");
1262 Delete delete = new Delete(regionInfo.getRegionName());
1263 delete.addFamily(getCatalogFamily(), ts);
1264 return delete;
1268 * Adds split daughters to the Put
1270 private static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB)
1271 throws IOException {
1272 if (splitA != null) {
1273 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1274 .setRow(put.getRow())
1275 .setFamily(HConstants.CATALOG_FAMILY)
1276 .setQualifier(HConstants.SPLITA_QUALIFIER)
1277 .setTimestamp(put.getTimestamp())
1278 .setType(Type.Put)
1279 .setValue(RegionInfo.toByteArray(splitA))
1280 .build());
1282 if (splitB != null) {
1283 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1284 .setRow(put.getRow())
1285 .setFamily(HConstants.CATALOG_FAMILY)
1286 .setQualifier(HConstants.SPLITB_QUALIFIER)
1287 .setTimestamp(put.getTimestamp())
1288 .setType(Type.Put)
1289 .setValue(RegionInfo.toByteArray(splitB))
1290 .build());
1292 return put;
1296 * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
1297 * @param connection connection we're using
1298 * @param p Put to add to hbase:meta
1300 private static void putToMetaTable(Connection connection, Put p) throws IOException {
1301 try (Table table = getMetaHTable(connection)) {
1302 put(table, p);
1307 * @param t Table to use
1308 * @param p put to make
1310 private static void put(Table t, Put p) throws IOException {
1311 debugLogMutation(p);
1312 t.put(p);
1316 * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
1317 * @param connection connection we're using
1318 * @param ps Put to add to hbase:meta
1320 public static void putsToMetaTable(final Connection connection, final List<Put> ps)
1321 throws IOException {
1322 if (ps.isEmpty()) {
1323 return;
1325 try (Table t = getMetaHTable(connection)) {
1326 debugLogMutations(ps);
1327 // the implementation for putting a single Put is much simpler so here we do a check first.
1328 if (ps.size() == 1) {
1329 t.put(ps.get(0));
1330 } else {
1331 t.put(ps);
1337 * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
1338 * @param connection connection we're using
1339 * @param d Delete to add to hbase:meta
1341 private static void deleteFromMetaTable(final Connection connection, final Delete d)
1342 throws IOException {
1343 List<Delete> dels = new ArrayList<>(1);
1344 dels.add(d);
1345 deleteFromMetaTable(connection, dels);
1349 * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
1350 * @param connection connection we're using
1351 * @param deletes Deletes to add to hbase:meta This list should support #remove.
1353 private static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes)
1354 throws IOException {
1355 try (Table t = getMetaHTable(connection)) {
1356 debugLogMutations(deletes);
1357 t.delete(deletes);
1362 * Deletes some replica columns corresponding to replicas for the passed rows
1363 * @param metaRows rows in hbase:meta
1364 * @param replicaIndexToDeleteFrom the replica ID we would start deleting from
1365 * @param numReplicasToRemove how many replicas to remove
1366 * @param connection connection we're using to access meta table
1368 public static void removeRegionReplicasFromMeta(Set<byte[]> metaRows,
1369 int replicaIndexToDeleteFrom, int numReplicasToRemove, Connection connection)
1370 throws IOException {
1371 int absoluteIndex = replicaIndexToDeleteFrom + numReplicasToRemove;
1372 for (byte[] row : metaRows) {
1373 long now = EnvironmentEdgeManager.currentTime();
1374 Delete deleteReplicaLocations = new Delete(row);
1375 for (int i = replicaIndexToDeleteFrom; i < absoluteIndex; i++) {
1376 deleteReplicaLocations.addColumns(getCatalogFamily(),
1377 getServerColumn(i), now);
1378 deleteReplicaLocations.addColumns(getCatalogFamily(),
1379 getSeqNumColumn(i), now);
1380 deleteReplicaLocations.addColumns(getCatalogFamily(),
1381 getStartCodeColumn(i), now);
1382 deleteReplicaLocations.addColumns(getCatalogFamily(), getServerNameColumn(i), now);
1383 deleteReplicaLocations.addColumns(getCatalogFamily(), getRegionStateColumn(i), now);
1386 deleteFromMetaTable(connection, deleteReplicaLocations);
1390 private static void addRegionStateToPut(Put put, RegionState.State state) throws IOException {
1391 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1392 .setRow(put.getRow())
1393 .setFamily(HConstants.CATALOG_FAMILY)
1394 .setQualifier(getRegionStateColumn())
1395 .setTimestamp(put.getTimestamp())
1396 .setType(Cell.Type.Put)
1397 .setValue(Bytes.toBytes(state.name()))
1398 .build());
1402 * Adds daughter region infos to hbase:meta row for the specified region. Note that this does not
1403 * add its daughter's as different rows, but adds information about the daughters in the same row
1404 * as the parent. Use
1405 * {@link #splitRegion(Connection, RegionInfo, long, RegionInfo, RegionInfo, ServerName, int)}
1406 * if you want to do that.
1407 * @param connection connection we're using
1408 * @param regionInfo RegionInfo of parent region
1409 * @param splitA first split daughter of the parent regionInfo
1410 * @param splitB second split daughter of the parent regionInfo
1411 * @throws IOException if problem connecting or updating meta
1413 public static void addSplitsToParent(Connection connection, RegionInfo regionInfo,
1414 RegionInfo splitA, RegionInfo splitB) throws IOException {
1415 try (Table meta = getMetaHTable(connection)) {
1416 Put put = makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime());
1417 addDaughtersToPut(put, splitA, splitB);
1418 meta.put(put);
1419 debugLogMutation(put);
1420 LOG.debug("Added region {}", regionInfo.getRegionNameAsString());
1425 * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1426 * does not add its daughter's as different rows, but adds information about the daughters
1427 * in the same row as the parent. Use
1428 * {@link #splitRegion(Connection, RegionInfo, long, RegionInfo, RegionInfo, ServerName, int)}
1429 * if you want to do that.
1430 * @param connection connection we're using
1431 * @param regionInfo region information
1432 * @throws IOException if problem connecting or updating meta
1434 @VisibleForTesting
1435 public static void addRegionToMeta(Connection connection, RegionInfo regionInfo)
1436 throws IOException {
1437 addRegionsToMeta(connection, Collections.singletonList(regionInfo), 1);
1441 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions
1442 * is CLOSED.
1443 * @param connection connection we're using
1444 * @param regionInfos region information list
1445 * @throws IOException if problem connecting or updating meta
1447 public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
1448 int regionReplication) throws IOException {
1449 addRegionsToMeta(connection, regionInfos, regionReplication,
1450 EnvironmentEdgeManager.currentTime());
1454 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions
1455 * is CLOSED.
1456 * @param connection connection we're using
1457 * @param regionInfos region information list
1458 * @param ts desired timestamp
1459 * @throws IOException if problem connecting or updating meta
1461 private static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
1462 int regionReplication, long ts) throws IOException {
1463 List<Put> puts = new ArrayList<>();
1464 for (RegionInfo regionInfo : regionInfos) {
1465 if (RegionReplicaUtil.isDefaultReplica(regionInfo)) {
1466 Put put = makePutFromRegionInfo(regionInfo, ts);
1467 // New regions are added with initial state of CLOSED.
1468 addRegionStateToPut(put, RegionState.State.CLOSED);
1469 // Add empty locations for region replicas so that number of replicas can be cached
1470 // whenever the primary region is looked up from meta
1471 for (int i = 1; i < regionReplication; i++) {
1472 addEmptyLocation(put, i);
1474 puts.add(put);
1477 putsToMetaTable(connection, puts);
1478 LOG.info("Added {} regions to meta.", puts.size());
1481 static Put addMergeRegions(Put put, Collection<RegionInfo> mergeRegions) throws IOException {
1482 int limit = 10000; // Arbitrary limit. No room in our formatted 'task0000' below for more.
1483 int max = mergeRegions.size();
1484 if (max > limit) {
1485 // Should never happen!!!!! But just in case.
1486 throw new RuntimeException("Can't merge " + max + " regions in one go; " + limit +
1487 " is upper-limit.");
1489 int counter = 0;
1490 for (RegionInfo ri: mergeRegions) {
1491 String qualifier = String.format(HConstants.MERGE_QUALIFIER_PREFIX_STR + "%04d", counter++);
1492 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).
1493 setRow(put.getRow()).
1494 setFamily(HConstants.CATALOG_FAMILY).
1495 setQualifier(Bytes.toBytes(qualifier)).
1496 setTimestamp(put.getTimestamp()).
1497 setType(Type.Put).
1498 setValue(RegionInfo.toByteArray(ri)).
1499 build());
1501 return put;
1505 * Merge regions into one in an atomic operation. Deletes the merging regions in
1506 * hbase:meta and adds the merged region.
1507 * @param connection connection we're using
1508 * @param mergedRegion the merged region
1509 * @param parentSeqNum Parent regions to merge and their next open sequence id used
1510 * by serial replication. Set to -1 if not needed by this table.
1511 * @param sn the location of the region
1513 public static void mergeRegions(Connection connection, RegionInfo mergedRegion,
1514 Map<RegionInfo, Long> parentSeqNum, ServerName sn, int regionReplication)
1515 throws IOException {
1516 try (Table meta = getMetaHTable(connection)) {
1517 long time = HConstants.LATEST_TIMESTAMP;
1518 List<Mutation> mutations = new ArrayList<>();
1519 List<RegionInfo> replicationParents = new ArrayList<>();
1520 for (Map.Entry<RegionInfo, Long> e: parentSeqNum.entrySet()) {
1521 RegionInfo ri = e.getKey();
1522 long seqNum = e.getValue();
1523 // Deletes for merging regions
1524 mutations.add(makeDeleteFromRegionInfo(ri, time));
1525 if (seqNum > 0) {
1526 mutations.add(makePutForReplicationBarrier(ri, seqNum, time));
1527 replicationParents.add(ri);
1530 // Put for parent
1531 Put putOfMerged = makePutFromRegionInfo(mergedRegion, time);
1532 putOfMerged = addMergeRegions(putOfMerged, parentSeqNum.keySet());
1533 // Set initial state to CLOSED.
1534 // NOTE: If initial state is not set to CLOSED then merged region gets added with the
1535 // default OFFLINE state. If Master gets restarted after this step, start up sequence of
1536 // master tries to assign this offline region. This is followed by re-assignments of the
1537 // merged region from resumed {@link MergeTableRegionsProcedure}
1538 addRegionStateToPut(putOfMerged, RegionState.State.CLOSED);
1539 mutations.add(putOfMerged);
1540 // The merged is a new region, openSeqNum = 1 is fine. ServerName may be null
1541 // if crash after merge happened but before we got to here.. means in-memory
1542 // locations of offlined merged, now-closed, regions is lost. Should be ok. We
1543 // assign the merged region later.
1544 if (sn != null) {
1545 addLocation(putOfMerged, sn, 1, mergedRegion.getReplicaId());
1548 // Add empty locations for region replicas of the merged region so that number of replicas
1549 // can be cached whenever the primary region is looked up from meta
1550 for (int i = 1; i < regionReplication; i++) {
1551 addEmptyLocation(putOfMerged, i);
1553 // add parent reference for serial replication
1554 if (!replicationParents.isEmpty()) {
1555 addReplicationParent(putOfMerged, replicationParents);
1557 byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString() + HConstants.DELIMITER);
1558 multiMutate(meta, tableRow, mutations);
1563 * Splits the region into two in an atomic operation. Offlines the parent region with the
1564 * information that it is split into two, and also adds the daughter regions. Does not add the
1565 * location information to the daughter regions since they are not open yet.
1566 * @param connection connection we're using
1567 * @param parent the parent region which is split
1568 * @param parentOpenSeqNum the next open sequence id for parent region, used by serial
1569 * replication. -1 if not necessary.
1570 * @param splitA Split daughter region A
1571 * @param splitB Split daughter region B
1572 * @param sn the location of the region
1574 public static void splitRegion(Connection connection, RegionInfo parent, long parentOpenSeqNum,
1575 RegionInfo splitA, RegionInfo splitB, ServerName sn, int regionReplication)
1576 throws IOException {
1577 try (Table meta = getMetaHTable(connection)) {
1578 long time = EnvironmentEdgeManager.currentTime();
1579 // Put for parent
1580 Put putParent = makePutFromRegionInfo(RegionInfoBuilder.newBuilder(parent)
1581 .setOffline(true)
1582 .setSplit(true).build(), time);
1583 addDaughtersToPut(putParent, splitA, splitB);
1585 // Puts for daughters
1586 Put putA = makePutFromRegionInfo(splitA, time);
1587 Put putB = makePutFromRegionInfo(splitB, time);
1588 if (parentOpenSeqNum > 0) {
1589 addReplicationBarrier(putParent, parentOpenSeqNum);
1590 addReplicationParent(putA, Collections.singletonList(parent));
1591 addReplicationParent(putB, Collections.singletonList(parent));
1593 // Set initial state to CLOSED
1594 // NOTE: If initial state is not set to CLOSED then daughter regions get added with the
1595 // default OFFLINE state. If Master gets restarted after this step, start up sequence of
1596 // master tries to assign these offline regions. This is followed by re-assignments of the
1597 // daughter regions from resumed {@link SplitTableRegionProcedure}
1598 addRegionStateToPut(putA, RegionState.State.CLOSED);
1599 addRegionStateToPut(putB, RegionState.State.CLOSED);
1601 addSequenceNum(putA, 1, splitA.getReplicaId()); // new regions, openSeqNum = 1 is fine.
1602 addSequenceNum(putB, 1, splitB.getReplicaId());
1604 // Add empty locations for region replicas of daughters so that number of replicas can be
1605 // cached whenever the primary region is looked up from meta
1606 for (int i = 1; i < regionReplication; i++) {
1607 addEmptyLocation(putA, i);
1608 addEmptyLocation(putB, i);
1611 byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER);
1612 multiMutate(meta, tableRow, putParent, putA, putB);
1617 * Update state of the table in meta.
1618 * @param connection what we use for update
1619 * @param state new state
1621 private static void updateTableState(Connection connection, TableState state) throws IOException {
1622 Put put = makePutFromTableState(state, EnvironmentEdgeManager.currentTime());
1623 putToMetaTable(connection, put);
1624 LOG.info("Updated {} in hbase:meta", state);
1628 * Construct PUT for given state
1629 * @param state new state
1631 public static Put makePutFromTableState(TableState state, long ts) {
1632 Put put = new Put(state.getTableName().getName(), ts);
1633 put.addColumn(getTableFamily(), getTableStateColumn(), state.convert().toByteArray());
1634 return put;
1638 * Remove state for table from meta
1639 * @param connection to use for deletion
1640 * @param table to delete state for
1642 public static void deleteTableState(Connection connection, TableName table)
1643 throws IOException {
1644 long time = EnvironmentEdgeManager.currentTime();
1645 Delete delete = new Delete(table.getName());
1646 delete.addColumns(getTableFamily(), getTableStateColumn(), time);
1647 deleteFromMetaTable(connection, delete);
1648 LOG.info("Deleted table " + table + " state from META");
1651 private static void multiMutate(Table table, byte[] row,
1652 Mutation... mutations) throws IOException {
1653 multiMutate(table, row, Arrays.asList(mutations));
1657 * Performs an atomic multi-mutate operation against the given table.
1659 private static void multiMutate(final Table table, byte[] row, final List<Mutation> mutations)
1660 throws IOException {
1661 debugLogMutations(mutations);
1662 Batch.Call<MultiRowMutationService, MutateRowsResponse> callable =
1663 new Batch.Call<MultiRowMutationService, MutateRowsResponse>() {
1665 @Override
1666 public MutateRowsResponse call(MultiRowMutationService instance) throws IOException {
1667 MutateRowsRequest.Builder builder = MutateRowsRequest.newBuilder();
1668 for (Mutation mutation : mutations) {
1669 if (mutation instanceof Put) {
1670 builder.addMutationRequest(
1671 ProtobufUtil.toMutation(ClientProtos.MutationProto.MutationType.PUT, mutation));
1672 } else if (mutation instanceof Delete) {
1673 builder.addMutationRequest(
1674 ProtobufUtil.toMutation(ClientProtos.MutationProto.MutationType.DELETE, mutation));
1675 } else {
1676 throw new DoNotRetryIOException(
1677 "multi in MetaEditor doesn't support " + mutation.getClass().getName());
1680 ServerRpcController controller = new ServerRpcController();
1681 CoprocessorRpcUtils.BlockingRpcCallback<MutateRowsResponse> rpcCallback =
1682 new CoprocessorRpcUtils.BlockingRpcCallback<>();
1683 instance.mutateRows(controller, builder.build(), rpcCallback);
1684 MutateRowsResponse resp = rpcCallback.get();
1685 if (controller.failedOnException()) {
1686 throw controller.getFailedOn();
1688 return resp;
1691 try {
1692 table.coprocessorService(MultiRowMutationService.class, row, row, callable);
1693 } catch (Throwable e) {
1694 Throwables.propagateIfPossible(e, IOException.class);
1695 throw new IOException(e);
1700 * Updates the location of the specified region in hbase:meta to be the specified server hostname
1701 * and startcode.
1702 * <p>
1703 * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes
1704 * edits to that region.
1705 * @param connection connection we're using
1706 * @param regionInfo region to update location of
1707 * @param openSeqNum the latest sequence number obtained when the region was open
1708 * @param sn Server name
1709 * @param masterSystemTime wall clock time from master if passed in the open region RPC
1711 @VisibleForTesting
1712 public static void updateRegionLocation(Connection connection, RegionInfo regionInfo,
1713 ServerName sn, long openSeqNum, long masterSystemTime) throws IOException {
1714 updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime);
1718 * Updates the location of the specified region to be the specified server.
1719 * <p>
1720 * Connects to the specified server which should be hosting the specified catalog region name to
1721 * perform the edit.
1722 * @param connection connection we're using
1723 * @param regionInfo region to update location of
1724 * @param sn Server name
1725 * @param openSeqNum the latest sequence number obtained when the region was open
1726 * @param masterSystemTime wall clock time from master if passed in the open region RPC
1727 * @throws IOException In particular could throw {@link java.net.ConnectException} if the server
1728 * is down on other end.
1730 private static void updateLocation(Connection connection, RegionInfo regionInfo, ServerName sn,
1731 long openSeqNum, long masterSystemTime) throws IOException {
1732 // region replicas are kept in the primary region's row
1733 Put put = new Put(getMetaKeyForRegion(regionInfo), masterSystemTime);
1734 addRegionInfo(put, regionInfo);
1735 addLocation(put, sn, openSeqNum, regionInfo.getReplicaId());
1736 putToMetaTable(connection, put);
1737 LOG.info("Updated row {} with server=", regionInfo.getRegionNameAsString(), sn);
1741 * Deletes the specified region from META.
1742 * @param connection connection we're using
1743 * @param regionInfo region to be deleted from META
1745 public static void deleteRegionInfo(Connection connection, RegionInfo regionInfo)
1746 throws IOException {
1747 Delete delete = new Delete(regionInfo.getRegionName());
1748 delete.addFamily(getCatalogFamily(), HConstants.LATEST_TIMESTAMP);
1749 deleteFromMetaTable(connection, delete);
1750 LOG.info("Deleted " + regionInfo.getRegionNameAsString());
1754 * Deletes the specified regions from META.
1755 * @param connection connection we're using
1756 * @param regionsInfo list of regions to be deleted from META
1758 public static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo)
1759 throws IOException {
1760 deleteRegionInfos(connection, regionsInfo, EnvironmentEdgeManager.currentTime());
1764 * Deletes the specified regions from META.
1765 * @param connection connection we're using
1766 * @param regionsInfo list of regions to be deleted from META
1768 private static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo,
1769 long ts)
1770 throws IOException {
1771 List<Delete> deletes = new ArrayList<>(regionsInfo.size());
1772 for (RegionInfo hri : regionsInfo) {
1773 Delete e = new Delete(hri.getRegionName());
1774 e.addFamily(getCatalogFamily(), ts);
1775 deletes.add(e);
1777 deleteFromMetaTable(connection, deletes);
1778 LOG.info("Deleted {} regions from META", regionsInfo.size());
1779 LOG.debug("Deleted regions: {}", regionsInfo);
1783 * Overwrites the specified regions from hbase:meta. Deletes old rows for the given regions and
1784 * adds new ones. Regions added back have state CLOSED.
1785 * @param connection connection we're using
1786 * @param regionInfos list of regions to be added to META
1788 public static void overwriteRegions(Connection connection, List<RegionInfo> regionInfos,
1789 int regionReplication) throws IOException {
1790 // use master time for delete marker and the Put
1791 long now = EnvironmentEdgeManager.currentTime();
1792 deleteRegionInfos(connection, regionInfos, now);
1793 // Why sleep? This is the easiest way to ensure that the previous deletes does not
1794 // eclipse the following puts, that might happen in the same ts from the server.
1795 // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed,
1796 // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep.
1798 // HBASE-13875 uses master timestamp for the mutations. The 20ms sleep is not needed
1799 addRegionsToMeta(connection, regionInfos, regionReplication, now + 1);
1800 LOG.info("Overwritten " + regionInfos.size() + " regions to Meta");
1801 LOG.debug("Overwritten regions: {} ", regionInfos);
1805 * Deletes merge qualifiers for the specified merge region.
1806 * @param connection connection we're using
1807 * @param mergeRegion the merged region
1809 public static void deleteMergeQualifiers(Connection connection, final RegionInfo mergeRegion)
1810 throws IOException {
1811 Delete delete = new Delete(mergeRegion.getRegionName());
1812 // NOTE: We are doing a new hbase:meta read here.
1813 Cell[] cells = getRegionResult(connection, mergeRegion.getRegionName()).rawCells();
1814 if (cells == null || cells.length == 0) {
1815 return;
1817 List<byte[]> qualifiers = new ArrayList<>();
1818 for (Cell cell : cells) {
1819 if (!isMergeQualifierPrefix(cell)) {
1820 continue;
1822 byte[] qualifier = CellUtil.cloneQualifier(cell);
1823 qualifiers.add(qualifier);
1824 delete.addColumns(getCatalogFamily(), qualifier, HConstants.LATEST_TIMESTAMP);
1826 deleteFromMetaTable(connection, delete);
1827 LOG.info("Deleted merge references in " + mergeRegion.getRegionNameAsString() +
1828 ", deleted qualifiers " + qualifiers.stream().map(Bytes::toStringBinary).
1829 collect(Collectors.joining(", ")));
1832 public static Put addRegionInfo(final Put p, final RegionInfo hri)
1833 throws IOException {
1834 p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1835 .setRow(p.getRow())
1836 .setFamily(getCatalogFamily())
1837 .setQualifier(HConstants.REGIONINFO_QUALIFIER)
1838 .setTimestamp(p.getTimestamp())
1839 .setType(Type.Put)
1840 .setValue(RegionInfo.toByteArray(hri))
1841 .build());
1842 return p;
1845 public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId)
1846 throws IOException {
1847 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
1848 return p.add(builder.clear()
1849 .setRow(p.getRow())
1850 .setFamily(getCatalogFamily())
1851 .setQualifier(getServerColumn(replicaId))
1852 .setTimestamp(p.getTimestamp())
1853 .setType(Cell.Type.Put)
1854 .setValue(Bytes.toBytes(sn.getAddress().toString()))
1855 .build())
1856 .add(builder.clear()
1857 .setRow(p.getRow())
1858 .setFamily(getCatalogFamily())
1859 .setQualifier(getStartCodeColumn(replicaId))
1860 .setTimestamp(p.getTimestamp())
1861 .setType(Cell.Type.Put)
1862 .setValue(Bytes.toBytes(sn.getStartcode()))
1863 .build())
1864 .add(builder.clear()
1865 .setRow(p.getRow())
1866 .setFamily(getCatalogFamily())
1867 .setQualifier(getSeqNumColumn(replicaId))
1868 .setTimestamp(p.getTimestamp())
1869 .setType(Type.Put)
1870 .setValue(Bytes.toBytes(openSeqNum))
1871 .build());
1874 private static void writeRegionName(ByteArrayOutputStream out, byte[] regionName) {
1875 for (byte b : regionName) {
1876 if (b == ESCAPE_BYTE) {
1877 out.write(ESCAPE_BYTE);
1879 out.write(b);
1883 @VisibleForTesting
1884 public static byte[] getParentsBytes(List<RegionInfo> parents) {
1885 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1886 Iterator<RegionInfo> iter = parents.iterator();
1887 writeRegionName(bos, iter.next().getRegionName());
1888 while (iter.hasNext()) {
1889 bos.write(ESCAPE_BYTE);
1890 bos.write(SEPARATED_BYTE);
1891 writeRegionName(bos, iter.next().getRegionName());
1893 return bos.toByteArray();
1896 private static List<byte[]> parseParentsBytes(byte[] bytes) {
1897 List<byte[]> parents = new ArrayList<>();
1898 ByteArrayOutputStream bos = new ByteArrayOutputStream();
1899 for (int i = 0; i < bytes.length; i++) {
1900 if (bytes[i] == ESCAPE_BYTE) {
1901 i++;
1902 if (bytes[i] == SEPARATED_BYTE) {
1903 parents.add(bos.toByteArray());
1904 bos.reset();
1905 continue;
1907 // fall through to append the byte
1909 bos.write(bytes[i]);
1911 if (bos.size() > 0) {
1912 parents.add(bos.toByteArray());
1914 return parents;
1917 private static void addReplicationParent(Put put, List<RegionInfo> parents) throws IOException {
1918 byte[] value = getParentsBytes(parents);
1919 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow())
1920 .setFamily(HConstants.REPLICATION_BARRIER_FAMILY).setQualifier(REPLICATION_PARENT_QUALIFIER)
1921 .setTimestamp(put.getTimestamp()).setType(Type.Put).setValue(value).build());
1924 public static Put makePutForReplicationBarrier(RegionInfo regionInfo, long openSeqNum, long ts)
1925 throws IOException {
1926 Put put = new Put(regionInfo.getRegionName(), ts);
1927 addReplicationBarrier(put, openSeqNum);
1928 return put;
1931 public static void addReplicationBarrier(Put put, long openSeqNum) throws IOException {
1932 put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1933 .setRow(put.getRow())
1934 .setFamily(HConstants.REPLICATION_BARRIER_FAMILY)
1935 .setQualifier(HConstants.SEQNUM_QUALIFIER)
1936 .setTimestamp(put.getTimestamp())
1937 .setType(Type.Put)
1938 .setValue(Bytes.toBytes(openSeqNum))
1939 .build());
1942 private static Put addEmptyLocation(Put p, int replicaId) throws IOException {
1943 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
1944 return p.add(builder.clear()
1945 .setRow(p.getRow())
1946 .setFamily(getCatalogFamily())
1947 .setQualifier(getServerColumn(replicaId))
1948 .setTimestamp(p.getTimestamp())
1949 .setType(Type.Put)
1950 .build())
1951 .add(builder.clear()
1952 .setRow(p.getRow())
1953 .setFamily(getCatalogFamily())
1954 .setQualifier(getStartCodeColumn(replicaId))
1955 .setTimestamp(p.getTimestamp())
1956 .setType(Cell.Type.Put)
1957 .build())
1958 .add(builder.clear()
1959 .setRow(p.getRow())
1960 .setFamily(getCatalogFamily())
1961 .setQualifier(getSeqNumColumn(replicaId))
1962 .setTimestamp(p.getTimestamp())
1963 .setType(Cell.Type.Put)
1964 .build());
1967 public static final class ReplicationBarrierResult {
1968 private final long[] barriers;
1969 private final RegionState.State state;
1970 private final List<byte[]> parentRegionNames;
1972 ReplicationBarrierResult(long[] barriers, State state, List<byte[]> parentRegionNames) {
1973 this.barriers = barriers;
1974 this.state = state;
1975 this.parentRegionNames = parentRegionNames;
1978 public long[] getBarriers() {
1979 return barriers;
1982 public RegionState.State getState() {
1983 return state;
1986 public List<byte[]> getParentRegionNames() {
1987 return parentRegionNames;
1990 @Override
1991 public String toString() {
1992 return "ReplicationBarrierResult [barriers=" + Arrays.toString(barriers) + ", state=" +
1993 state + ", parentRegionNames=" +
1994 parentRegionNames.stream().map(Bytes::toStringBinary).collect(Collectors.joining(", ")) +
1995 "]";
1999 private static long getReplicationBarrier(Cell c) {
2000 return Bytes.toLong(c.getValueArray(), c.getValueOffset(), c.getValueLength());
2003 public static long[] getReplicationBarriers(Result result) {
2004 return result.getColumnCells(HConstants.REPLICATION_BARRIER_FAMILY, HConstants.SEQNUM_QUALIFIER)
2005 .stream().mapToLong(MetaTableAccessor::getReplicationBarrier).sorted().distinct().toArray();
2008 private static ReplicationBarrierResult getReplicationBarrierResult(Result result) {
2009 long[] barriers = getReplicationBarriers(result);
2010 byte[] stateBytes = result.getValue(getCatalogFamily(), getRegionStateColumn());
2011 RegionState.State state =
2012 stateBytes != null ? RegionState.State.valueOf(Bytes.toString(stateBytes)) : null;
2013 byte[] parentRegionsBytes =
2014 result.getValue(HConstants.REPLICATION_BARRIER_FAMILY, REPLICATION_PARENT_QUALIFIER);
2015 List<byte[]> parentRegionNames =
2016 parentRegionsBytes != null ? parseParentsBytes(parentRegionsBytes) : Collections.emptyList();
2017 return new ReplicationBarrierResult(barriers, state, parentRegionNames);
2020 public static ReplicationBarrierResult getReplicationBarrierResult(Connection conn,
2021 TableName tableName, byte[] row, byte[] encodedRegionName) throws IOException {
2022 byte[] metaStartKey = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
2023 byte[] metaStopKey =
2024 RegionInfo.createRegionName(tableName, HConstants.EMPTY_START_ROW, "", false);
2025 Scan scan = new Scan().withStartRow(metaStartKey).withStopRow(metaStopKey)
2026 .addColumn(getCatalogFamily(), getRegionStateColumn())
2027 .addFamily(HConstants.REPLICATION_BARRIER_FAMILY).readAllVersions().setReversed(true)
2028 .setCaching(10);
2029 try (Table table = getMetaHTable(conn); ResultScanner scanner = table.getScanner(scan)) {
2030 for (Result result;;) {
2031 result = scanner.next();
2032 if (result == null) {
2033 return new ReplicationBarrierResult(new long[0], null, Collections.emptyList());
2035 byte[] regionName = result.getRow();
2036 // TODO: we may look up a region which has already been split or merged so we need to check
2037 // whether the encoded name matches. Need to find a way to quit earlier when there is no
2038 // record for the given region, for now it will scan to the end of the table.
2039 if (!Bytes.equals(encodedRegionName,
2040 Bytes.toBytes(RegionInfo.encodeRegionName(regionName)))) {
2041 continue;
2043 return getReplicationBarrierResult(result);
2048 public static long[] getReplicationBarrier(Connection conn, byte[] regionName)
2049 throws IOException {
2050 try (Table table = getMetaHTable(conn)) {
2051 Result result = table.get(new Get(regionName)
2052 .addColumn(HConstants.REPLICATION_BARRIER_FAMILY, HConstants.SEQNUM_QUALIFIER)
2053 .readAllVersions());
2054 return getReplicationBarriers(result);
2058 public static List<Pair<String, Long>> getTableEncodedRegionNameAndLastBarrier(Connection conn,
2059 TableName tableName) throws IOException {
2060 List<Pair<String, Long>> list = new ArrayList<>();
2061 scanMeta(conn, getTableStartRowForMeta(tableName, QueryType.REPLICATION),
2062 getTableStopRowForMeta(tableName, QueryType.REPLICATION), QueryType.REPLICATION, r -> {
2063 byte[] value =
2064 r.getValue(HConstants.REPLICATION_BARRIER_FAMILY, HConstants.SEQNUM_QUALIFIER);
2065 if (value == null) {
2066 return true;
2068 long lastBarrier = Bytes.toLong(value);
2069 String encodedRegionName = RegionInfo.encodeRegionName(r.getRow());
2070 list.add(Pair.newPair(encodedRegionName, lastBarrier));
2071 return true;
2073 return list;
2076 public static List<String> getTableEncodedRegionNamesForSerialReplication(Connection conn,
2077 TableName tableName) throws IOException {
2078 List<String> list = new ArrayList<>();
2079 scanMeta(conn, getTableStartRowForMeta(tableName, QueryType.REPLICATION),
2080 getTableStopRowForMeta(tableName, QueryType.REPLICATION), QueryType.REPLICATION,
2081 new FirstKeyOnlyFilter(), Integer.MAX_VALUE, r -> {
2082 list.add(RegionInfo.encodeRegionName(r.getRow()));
2083 return true;
2085 return list;
2088 private static void debugLogMutations(List<? extends Mutation> mutations) throws IOException {
2089 if (!METALOG.isDebugEnabled()) {
2090 return;
2092 // Logging each mutation in separate line makes it easier to see diff between them visually
2093 // because of common starting indentation.
2094 for (Mutation mutation : mutations) {
2095 debugLogMutation(mutation);
2099 private static void debugLogMutation(Mutation p) throws IOException {
2100 METALOG.debug("{} {}", p.getClass().getSimpleName(), p.toJSON());
2103 private static Put addSequenceNum(Put p, long openSeqNum, int replicaId) throws IOException {
2104 return p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
2105 .setRow(p.getRow())
2106 .setFamily(HConstants.CATALOG_FAMILY)
2107 .setQualifier(getSeqNumColumn(replicaId))
2108 .setTimestamp(p.getTimestamp())
2109 .setType(Type.Put)
2110 .setValue(Bytes.toBytes(openSeqNum))
2111 .build());