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
;
33 import java
.util
.NavigableMap
;
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
;
86 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
87 * to <code>hbase:meta</code>.
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.
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.
101 * <h2>Meta layout</h2>
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 * <tableName>,<startKey>,<regionId>,<encodedRegionName>.
111 * This row is the serialized regionName of the default region replica.
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_<replicaId> => 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_<replicaId> => contains server start code (in binary long form) for
121 * the server hosting the regionInfo replica with
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_<replicaId> => contains seqNum (in binary long form) for the region
126 * at the time the server opened the region with
128 * info:splitA => contains a serialized HRI for the first daughter region if the
130 * info:splitB => contains a serialized HRI for the second daughter region if the
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.
143 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not
144 * leak out of it (through Result objects, etc)
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");
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 > 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
)
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
)
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
)
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;
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
266 public static Pair
<RegionInfo
, ServerName
> getRegion(Connection connection
, byte [] regionName
)
268 HRegionLocation location
= getRegionLocation(connection
, regionName
);
269 return location
== 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
)
282 byte[] row
= regionName
;
283 RegionInfo parsedInfo
= null;
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
)
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
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])
330 .setRegionId(regionId
)
331 .setReplicaId(replicaId
)
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.
372 public static List
<RegionInfo
> getMergeRegions(Connection connection
, byte[] regionName
)
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>.
382 public static List
<RegionInfo
> getMergeRegions(Cell
[] cells
) {
386 List
<RegionInfo
> regionsToMerge
= null;
387 for (Cell cell
: cells
) {
388 if (!isMergeQualifierPrefix(cell
)) {
391 // Ok. This cell is that of a info:merge* column.
392 RegionInfo ri
= RegionInfo
.parseFromOrNull(cell
.getValueArray(), cell
.getValueOffset(),
393 cell
.getValueLength());
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
)) {
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
)
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.
451 public static List
<RegionInfo
> getAllRegions(Connection connection
,
452 boolean excludeOfflinedSplitParents
)
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
)
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());
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) {
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
;
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) {
533 final byte[] stopRow
;
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
;
545 stopRow
= new byte[tableName
.getName().length
+ 1];
546 System
.arraycopy(tableName
.getName(), 0, stopRow
, 0, tableName
.getName().length
);
547 stopRow
[stopRow
.length
- 1] = ' ';
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
559 * @param tableName bytes of table's name
560 * @return configured Scan object
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
);
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
);
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
)
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;
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
);
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
);
637 if (current
== null) {
640 for (HRegionLocation loc
: current
.getRegionLocations()) {
642 this.results
.add(new Pair
<>(loc
.getRegion(), loc
.getServerName()));
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
)
657 if (r
== null || r
.isEmpty()) {
660 LOG
.info("fullScanMetaAndPrint.Current Meta Row: " + r
);
661 TableState state
= getTableState(r
);
663 LOG
.info("fullScanMetaAndPrint.Table State={}" + state
);
665 RegionLocations locations
= getRegionLocations(r
);
666 if (locations
== null) {
669 for (HRegionLocation loc
: locations
.getRegionLocations()) {
671 LOG
.info("fullScanMetaAndPrint.HRI Print={}", loc
.getRegion());
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
);
711 RegionInfo closestRi
= getClosestRegionInfo(connection
, tableName
, row
);
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
725 * @param stopRow Where to stop the scan. Pass null if want to scan all rows
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
)
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());
763 try (Table metaTable
= getMetaHTable(connection
)) {
764 try (ResultScanner scanner
= metaTable
.getScanner(scan
)) {
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
) {
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>
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
));
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
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
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
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
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
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
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);
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.
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;
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
);
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
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.
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
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) {
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);
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
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
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.
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
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 -> 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
);
1103 fullScanTables(conn
, collector
);
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
1123 public static TableState
getTableState(Result r
) throws IOException
{
1124 Cell cell
= r
.getColumnLatestCell(getTableFamily(), getTableStateColumn());
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
<>();
1162 public boolean visit(Result r
) throws IOException
{
1163 if (r
!= null && !r
.isEmpty()) {
1169 abstract void add(Result r
);
1172 * @return Collected results; wait till visits complete to collect all
1175 List
<T
> getResults() {
1176 return this.results
;
1181 * Collects all returned.
1183 static class CollectAllVisitor
extends CollectingVisitor
<Result
> {
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() {
1199 public abstract boolean visitInternal(Result rowResult
) throws IOException
;
1202 public boolean visit(Result rowResult
) throws IOException
{
1203 RegionInfo info
= getRegionInfo(rowResult
);
1208 //skip over offline and split regions
1209 if (!(info
.isOffline() || info
.isSplit())) {
1210 return visitInternal(rowResult
);
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
);
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
);
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())
1279 .setValue(RegionInfo
.toByteArray(splitA
))
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())
1289 .setValue(RegionInfo
.toByteArray(splitB
))
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
)) {
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
);
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
{
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) {
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);
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
);
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()))
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
);
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
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
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
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
);
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();
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.");
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()).
1498 setValue(RegionInfo
.toByteArray(ri
)).
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
));
1526 mutations
.add(makePutForReplicationBarrier(ri
, seqNum
, time
));
1527 replicationParents
.add(ri
);
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.
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();
1580 Put putParent
= makePutFromRegionInfo(RegionInfoBuilder
.newBuilder(parent
)
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());
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
>() {
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
));
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();
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
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
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.
1720 * Connects to the specified server which should be hosting the specified catalog region name to
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
,
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
);
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) {
1817 List
<byte[]> qualifiers
= new ArrayList
<>();
1818 for (Cell cell
: cells
) {
1819 if (!isMergeQualifierPrefix(cell
)) {
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
)
1836 .setFamily(getCatalogFamily())
1837 .setQualifier(HConstants
.REGIONINFO_QUALIFIER
)
1838 .setTimestamp(p
.getTimestamp())
1840 .setValue(RegionInfo
.toByteArray(hri
))
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()
1850 .setFamily(getCatalogFamily())
1851 .setQualifier(getServerColumn(replicaId
))
1852 .setTimestamp(p
.getTimestamp())
1853 .setType(Cell
.Type
.Put
)
1854 .setValue(Bytes
.toBytes(sn
.getAddress().toString()))
1856 .add(builder
.clear()
1858 .setFamily(getCatalogFamily())
1859 .setQualifier(getStartCodeColumn(replicaId
))
1860 .setTimestamp(p
.getTimestamp())
1861 .setType(Cell
.Type
.Put
)
1862 .setValue(Bytes
.toBytes(sn
.getStartcode()))
1864 .add(builder
.clear()
1866 .setFamily(getCatalogFamily())
1867 .setQualifier(getSeqNumColumn(replicaId
))
1868 .setTimestamp(p
.getTimestamp())
1870 .setValue(Bytes
.toBytes(openSeqNum
))
1874 private static void writeRegionName(ByteArrayOutputStream out
, byte[] regionName
) {
1875 for (byte b
: regionName
) {
1876 if (b
== ESCAPE_BYTE
) {
1877 out
.write(ESCAPE_BYTE
);
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
) {
1902 if (bytes
[i
] == SEPARATED_BYTE
) {
1903 parents
.add(bos
.toByteArray());
1907 // fall through to append the byte
1909 bos
.write(bytes
[i
]);
1911 if (bos
.size() > 0) {
1912 parents
.add(bos
.toByteArray());
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
);
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())
1938 .setValue(Bytes
.toBytes(openSeqNum
))
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()
1946 .setFamily(getCatalogFamily())
1947 .setQualifier(getServerColumn(replicaId
))
1948 .setTimestamp(p
.getTimestamp())
1951 .add(builder
.clear()
1953 .setFamily(getCatalogFamily())
1954 .setQualifier(getStartCodeColumn(replicaId
))
1955 .setTimestamp(p
.getTimestamp())
1956 .setType(Cell
.Type
.Put
)
1958 .add(builder
.clear()
1960 .setFamily(getCatalogFamily())
1961 .setQualifier(getSeqNumColumn(replicaId
))
1962 .setTimestamp(p
.getTimestamp())
1963 .setType(Cell
.Type
.Put
)
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
;
1975 this.parentRegionNames
= parentRegionNames
;
1978 public long[] getBarriers() {
1982 public RegionState
.State
getState() {
1986 public List
<byte[]> getParentRegionNames() {
1987 return parentRegionNames
;
1991 public String
toString() {
1992 return "ReplicationBarrierResult [barriers=" + Arrays
.toString(barriers
) + ", state=" +
1993 state
+ ", parentRegionNames=" +
1994 parentRegionNames
.stream().map(Bytes
::toStringBinary
).collect(Collectors
.joining(", ")) +
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)
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
)))) {
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
-> {
2064 r
.getValue(HConstants
.REPLICATION_BARRIER_FAMILY
, HConstants
.SEQNUM_QUALIFIER
);
2065 if (value
== null) {
2068 long lastBarrier
= Bytes
.toLong(value
);
2069 String encodedRegionName
= RegionInfo
.encodeRegionName(r
.getRow());
2070 list
.add(Pair
.newPair(encodedRegionName
, lastBarrier
));
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()));
2088 private static void debugLogMutations(List
<?
extends Mutation
> mutations
) throws IOException
{
2089 if (!METALOG
.isDebugEnabled()) {
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
)
2106 .setFamily(HConstants
.CATALOG_FAMILY
)
2107 .setQualifier(getSeqNumColumn(replicaId
))
2108 .setTimestamp(p
.getTimestamp())
2110 .setValue(Bytes
.toBytes(openSeqNum
))