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
.Closeable
;
23 import java
.io
.IOException
;
24 import java
.util
.ArrayList
;
25 import java
.util
.Collections
;
26 import java
.util
.LinkedHashMap
;
27 import java
.util
.List
;
29 import java
.util
.Objects
;
30 import org
.apache
.hadoop
.conf
.Configuration
;
31 import org
.apache
.hadoop
.hbase
.Cell
.Type
;
32 import org
.apache
.hadoop
.hbase
.ClientMetaTableAccessor
.QueryType
;
33 import org
.apache
.hadoop
.hbase
.client
.Connection
;
34 import org
.apache
.hadoop
.hbase
.client
.Consistency
;
35 import org
.apache
.hadoop
.hbase
.client
.Delete
;
36 import org
.apache
.hadoop
.hbase
.client
.Get
;
37 import org
.apache
.hadoop
.hbase
.client
.Mutation
;
38 import org
.apache
.hadoop
.hbase
.client
.Put
;
39 import org
.apache
.hadoop
.hbase
.client
.RegionInfo
;
40 import org
.apache
.hadoop
.hbase
.client
.RegionReplicaUtil
;
41 import org
.apache
.hadoop
.hbase
.client
.Result
;
42 import org
.apache
.hadoop
.hbase
.client
.ResultScanner
;
43 import org
.apache
.hadoop
.hbase
.client
.Scan
;
44 import org
.apache
.hadoop
.hbase
.client
.Table
;
45 import org
.apache
.hadoop
.hbase
.client
.TableState
;
46 import org
.apache
.hadoop
.hbase
.filter
.Filter
;
47 import org
.apache
.hadoop
.hbase
.filter
.RowFilter
;
48 import org
.apache
.hadoop
.hbase
.filter
.SubstringComparator
;
49 import org
.apache
.hadoop
.hbase
.master
.RegionState
;
50 import org
.apache
.hadoop
.hbase
.util
.Bytes
;
51 import org
.apache
.hadoop
.hbase
.util
.EnvironmentEdgeManager
;
52 import org
.apache
.hadoop
.hbase
.util
.ExceptionUtil
;
53 import org
.apache
.hadoop
.hbase
.util
.Pair
;
54 import org
.apache
.hadoop
.hbase
.util
.PairOfSameType
;
55 import org
.apache
.yetus
.audience
.InterfaceAudience
;
56 import org
.slf4j
.Logger
;
57 import org
.slf4j
.LoggerFactory
;
60 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
61 * to <code>hbase:meta</code>.
63 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is
64 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection
65 * (opened before each operation, closed right after), while when used on HM or HRS (like in
66 * AssignmentManager) we want permanent connection.
68 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table
69 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is
70 * called default replica.
72 * <h2>Meta layout</h2> For each table there is single row named for the table with a 'table' column
73 * family. The column family currently has one column in it, the 'state' column:
76 * table:state => contains table state
79 * For the catalog family, see the comments of {@link CatalogFamilyFormat} for more details.
81 * TODO: Add rep_barrier for serial replication explanation. See SerialReplicationChecker.
83 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not
84 * leak out of it (through Result objects, etc)
85 * @see CatalogFamilyFormat
86 * @see ClientMetaTableAccessor
88 @InterfaceAudience.Private
89 public final class MetaTableAccessor
{
91 private static final Logger LOG
= LoggerFactory
.getLogger(MetaTableAccessor
.class);
92 private static final Logger METALOG
= LoggerFactory
.getLogger("org.apache.hadoop.hbase.META");
94 private MetaTableAccessor() {
97 ////////////////////////
98 // Reading operations //
99 ////////////////////////
102 * Performs a full scan of <code>hbase:meta</code> for regions.
103 * @param connection connection we're using
104 * @param visitor Visitor invoked against each row in regions family.
106 public static void fullScanRegions(Connection connection
,
107 final ClientMetaTableAccessor
.Visitor visitor
) throws IOException
{
108 scanMeta(connection
, null, null, QueryType
.REGION
, visitor
);
112 * Performs a full scan of <code>hbase:meta</code> for regions.
113 * @param connection connection we're using
115 public static List
<Result
> fullScanRegions(Connection connection
) throws IOException
{
116 return fullScan(connection
, QueryType
.REGION
);
120 * Performs a full scan of <code>hbase:meta</code> for tables.
121 * @param connection connection we're using
122 * @param visitor Visitor invoked against each row in tables family.
124 public static void fullScanTables(Connection connection
,
125 final ClientMetaTableAccessor
.Visitor visitor
) throws IOException
{
126 scanMeta(connection
, null, null, QueryType
.TABLE
, visitor
);
130 * Performs a full scan of <code>hbase:meta</code>.
131 * @param connection connection we're using
132 * @param type scanned part of meta
133 * @return List of {@link Result}
135 private static List
<Result
> fullScan(Connection connection
, QueryType type
) throws IOException
{
136 ClientMetaTableAccessor
.CollectAllVisitor v
= new ClientMetaTableAccessor
.CollectAllVisitor();
137 scanMeta(connection
, null, null, type
, v
);
138 return v
.getResults();
142 * Callers should call close on the returned {@link Table} instance.
143 * @param connection connection we're using to access Meta
144 * @return An {@link Table} for <code>hbase:meta</code>
145 * @throws NullPointerException if {@code connection} is {@code null}
147 public static Table
getMetaHTable(final Connection connection
) throws IOException
{
148 // We used to pass whole CatalogTracker in here, now we just pass in Connection
149 Objects
.requireNonNull(connection
, "Connection cannot be null");
150 if (connection
.isClosed()) {
151 throw new IOException("connection is closed");
153 return connection
.getTable(TableName
.META_TABLE_NAME
);
157 * Gets the region info and assignment for the specified region.
158 * @param connection connection we're using
159 * @param regionName Region to lookup.
160 * @return Location and RegionInfo for <code>regionName</code>
161 * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
164 public static Pair
<RegionInfo
, ServerName
> getRegion(Connection connection
, byte[] regionName
)
166 HRegionLocation location
= getRegionLocation(connection
, regionName
);
167 return location
== null ?
null : new Pair
<>(location
.getRegion(), location
.getServerName());
171 * Returns the HRegionLocation from meta for the given region
172 * @param connection connection we're using
173 * @param regionName region we're looking for
174 * @return HRegionLocation for the given region
176 public static HRegionLocation
getRegionLocation(Connection connection
, byte[] regionName
)
178 byte[] row
= regionName
;
179 RegionInfo parsedInfo
= null;
181 parsedInfo
= CatalogFamilyFormat
.parseRegionInfoFromRegionName(regionName
);
182 row
= CatalogFamilyFormat
.getMetaKeyForRegion(parsedInfo
);
183 } catch (Exception parseEx
) {
184 // Ignore. This is used with tableName passed as regionName.
186 Get get
= new Get(row
);
187 get
.addFamily(HConstants
.CATALOG_FAMILY
);
189 try (Table t
= getMetaHTable(connection
)) {
192 RegionLocations locations
= CatalogFamilyFormat
.getRegionLocations(r
);
193 return locations
== null ?
null :
194 locations
.getRegionLocation(parsedInfo
== null ?
0 : parsedInfo
.getReplicaId());
198 * Returns the HRegionLocation from meta for the given region
199 * @param connection connection we're using
200 * @param regionInfo region information
201 * @return HRegionLocation for the given region
203 public static HRegionLocation
getRegionLocation(Connection connection
, RegionInfo regionInfo
)
205 return CatalogFamilyFormat
.getRegionLocation(getCatalogFamilyRow(connection
, regionInfo
),
206 regionInfo
, regionInfo
.getReplicaId());
210 * @return Return the {@link HConstants#CATALOG_FAMILY} row from hbase:meta.
212 public static Result
getCatalogFamilyRow(Connection connection
, RegionInfo ri
)
214 Get get
= new Get(CatalogFamilyFormat
.getMetaKeyForRegion(ri
));
215 get
.addFamily(HConstants
.CATALOG_FAMILY
);
216 try (Table t
= getMetaHTable(connection
)) {
222 * Gets the result in hbase:meta for the specified region.
223 * @param connection connection we're using
224 * @param regionName region we're looking for
225 * @return result of the specified region
227 public static Result
getRegionResult(Connection connection
, byte[] regionName
)
229 Get get
= new Get(regionName
);
230 get
.addFamily(HConstants
.CATALOG_FAMILY
);
231 try (Table t
= getMetaHTable(connection
)) {
237 * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>, returning
238 * a single related <code>Result</code> instance if any row is found, null otherwise.
239 * @param connection the connection to query META table.
240 * @param regionEncodedName the region encoded name to look for at META.
241 * @return <code>Result</code> instance with the row related info in META, null otherwise.
242 * @throws IOException if any errors occur while querying META.
244 public static Result
scanByRegionEncodedName(Connection connection
, String regionEncodedName
)
246 RowFilter rowFilter
=
247 new RowFilter(CompareOperator
.EQUAL
, new SubstringComparator(regionEncodedName
));
248 Scan scan
= getMetaScan(connection
.getConfiguration(), 1);
249 scan
.setFilter(rowFilter
);
250 try (Table table
= getMetaHTable(connection
);
251 ResultScanner resultScanner
= table
.getScanner(scan
)) {
252 return resultScanner
.next();
257 * Lists all of the regions currently in META.
258 * @param connection to connect with
259 * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions,
260 * true and we'll leave out offlined regions from returned list
261 * @return List of all user-space regions.
263 public static List
<RegionInfo
> getAllRegions(Connection connection
,
264 boolean excludeOfflinedSplitParents
) throws IOException
{
265 List
<Pair
<RegionInfo
, ServerName
>> result
;
267 result
= getTableRegionsAndLocations(connection
, null, excludeOfflinedSplitParents
);
269 return getListOfRegionInfos(result
);
274 * Gets all of the regions of the specified table. Do not use this method to get meta table
275 * regions, use methods in MetaTableLocator instead.
276 * @param connection connection we're using
277 * @param tableName table we're looking for
278 * @return Ordered list of {@link RegionInfo}.
280 public static List
<RegionInfo
> getTableRegions(Connection connection
, TableName tableName
)
282 return getTableRegions(connection
, tableName
, false);
286 * Gets all of the regions of the specified table. Do not use this method to get meta table
287 * regions, use methods in MetaTableLocator instead.
288 * @param connection connection we're using
289 * @param tableName table we're looking for
290 * @param excludeOfflinedSplitParents If true, do not include offlined split parents in the
292 * @return Ordered list of {@link RegionInfo}.
294 public static List
<RegionInfo
> getTableRegions(Connection connection
, TableName tableName
,
295 final boolean excludeOfflinedSplitParents
) throws IOException
{
296 List
<Pair
<RegionInfo
, ServerName
>> result
=
297 getTableRegionsAndLocations(connection
, tableName
, excludeOfflinedSplitParents
);
298 return getListOfRegionInfos(result
);
301 private static List
<RegionInfo
>
302 getListOfRegionInfos(final List
<Pair
<RegionInfo
, ServerName
>> pairs
) {
303 if (pairs
== null || pairs
.isEmpty()) {
304 return Collections
.emptyList();
306 List
<RegionInfo
> result
= new ArrayList
<>(pairs
.size());
307 for (Pair
<RegionInfo
, ServerName
> pair
: pairs
) {
308 result
.add(pair
.getFirst());
314 * This method creates a Scan object that will only scan catalog rows that belong to the specified
315 * table. It doesn't specify any columns. This is a better alternative to just using a start row
316 * and scan until it hits a new table since that requires parsing the HRI to get the table name.
317 * @param tableName bytes of table's name
318 * @return configured Scan object
320 public static Scan
getScanForTableName(Configuration conf
, TableName tableName
) {
321 // Start key is just the table name with delimiters
322 byte[] startKey
= ClientMetaTableAccessor
.getTableStartRowForMeta(tableName
, QueryType
.REGION
);
323 // Stop key appends the smallest possible char to the table name
324 byte[] stopKey
= ClientMetaTableAccessor
.getTableStopRowForMeta(tableName
, QueryType
.REGION
);
326 Scan scan
= getMetaScan(conf
, -1);
327 scan
.withStartRow(startKey
);
328 scan
.withStopRow(stopKey
);
332 private static Scan
getMetaScan(Configuration conf
, int rowUpperLimit
) {
333 Scan scan
= new Scan();
334 int scannerCaching
= conf
.getInt(HConstants
.HBASE_META_SCANNER_CACHING
,
335 HConstants
.DEFAULT_HBASE_META_SCANNER_CACHING
);
336 if (conf
.getBoolean(HConstants
.USE_META_REPLICAS
, HConstants
.DEFAULT_USE_META_REPLICAS
)) {
337 scan
.setConsistency(Consistency
.TIMELINE
);
339 if (rowUpperLimit
> 0) {
340 scan
.setLimit(rowUpperLimit
);
341 scan
.setReadType(Scan
.ReadType
.PREAD
);
343 scan
.setCaching(scannerCaching
);
348 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
349 * @param connection connection we're using
350 * @param tableName table we're looking for
351 * @return Return list of regioninfos and server.
353 public static List
<Pair
<RegionInfo
, ServerName
>>
354 getTableRegionsAndLocations(Connection connection
, TableName tableName
) throws IOException
{
355 return getTableRegionsAndLocations(connection
, tableName
, true);
359 * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
360 * @param connection connection we're using
361 * @param tableName table to work with, can be null for getting all regions
362 * @param excludeOfflinedSplitParents don't return split parents
363 * @return Return list of regioninfos and server addresses.
365 // What happens here when 1M regions in hbase:meta? This won't scale?
366 public static List
<Pair
<RegionInfo
, ServerName
>> getTableRegionsAndLocations(
367 Connection connection
, @Nullable final TableName tableName
,
368 final boolean excludeOfflinedSplitParents
) throws IOException
{
369 if (tableName
!= null && tableName
.equals(TableName
.META_TABLE_NAME
)) {
370 throw new IOException(
371 "This method can't be used to locate meta regions;" + " use MetaTableLocator instead");
373 // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress
374 ClientMetaTableAccessor
.CollectRegionLocationsVisitor visitor
=
375 new ClientMetaTableAccessor
.CollectRegionLocationsVisitor(excludeOfflinedSplitParents
);
377 ClientMetaTableAccessor
.getTableStartRowForMeta(tableName
, QueryType
.REGION
),
378 ClientMetaTableAccessor
.getTableStopRowForMeta(tableName
, QueryType
.REGION
), QueryType
.REGION
,
380 return visitor
.getResults();
383 public static void fullScanMetaAndPrint(Connection connection
) throws IOException
{
384 ClientMetaTableAccessor
.Visitor v
= r
-> {
385 if (r
== null || r
.isEmpty()) {
388 LOG
.info("fullScanMetaAndPrint.Current Meta Row: " + r
);
389 TableState state
= CatalogFamilyFormat
.getTableState(r
);
391 LOG
.info("fullScanMetaAndPrint.Table State={}" + state
);
393 RegionLocations locations
= CatalogFamilyFormat
.getRegionLocations(r
);
394 if (locations
== null) {
397 for (HRegionLocation loc
: locations
.getRegionLocations()) {
399 LOG
.info("fullScanMetaAndPrint.HRI Print={}", loc
.getRegion());
405 scanMeta(connection
, null, null, QueryType
.ALL
, v
);
408 public static void scanMetaForTableRegions(Connection connection
,
409 ClientMetaTableAccessor
.Visitor visitor
, TableName tableName
) throws IOException
{
410 scanMeta(connection
, tableName
, QueryType
.REGION
, Integer
.MAX_VALUE
, visitor
);
413 private static void scanMeta(Connection connection
, TableName table
, QueryType type
, int maxRows
,
414 final ClientMetaTableAccessor
.Visitor visitor
) throws IOException
{
415 scanMeta(connection
, ClientMetaTableAccessor
.getTableStartRowForMeta(table
, type
),
416 ClientMetaTableAccessor
.getTableStopRowForMeta(table
, type
), type
, maxRows
, visitor
);
419 public static void scanMeta(Connection connection
, @Nullable final byte[] startRow
,
420 @Nullable final byte[] stopRow
, QueryType type
, final ClientMetaTableAccessor
.Visitor visitor
)
422 scanMeta(connection
, startRow
, stopRow
, type
, Integer
.MAX_VALUE
, visitor
);
426 * Performs a scan of META table for given table starting from given row.
427 * @param connection connection we're using
428 * @param visitor visitor to call
429 * @param tableName table withing we scan
430 * @param row start scan from this row
431 * @param rowLimit max number of rows to return
433 public static void scanMeta(Connection connection
, final ClientMetaTableAccessor
.Visitor visitor
,
434 final TableName tableName
, final byte[] row
, final int rowLimit
) throws IOException
{
435 byte[] startRow
= null;
436 byte[] stopRow
= null;
437 if (tableName
!= null) {
438 startRow
= ClientMetaTableAccessor
.getTableStartRowForMeta(tableName
, QueryType
.REGION
);
440 RegionInfo closestRi
= getClosestRegionInfo(connection
, tableName
, row
);
442 RegionInfo
.createRegionName(tableName
, closestRi
.getStartKey(), HConstants
.ZEROES
, false);
444 stopRow
= ClientMetaTableAccessor
.getTableStopRowForMeta(tableName
, QueryType
.REGION
);
446 scanMeta(connection
, startRow
, stopRow
, QueryType
.REGION
, rowLimit
, visitor
);
450 * Performs a scan of META table.
451 * @param connection connection we're using
452 * @param startRow Where to start the scan. Pass null if want to begin scan at first row.
453 * @param stopRow Where to stop the scan. Pass null if want to scan all rows from the start one
454 * @param type scanned part of meta
455 * @param maxRows maximum rows to return
456 * @param visitor Visitor invoked against each row.
458 public static void scanMeta(Connection connection
, @Nullable final byte[] startRow
,
459 @Nullable final byte[] stopRow
, QueryType type
, int maxRows
,
460 final ClientMetaTableAccessor
.Visitor visitor
) throws IOException
{
461 scanMeta(connection
, startRow
, stopRow
, type
, null, maxRows
, visitor
);
464 public static void scanMeta(Connection connection
, @Nullable final byte[] startRow
,
465 @Nullable final byte[] stopRow
, QueryType type
, @Nullable Filter filter
, int maxRows
,
466 final ClientMetaTableAccessor
.Visitor visitor
) throws IOException
{
467 int rowUpperLimit
= maxRows
> 0 ? maxRows
: Integer
.MAX_VALUE
;
468 Scan scan
= getMetaScan(connection
.getConfiguration(), rowUpperLimit
);
470 for (byte[] family
: type
.getFamilies()) {
471 scan
.addFamily(family
);
473 if (startRow
!= null) {
474 scan
.withStartRow(startRow
);
476 if (stopRow
!= null) {
477 scan
.withStopRow(stopRow
);
479 if (filter
!= null) {
480 scan
.setFilter(filter
);
483 if (LOG
.isTraceEnabled()) {
484 LOG
.trace("Scanning META" + " starting at row=" + Bytes
.toStringBinary(startRow
) +
485 " stopping at row=" + Bytes
.toStringBinary(stopRow
) + " for max=" + rowUpperLimit
+
486 " with caching=" + scan
.getCaching());
490 try (Table metaTable
= getMetaHTable(connection
)) {
491 try (ResultScanner scanner
= metaTable
.getScanner(scan
)) {
493 while ((data
= scanner
.next()) != null) {
494 if (data
.isEmpty()) {
497 // Break if visit returns false.
498 if (!visitor
.visit(data
)) {
501 if (++currentRow
>= rowUpperLimit
) {
507 if (visitor
instanceof Closeable
) {
509 ((Closeable
) visitor
).close();
510 } catch (Throwable t
) {
511 ExceptionUtil
.rethrowIfInterrupt(t
);
512 LOG
.debug("Got exception in closing the meta scanner visitor", t
);
518 * @return Get closest metatable region row to passed <code>row</code>
521 private static RegionInfo
getClosestRegionInfo(Connection connection
,
522 @NonNull final TableName tableName
, @NonNull final byte[] row
) throws IOException
{
523 byte[] searchRow
= RegionInfo
.createRegionName(tableName
, row
, HConstants
.NINES
, false);
524 Scan scan
= getMetaScan(connection
.getConfiguration(), 1);
525 scan
.setReversed(true);
526 scan
.withStartRow(searchRow
);
527 try (ResultScanner resultScanner
= getMetaHTable(connection
).getScanner(scan
)) {
528 Result result
= resultScanner
.next();
529 if (result
== null) {
530 throw new TableNotFoundException("Cannot find row in META " + " for table: " + tableName
+
531 ", row=" + Bytes
.toStringBinary(row
));
533 RegionInfo regionInfo
= CatalogFamilyFormat
.getRegionInfo(result
);
534 if (regionInfo
== null) {
535 throw new IOException("RegionInfo was null or empty in Meta for " + tableName
+ ", row=" +
536 Bytes
.toStringBinary(row
));
543 * Returns the {@link ServerName} from catalog table {@link Result} where the region is
544 * transitioning on. It should be the same as
545 * {@link CatalogFamilyFormat#getServerName(Result,int)} if the server is at OPEN state.
546 * @param r Result to pull the transitioning server name from
547 * @return A ServerName instance or {@link CatalogFamilyFormat#getServerName(Result,int)} if
548 * necessary fields not found or empty.
551 public static ServerName
getTargetServerName(final Result r
, final int replicaId
) {
552 final Cell cell
= r
.getColumnLatestCell(HConstants
.CATALOG_FAMILY
,
553 CatalogFamilyFormat
.getServerNameColumn(replicaId
));
554 if (cell
== null || cell
.getValueLength() == 0) {
555 RegionLocations locations
= CatalogFamilyFormat
.getRegionLocations(r
);
556 if (locations
!= null) {
557 HRegionLocation location
= locations
.getRegionLocation(replicaId
);
558 if (location
!= null) {
559 return location
.getServerName();
564 return ServerName
.parseServerName(
565 Bytes
.toString(cell
.getValueArray(), cell
.getValueOffset(), cell
.getValueLength()));
569 * Returns the daughter regions by reading the corresponding columns of the catalog table Result.
570 * @param data a Result object from the catalog table scan
571 * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent
573 public static PairOfSameType
<RegionInfo
> getDaughterRegions(Result data
) {
574 RegionInfo splitA
= CatalogFamilyFormat
.getRegionInfo(data
, HConstants
.SPLITA_QUALIFIER
);
575 RegionInfo splitB
= CatalogFamilyFormat
.getRegionInfo(data
, HConstants
.SPLITB_QUALIFIER
);
576 return new PairOfSameType
<>(splitA
, splitB
);
580 * Fetch table state for given table from META table
581 * @param conn connection to use
582 * @param tableName table to fetch state for
585 public static TableState
getTableState(Connection conn
, TableName tableName
) throws IOException
{
586 if (tableName
.equals(TableName
.META_TABLE_NAME
)) {
587 return new TableState(tableName
, TableState
.State
.ENABLED
);
589 Table metaHTable
= getMetaHTable(conn
);
590 Get get
= new Get(tableName
.getName()).addColumn(HConstants
.TABLE_FAMILY
,
591 HConstants
.TABLE_STATE_QUALIFIER
);
592 Result result
= metaHTable
.get(get
);
593 return CatalogFamilyFormat
.getTableState(result
);
597 * Fetch table states from META table
598 * @param conn connection to use
599 * @return map {tableName -> state}
601 public static Map
<TableName
, TableState
> getTableStates(Connection conn
) throws IOException
{
602 final Map
<TableName
, TableState
> states
= new LinkedHashMap
<>();
603 ClientMetaTableAccessor
.Visitor collector
= r
-> {
604 TableState state
= CatalogFamilyFormat
.getTableState(r
);
606 states
.put(state
.getTableName(), state
);
610 fullScanTables(conn
, collector
);
615 * Updates state in META Do not use. For internal use only.
616 * @param conn connection to use
617 * @param tableName table to look for
619 public static void updateTableState(Connection conn
, TableName tableName
, TableState
.State actual
)
621 updateTableState(conn
, new TableState(tableName
, actual
));
624 ////////////////////////
625 // Editing operations //
626 ////////////////////////
628 * Generates and returns a Put containing the region into for the catalog table
630 public static Put
makePutFromRegionInfo(RegionInfo regionInfo
, long ts
) throws IOException
{
631 return addRegionInfo(new Put(regionInfo
.getRegionName(), ts
), regionInfo
);
635 * Generates and returns a Delete containing the region info for the catalog table
637 public static Delete
makeDeleteFromRegionInfo(RegionInfo regionInfo
, long ts
) {
638 if (regionInfo
== null) {
639 throw new IllegalArgumentException("Can't make a delete for null region");
641 Delete delete
= new Delete(regionInfo
.getRegionName());
642 delete
.addFamily(HConstants
.CATALOG_FAMILY
, ts
);
647 * Adds split daughters to the Put
649 public static Put
addDaughtersToPut(Put put
, RegionInfo splitA
, RegionInfo splitB
)
651 if (splitA
!= null) {
652 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
).setRow(put
.getRow())
653 .setFamily(HConstants
.CATALOG_FAMILY
).setQualifier(HConstants
.SPLITA_QUALIFIER
)
654 .setTimestamp(put
.getTimestamp()).setType(Type
.Put
).setValue(RegionInfo
.toByteArray(splitA
))
657 if (splitB
!= null) {
658 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
).setRow(put
.getRow())
659 .setFamily(HConstants
.CATALOG_FAMILY
).setQualifier(HConstants
.SPLITB_QUALIFIER
)
660 .setTimestamp(put
.getTimestamp()).setType(Type
.Put
).setValue(RegionInfo
.toByteArray(splitB
))
667 * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
668 * @param connection connection we're using
669 * @param p Put to add to hbase:meta
671 private static void putToMetaTable(Connection connection
, Put p
) throws IOException
{
672 try (Table table
= getMetaHTable(connection
)) {
678 * @param t Table to use
679 * @param p put to make
681 private static void put(Table t
, Put p
) throws IOException
{
687 * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
688 * @param connection connection we're using
689 * @param ps Put to add to hbase:meta
691 public static void putsToMetaTable(final Connection connection
, final List
<Put
> ps
)
696 try (Table t
= getMetaHTable(connection
)) {
697 debugLogMutations(ps
);
698 // the implementation for putting a single Put is much simpler so here we do a check first.
699 if (ps
.size() == 1) {
708 * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
709 * @param connection connection we're using
710 * @param d Delete to add to hbase:meta
712 private static void deleteFromMetaTable(final Connection connection
, final Delete d
)
714 List
<Delete
> dels
= new ArrayList
<>(1);
716 deleteFromMetaTable(connection
, dels
);
720 * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
721 * @param connection connection we're using
722 * @param deletes Deletes to add to hbase:meta This list should support #remove.
724 private static void deleteFromMetaTable(final Connection connection
, final List
<Delete
> deletes
)
726 try (Table t
= getMetaHTable(connection
)) {
727 debugLogMutations(deletes
);
732 public static Put
addRegionStateToPut(Put put
, RegionState
.State state
) throws IOException
{
733 put
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
).setRow(put
.getRow())
734 .setFamily(HConstants
.CATALOG_FAMILY
).setQualifier(HConstants
.STATE_QUALIFIER
)
735 .setTimestamp(put
.getTimestamp()).setType(Cell
.Type
.Put
).setValue(Bytes
.toBytes(state
.name()))
741 * Update state column in hbase:meta.
743 public static void updateRegionState(Connection connection
, RegionInfo ri
,
744 RegionState
.State state
) throws IOException
{
745 Put put
= new Put(RegionReplicaUtil
.getRegionInfoForDefaultReplica(ri
).getRegionName());
746 putsToMetaTable(connection
, Collections
.singletonList(addRegionStateToPut(put
, state
)));
750 * Adds daughter region infos to hbase:meta row for the specified region.
752 * Note that this does not add its daughter's as different rows, but adds information about the
753 * daughters in the same row as the parent. Now only used in snapshot. Use
754 * {@link org.apache.hadoop.hbase.master.assignment.RegionStateStore} if you want to split a
756 * @param connection connection we're using
757 * @param regionInfo RegionInfo of parent region
758 * @param splitA first split daughter of the parent regionInfo
759 * @param splitB second split daughter of the parent regionInfo
760 * @throws IOException if problem connecting or updating meta
762 public static void addSplitsToParent(Connection connection
, RegionInfo regionInfo
,
763 RegionInfo splitA
, RegionInfo splitB
) throws IOException
{
764 try (Table meta
= getMetaHTable(connection
)) {
765 Put put
= makePutFromRegionInfo(regionInfo
, EnvironmentEdgeManager
.currentTime());
766 addDaughtersToPut(put
, splitA
, splitB
);
768 debugLogMutation(put
);
769 LOG
.debug("Added region {}", regionInfo
.getRegionNameAsString());
774 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is
776 * @param connection connection we're using
777 * @param regionInfos region information list
778 * @throws IOException if problem connecting or updating meta
780 public static void addRegionsToMeta(Connection connection
, List
<RegionInfo
> regionInfos
,
781 int regionReplication
) throws IOException
{
782 addRegionsToMeta(connection
, regionInfos
, regionReplication
,
783 EnvironmentEdgeManager
.currentTime());
787 * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is
789 * @param connection connection we're using
790 * @param regionInfos region information list
791 * @param ts desired timestamp
792 * @throws IOException if problem connecting or updating meta
794 public static void addRegionsToMeta(Connection connection
, List
<RegionInfo
> regionInfos
,
795 int regionReplication
, long ts
) throws IOException
{
796 List
<Put
> puts
= new ArrayList
<>();
797 for (RegionInfo regionInfo
: regionInfos
) {
798 if (!RegionReplicaUtil
.isDefaultReplica(regionInfo
)) {
801 Put put
= makePutFromRegionInfo(regionInfo
, ts
);
802 // New regions are added with initial state of CLOSED.
803 addRegionStateToPut(put
, RegionState
.State
.CLOSED
);
804 // Add empty locations for region replicas so that number of replicas can be cached
805 // whenever the primary region is looked up from meta
806 for (int i
= 1; i
< regionReplication
; i
++) {
807 addEmptyLocation(put
, i
);
811 putsToMetaTable(connection
, puts
);
812 LOG
.info("Added {} regions to meta.", puts
.size());
816 * Update state of the table in meta.
817 * @param connection what we use for update
818 * @param state new state
820 private static void updateTableState(Connection connection
, TableState state
) throws IOException
{
821 Put put
= makePutFromTableState(state
, EnvironmentEdgeManager
.currentTime());
822 putToMetaTable(connection
, put
);
823 LOG
.info("Updated {} in hbase:meta", state
);
827 * Construct PUT for given state
828 * @param state new state
830 public static Put
makePutFromTableState(TableState state
, long ts
) {
831 Put put
= new Put(state
.getTableName().getName(), ts
);
832 put
.addColumn(HConstants
.TABLE_FAMILY
, HConstants
.TABLE_STATE_QUALIFIER
,
833 state
.convert().toByteArray());
838 * Remove state for table from meta
839 * @param connection to use for deletion
840 * @param table to delete state for
842 public static void deleteTableState(Connection connection
, TableName table
) throws IOException
{
843 long time
= EnvironmentEdgeManager
.currentTime();
844 Delete delete
= new Delete(table
.getName());
845 delete
.addColumns(HConstants
.TABLE_FAMILY
, HConstants
.TABLE_STATE_QUALIFIER
, time
);
846 deleteFromMetaTable(connection
, delete
);
847 LOG
.info("Deleted table " + table
+ " state from META");
851 * Updates the location of the specified region in hbase:meta to be the specified server hostname
854 * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes
855 * edits to that region.
856 * @param connection connection we're using
857 * @param regionInfo region to update location of
858 * @param openSeqNum the latest sequence number obtained when the region was open
859 * @param sn Server name
860 * @param masterSystemTime wall clock time from master if passed in the open region RPC
862 public static void updateRegionLocation(Connection connection
, RegionInfo regionInfo
,
863 ServerName sn
, long openSeqNum
, long masterSystemTime
) throws IOException
{
864 updateLocation(connection
, regionInfo
, sn
, openSeqNum
, masterSystemTime
);
868 * Updates the location of the specified region to be the specified server.
870 * Connects to the specified server which should be hosting the specified catalog region name to
872 * @param connection connection we're using
873 * @param regionInfo region to update location of
874 * @param sn Server name
875 * @param openSeqNum the latest sequence number obtained when the region was open
876 * @param masterSystemTime wall clock time from master if passed in the open region RPC
877 * @throws IOException In particular could throw {@link java.net.ConnectException} if the server
878 * is down on other end.
880 private static void updateLocation(Connection connection
, RegionInfo regionInfo
, ServerName sn
,
881 long openSeqNum
, long masterSystemTime
) throws IOException
{
882 // region replicas are kept in the primary region's row
883 Put put
= new Put(CatalogFamilyFormat
.getMetaKeyForRegion(regionInfo
), masterSystemTime
);
884 addRegionInfo(put
, regionInfo
);
885 addLocation(put
, sn
, openSeqNum
, regionInfo
.getReplicaId());
886 putToMetaTable(connection
, put
);
887 LOG
.info("Updated row {} with server=", regionInfo
.getRegionNameAsString(), sn
);
890 public static Put
addRegionInfo(final Put p
, final RegionInfo hri
) throws IOException
{
891 p
.add(CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
).setRow(p
.getRow())
892 .setFamily(HConstants
.CATALOG_FAMILY
).setQualifier(HConstants
.REGIONINFO_QUALIFIER
)
893 .setTimestamp(p
.getTimestamp()).setType(Type
.Put
)
894 // Serialize the Default Replica HRI otherwise scan of hbase:meta
895 // shows an info:regioninfo value with encoded name and region
896 // name that differs from that of the hbase;meta row.
897 .setValue(RegionInfo
.toByteArray(RegionReplicaUtil
.getRegionInfoForDefaultReplica(hri
)))
902 public static Put
addLocation(Put p
, ServerName sn
, long openSeqNum
, int replicaId
)
904 CellBuilder builder
= CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
);
906 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
907 .setQualifier(CatalogFamilyFormat
.getServerColumn(replicaId
)).setTimestamp(p
.getTimestamp())
908 .setType(Cell
.Type
.Put
).setValue(Bytes
.toBytes(sn
.getAddress().toString())).build())
909 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
910 .setQualifier(CatalogFamilyFormat
.getStartCodeColumn(replicaId
))
911 .setTimestamp(p
.getTimestamp()).setType(Cell
.Type
.Put
)
912 .setValue(Bytes
.toBytes(sn
.getStartcode())).build())
913 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
914 .setQualifier(CatalogFamilyFormat
.getSeqNumColumn(replicaId
)).setTimestamp(p
.getTimestamp())
915 .setType(Type
.Put
).setValue(Bytes
.toBytes(openSeqNum
)).build());
918 public static Put
addEmptyLocation(Put p
, int replicaId
) throws IOException
{
919 CellBuilder builder
= CellBuilderFactory
.create(CellBuilderType
.SHALLOW_COPY
);
921 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
922 .setQualifier(CatalogFamilyFormat
.getServerColumn(replicaId
)).setTimestamp(p
.getTimestamp())
923 .setType(Type
.Put
).build())
924 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
925 .setQualifier(CatalogFamilyFormat
.getStartCodeColumn(replicaId
))
926 .setTimestamp(p
.getTimestamp()).setType(Cell
.Type
.Put
).build())
927 .add(builder
.clear().setRow(p
.getRow()).setFamily(HConstants
.CATALOG_FAMILY
)
928 .setQualifier(CatalogFamilyFormat
.getSeqNumColumn(replicaId
)).setTimestamp(p
.getTimestamp())
929 .setType(Cell
.Type
.Put
).build());
933 private static void debugLogMutations(List
<?
extends Mutation
> mutations
) throws IOException
{
934 if (!METALOG
.isDebugEnabled()) {
937 // Logging each mutation in separate line makes it easier to see diff between them visually
938 // because of common starting indentation.
939 for (Mutation mutation
: mutations
) {
940 debugLogMutation(mutation
);
944 private static void debugLogMutation(Mutation p
) throws IOException
{
945 METALOG
.debug("{} {}", p
.getClass().getSimpleName(), p
.toJSON());