HBASE-26286: Add support for specifying store file tracker when restoring or cloning...
[hbase.git] / hbase-server / src / main / java / org / apache / hadoop / hbase / MetaTableAccessor.java
blob512916f483e00cf1a25b015eddb05380ff83f9ed
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 package org.apache.hadoop.hbase;
20 import edu.umd.cs.findbugs.annotations.NonNull;
21 import edu.umd.cs.findbugs.annotations.Nullable;
22 import java.io.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;
28 import java.util.Map;
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;
59 /**
60 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
61 * to <code>hbase:meta</code>.
62 * <p/>
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.
67 * <p/>
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.
71 * <p/>
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:
75 * <pre>
76 * table:state =&gt; contains table state
77 * </pre>
79 * For the catalog family, see the comments of {@link CatalogFamilyFormat} for more details.
80 * <p/>
81 * TODO: Add rep_barrier for serial replication explanation. See SerialReplicationChecker.
82 * <p/>
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
163 @Deprecated
164 public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte[] regionName)
165 throws IOException {
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)
177 throws IOException {
178 byte[] row = regionName;
179 RegionInfo parsedInfo = null;
180 try {
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);
188 Result r;
189 try (Table t = getMetaHTable(connection)) {
190 r = t.get(get);
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)
204 throws IOException {
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)
213 throws IOException {
214 Get get = new Get(CatalogFamilyFormat.getMetaKeyForRegion(ri));
215 get.addFamily(HConstants.CATALOG_FAMILY);
216 try (Table t = getMetaHTable(connection)) {
217 return t.get(get);
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)
228 throws IOException {
229 Get get = new Get(regionName);
230 get.addFamily(HConstants.CATALOG_FAMILY);
231 try (Table t = getMetaHTable(connection)) {
232 return t.get(get);
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)
245 throws IOException {
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)
281 throws IOException {
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
291 * return.
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());
310 return result;
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);
329 return scan;
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);
344 return scan;
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);
376 scanMeta(connection,
377 ClientMetaTableAccessor.getTableStartRowForMeta(tableName, QueryType.REGION),
378 ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION), QueryType.REGION,
379 visitor);
380 return visitor.getResults();
383 public static void fullScanMetaAndPrint(Connection connection) throws IOException {
384 ClientMetaTableAccessor.Visitor v = r -> {
385 if (r == null || r.isEmpty()) {
386 return true;
388 LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
389 TableState state = CatalogFamilyFormat.getTableState(r);
390 if (state != null) {
391 LOG.info("fullScanMetaAndPrint.Table State={}" + state);
392 } else {
393 RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r);
394 if (locations == null) {
395 return true;
397 for (HRegionLocation loc : locations.getRegionLocations()) {
398 if (loc != null) {
399 LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion());
403 return true;
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)
421 throws IOException {
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);
439 if (row != null) {
440 RegionInfo closestRi = getClosestRegionInfo(connection, tableName, row);
441 startRow =
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());
489 int currentRow = 0;
490 try (Table metaTable = getMetaHTable(connection)) {
491 try (ResultScanner scanner = metaTable.getScanner(scan)) {
492 Result data;
493 while ((data = scanner.next()) != null) {
494 if (data.isEmpty()) {
495 continue;
497 // Break if visit returns false.
498 if (!visitor.visit(data)) {
499 break;
501 if (++currentRow >= rowUpperLimit) {
502 break;
507 if (visitor instanceof Closeable) {
508 try {
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>
520 @NonNull
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));
538 return regionInfo;
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.
550 @Nullable
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();
562 return null;
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
584 @Nullable
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 -&gt; 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);
605 if (state != null) {
606 states.put(state.getTableName(), state);
608 return true;
610 fullScanTables(conn, collector);
611 return states;
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)
620 throws IOException {
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);
643 return delete;
647 * Adds split daughters to the Put
649 public static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB)
650 throws IOException {
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))
655 .build());
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))
661 .build());
663 return put;
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)) {
673 put(table, p);
678 * @param t Table to use
679 * @param p put to make
681 private static void put(Table t, Put p) throws IOException {
682 debugLogMutation(p);
683 t.put(p);
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)
692 throws IOException {
693 if (ps.isEmpty()) {
694 return;
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) {
700 t.put(ps.get(0));
701 } else {
702 t.put(ps);
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)
713 throws IOException {
714 List<Delete> dels = new ArrayList<>(1);
715 dels.add(d);
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)
725 throws IOException {
726 try (Table t = getMetaHTable(connection)) {
727 debugLogMutations(deletes);
728 t.delete(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()))
736 .build());
737 return put;
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.
751 * <p/>
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
755 * region.
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);
767 meta.put(put);
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
775 * CLOSED.
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
788 * CLOSED.
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)) {
799 continue;
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);
809 puts.add(put);
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());
834 return put;
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
852 * and startcode.
853 * <p>
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.
869 * <p>
870 * Connects to the specified server which should be hosting the specified catalog region name to
871 * perform the edit.
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)))
898 .build());
899 return p;
902 public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId)
903 throws IOException {
904 CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
905 return p
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);
920 return p
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()) {
935 return;
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());