001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.apache.hadoop.hbase;
019
020import edu.umd.cs.findbugs.annotations.NonNull;
021import edu.umd.cs.findbugs.annotations.Nullable;
022import java.io.Closeable;
023import java.io.IOException;
024import java.util.ArrayList;
025import java.util.Collections;
026import java.util.LinkedHashMap;
027import java.util.List;
028import java.util.Map;
029import java.util.Objects;
030import org.apache.hadoop.conf.Configuration;
031import org.apache.hadoop.hbase.Cell.Type;
032import org.apache.hadoop.hbase.ClientMetaTableAccessor.QueryType;
033import org.apache.hadoop.hbase.client.Connection;
034import org.apache.hadoop.hbase.client.Consistency;
035import org.apache.hadoop.hbase.client.Delete;
036import org.apache.hadoop.hbase.client.Get;
037import org.apache.hadoop.hbase.client.Mutation;
038import org.apache.hadoop.hbase.client.Put;
039import org.apache.hadoop.hbase.client.RegionInfo;
040import org.apache.hadoop.hbase.client.RegionReplicaUtil;
041import org.apache.hadoop.hbase.client.Result;
042import org.apache.hadoop.hbase.client.ResultScanner;
043import org.apache.hadoop.hbase.client.Scan;
044import org.apache.hadoop.hbase.client.Table;
045import org.apache.hadoop.hbase.client.TableState;
046import org.apache.hadoop.hbase.filter.Filter;
047import org.apache.hadoop.hbase.filter.RowFilter;
048import org.apache.hadoop.hbase.filter.SubstringComparator;
049import org.apache.hadoop.hbase.master.RegionState;
050import org.apache.hadoop.hbase.regionserver.RSAnnotationReadingPriorityFunction;
051import org.apache.hadoop.hbase.util.Bytes;
052import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
053import org.apache.hadoop.hbase.util.ExceptionUtil;
054import org.apache.hadoop.hbase.util.Pair;
055import org.apache.hadoop.hbase.util.PairOfSameType;
056import org.apache.yetus.audience.InterfaceAudience;
057import org.slf4j.Logger;
058import org.slf4j.LoggerFactory;
059
060/**
061 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
062 * to <code>hbase:meta</code>.
063 * <p/>
064 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is
065 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection
066 * (opened before each operation, closed right after), while when used on HM or HRS (like in
067 * AssignmentManager) we want permanent connection.
068 * <p/>
069 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table
070 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is
071 * called default replica.
072 * <p/>
073 * <h2>Meta layout</h2> For each table there is single row named for the table with a 'table' column
074 * family. The column family currently has one column in it, the 'state' column:
075 *
076 * <pre>
077 * table:state =&gt; contains table state
078 * </pre>
079 *
080 * For the catalog family, see the comments of {@link CatalogFamilyFormat} for more details.
081 * <p/>
082 * TODO: Add rep_barrier for serial replication explanation. See SerialReplicationChecker.
083 * <p/>
084 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not
085 * leak out of it (through Result objects, etc)
086 * @see CatalogFamilyFormat
087 * @see ClientMetaTableAccessor
088 */
089@InterfaceAudience.Private
090public final class MetaTableAccessor {
091
092  private static final Logger LOG = LoggerFactory.getLogger(MetaTableAccessor.class);
093  private static final Logger METALOG = LoggerFactory.getLogger("org.apache.hadoop.hbase.META");
094
095  private MetaTableAccessor() {
096  }
097
098  ////////////////////////
099  // Reading operations //
100  ////////////////////////
101
102  /**
103   * Performs a full scan of <code>hbase:meta</code> for regions.
104   * @param connection connection we're using
105   * @param visitor    Visitor invoked against each row in regions family.
106   */
107  public static void fullScanRegions(Connection connection,
108    final ClientMetaTableAccessor.Visitor visitor) throws IOException {
109    scanMeta(connection, null, null, QueryType.REGION, visitor);
110  }
111
112  /**
113   * Performs a full scan of <code>hbase:meta</code> for regions.
114   * @param connection connection we're using
115   */
116  public static List<Result> fullScanRegions(Connection connection) throws IOException {
117    return fullScan(connection, QueryType.REGION);
118  }
119
120  /**
121   * Performs a full scan of <code>hbase:meta</code> for tables.
122   * @param connection connection we're using
123   * @param visitor    Visitor invoked against each row in tables family.
124   */
125  public static void fullScanTables(Connection connection,
126    final ClientMetaTableAccessor.Visitor visitor) throws IOException {
127    scanMeta(connection, null, null, QueryType.TABLE, visitor);
128  }
129
130  /**
131   * Performs a full scan of <code>hbase:meta</code>.
132   * @param connection connection we're using
133   * @param type       scanned part of meta
134   * @return List of {@link Result}
135   */
136  private static List<Result> fullScan(Connection connection, QueryType type) throws IOException {
137    ClientMetaTableAccessor.CollectAllVisitor v = new ClientMetaTableAccessor.CollectAllVisitor();
138    scanMeta(connection, null, null, type, v);
139    return v.getResults();
140  }
141
142  /**
143   * Callers should call close on the returned {@link Table} instance.
144   * @param connection connection we're using to access Meta
145   * @return An {@link Table} for <code>hbase:meta</code>
146   * @throws NullPointerException if {@code connection} is {@code null}
147   */
148  public static Table getMetaHTable(final Connection connection) throws IOException {
149    // We used to pass whole CatalogTracker in here, now we just pass in Connection
150    Objects.requireNonNull(connection, "Connection cannot be null");
151    if (connection.isClosed()) {
152      throw new IOException("connection is closed");
153    }
154    return connection.getTable(TableName.META_TABLE_NAME);
155  }
156
157  /**
158   * Gets the region info and assignment for the specified region.
159   * @param connection connection we're using
160   * @param regionName Region to lookup.
161   * @return Location and RegionInfo for <code>regionName</code>
162   * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
163   */
164  @Deprecated
165  public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte[] regionName)
166    throws IOException {
167    HRegionLocation location = getRegionLocation(connection, regionName);
168    return location == null ? null : new Pair<>(location.getRegion(), location.getServerName());
169  }
170
171  /**
172   * Returns the HRegionLocation from meta for the given region
173   * @param connection connection we're using
174   * @param regionName region we're looking for
175   * @return HRegionLocation for the given region
176   */
177  public static HRegionLocation getRegionLocation(Connection connection, byte[] regionName)
178    throws IOException {
179    byte[] row = regionName;
180    RegionInfo parsedInfo = null;
181    try {
182      parsedInfo = CatalogFamilyFormat.parseRegionInfoFromRegionName(regionName);
183      row = CatalogFamilyFormat.getMetaKeyForRegion(parsedInfo);
184    } catch (Exception parseEx) {
185      // Ignore. This is used with tableName passed as regionName.
186    }
187    Get get = new Get(row);
188    get.addFamily(HConstants.CATALOG_FAMILY);
189    get.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS);
190    Result r;
191    try (Table t = getMetaHTable(connection)) {
192      r = t.get(get);
193    }
194    RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r);
195    return locations == null
196      ? null
197      : locations.getRegionLocation(
198        parsedInfo == null ? RegionInfo.DEFAULT_REPLICA_ID : parsedInfo.getReplicaId());
199  }
200
201  /**
202   * Returns the HRegionLocation from meta for the given region
203   * @param connection connection we're using
204   * @param regionInfo region information
205   * @return HRegionLocation for the given region
206   */
207  public static HRegionLocation getRegionLocation(Connection connection, RegionInfo regionInfo)
208    throws IOException {
209    return CatalogFamilyFormat.getRegionLocation(getCatalogFamilyRow(connection, regionInfo),
210      regionInfo, regionInfo.getReplicaId());
211  }
212
213  /** Returns Return the {@link HConstants#CATALOG_FAMILY} row from hbase:meta. */
214  public static Result getCatalogFamilyRow(Connection connection, RegionInfo ri)
215    throws IOException {
216    Get get = new Get(CatalogFamilyFormat.getMetaKeyForRegion(ri));
217    get.addFamily(HConstants.CATALOG_FAMILY);
218    get.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS);
219    try (Table t = getMetaHTable(connection)) {
220      return t.get(get);
221    }
222  }
223
224  /**
225   * Gets the result in hbase:meta for the specified region.
226   * @param connection connection we're using
227   * @param regionInfo region we're looking for
228   * @return result of the specified region
229   */
230  public static Result getRegionResult(Connection connection, RegionInfo regionInfo)
231    throws IOException {
232    return getCatalogFamilyRow(connection, regionInfo);
233  }
234
235  /**
236   * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>, returning
237   * a single related <code>Result</code> instance if any row is found, null otherwise.
238   * @param connection        the connection to query META table.
239   * @param regionEncodedName the region encoded name to look for at META.
240   * @return <code>Result</code> instance with the row related info in META, null otherwise.
241   * @throws IOException if any errors occur while querying META.
242   */
243  public static Result scanByRegionEncodedName(Connection connection, String regionEncodedName)
244    throws IOException {
245    RowFilter rowFilter =
246      new RowFilter(CompareOperator.EQUAL, new SubstringComparator(regionEncodedName));
247    Scan scan = getMetaScan(connection.getConfiguration(), 1);
248    scan.setFilter(rowFilter);
249    try (Table table = getMetaHTable(connection);
250      ResultScanner resultScanner = table.getScanner(scan)) {
251      return resultScanner.next();
252    }
253  }
254
255  /**
256   * Lists all of the regions currently in META.
257   * @param connection                  to connect with
258   * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions,
259   *                                    true and we'll leave out offlined regions from returned list
260   * @return List of all user-space regions.
261   */
262  public static List<RegionInfo> getAllRegions(Connection connection,
263    boolean excludeOfflinedSplitParents) throws IOException {
264    List<Pair<RegionInfo, ServerName>> result;
265
266    result = getTableRegionsAndLocations(connection, null, excludeOfflinedSplitParents);
267
268    return getListOfRegionInfos(result);
269
270  }
271
272  /**
273   * Gets all of the regions of the specified table. Do not use this method to get meta table
274   * regions, use methods in MetaTableLocator instead.
275   * @param connection connection we're using
276   * @param tableName  table we're looking for
277   * @return Ordered list of {@link RegionInfo}.
278   */
279  public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName)
280    throws IOException {
281    return getTableRegions(connection, tableName, false);
282  }
283
284  /**
285   * Gets all of the regions of the specified table. Do not use this method to get meta table
286   * regions, use methods in MetaTableLocator instead.
287   * @param connection                  connection we're using
288   * @param tableName                   table we're looking for
289   * @param excludeOfflinedSplitParents If true, do not include offlined split parents in the
290   *                                    return.
291   * @return Ordered list of {@link RegionInfo}.
292   */
293  public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName,
294    final boolean excludeOfflinedSplitParents) throws IOException {
295    List<Pair<RegionInfo, ServerName>> result =
296      getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents);
297    return getListOfRegionInfos(result);
298  }
299
300  private static List<RegionInfo>
301    getListOfRegionInfos(final List<Pair<RegionInfo, ServerName>> pairs) {
302    if (pairs == null || pairs.isEmpty()) {
303      return Collections.emptyList();
304    }
305    List<RegionInfo> result = new ArrayList<>(pairs.size());
306    for (Pair<RegionInfo, ServerName> pair : pairs) {
307      result.add(pair.getFirst());
308    }
309    return result;
310  }
311
312  /**
313   * This method creates a Scan object that will only scan catalog rows that belong to the specified
314   * table. It doesn't specify any columns. This is a better alternative to just using a start row
315   * and scan until it hits a new table since that requires parsing the HRI to get the table name.
316   * @param tableName bytes of table's name
317   * @return configured Scan object
318   */
319  public static Scan getScanForTableName(Configuration conf, TableName tableName) {
320    // Start key is just the table name with delimiters
321    byte[] startKey = ClientMetaTableAccessor.getTableStartRowForMeta(tableName, QueryType.REGION);
322    // Stop key appends the smallest possible char to the table name
323    byte[] stopKey = ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION);
324
325    Scan scan = getMetaScan(conf, -1);
326    scan.withStartRow(startKey);
327    scan.withStopRow(stopKey);
328    return scan;
329  }
330
331  private static Scan getMetaScan(Configuration conf, int rowUpperLimit) {
332    Scan scan = new Scan();
333    int scannerCaching = conf.getInt(HConstants.HBASE_META_SCANNER_CACHING,
334      HConstants.DEFAULT_HBASE_META_SCANNER_CACHING);
335    if (conf.getBoolean(HConstants.USE_META_REPLICAS, HConstants.DEFAULT_USE_META_REPLICAS)) {
336      scan.setConsistency(Consistency.TIMELINE);
337    }
338    if (rowUpperLimit > 0) {
339      scan.setLimit(rowUpperLimit);
340      scan.setReadType(Scan.ReadType.PREAD);
341    }
342    scan.setCaching(scannerCaching);
343    scan.setPriority(RSAnnotationReadingPriorityFunction.INTERNAL_READ_QOS);
344    return scan;
345  }
346
347  /**
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.
352   */
353  public static List<Pair<RegionInfo, ServerName>>
354    getTableRegionsAndLocations(Connection connection, TableName tableName) throws IOException {
355    return getTableRegionsAndLocations(connection, tableName, true);
356  }
357
358  /**
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.
364   */
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");
372    }
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();
381  }
382
383  public static void fullScanMetaAndPrint(Connection connection) throws IOException {
384    ClientMetaTableAccessor.Visitor v = r -> {
385      if (r == null || r.isEmpty()) {
386        return true;
387      }
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;
396        }
397        for (HRegionLocation loc : locations.getRegionLocations()) {
398          if (loc != null) {
399            LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion());
400          }
401        }
402      }
403      return true;
404    };
405    scanMeta(connection, null, null, QueryType.ALL, v);
406  }
407
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);
411  }
412
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);
417  }
418
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);
423  }
424
425  /**
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
432   */
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);
443      }
444      stopRow = ClientMetaTableAccessor.getTableStopRowForMeta(tableName, QueryType.REGION);
445    }
446    scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor);
447  }
448
449  /**
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.
457   */
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);
462  }
463
464  /**
465   * Performs a scan of META table.
466   * @param connection connection we're using
467   * @param startRow   Where to start the scan. Pass null if want to begin scan at first row.
468   * @param stopRow    Where to stop the scan. Pass null if want to scan all rows from the start one
469   * @param type       scanned part of meta
470   * @param maxRows    maximum rows to return
471   * @param visitor    Visitor invoked against each row.
472   */
473  public static void scanMeta(Connection connection, @Nullable final byte[] startRow,
474    @Nullable final byte[] stopRow, QueryType type, @Nullable Filter filter, int maxRows,
475    final ClientMetaTableAccessor.Visitor visitor) throws IOException {
476    int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE;
477    Scan scan = getMetaScan(connection.getConfiguration(), rowUpperLimit);
478
479    for (byte[] family : type.getFamilies()) {
480      scan.addFamily(family);
481    }
482    if (startRow != null) {
483      scan.withStartRow(startRow);
484    }
485    if (stopRow != null) {
486      scan.withStopRow(stopRow);
487    }
488    if (filter != null) {
489      scan.setFilter(filter);
490    }
491
492    if (LOG.isTraceEnabled()) {
493      LOG.trace(
494        "Scanning META starting at row={} stopping at row={} for max={} with caching={} "
495          + "priority={}",
496        Bytes.toStringBinary(startRow), Bytes.toStringBinary(stopRow), rowUpperLimit,
497        scan.getCaching(), scan.getPriority());
498    }
499
500    int currentRow = 0;
501    try (Table metaTable = getMetaHTable(connection)) {
502      try (ResultScanner scanner = metaTable.getScanner(scan)) {
503        Result data;
504        while ((data = scanner.next()) != null) {
505          if (data.isEmpty()) {
506            continue;
507          }
508          // Break if visit returns false.
509          if (!visitor.visit(data)) {
510            break;
511          }
512          if (++currentRow >= rowUpperLimit) {
513            break;
514          }
515        }
516      }
517    }
518    if (visitor instanceof Closeable) {
519      try {
520        ((Closeable) visitor).close();
521      } catch (Throwable t) {
522        ExceptionUtil.rethrowIfInterrupt(t);
523        LOG.debug("Got exception in closing the meta scanner visitor", t);
524      }
525    }
526  }
527
528  /** Returns Get closest metatable region row to passed <code>row</code> */
529  @NonNull
530  private static RegionInfo getClosestRegionInfo(Connection connection,
531    @NonNull final TableName tableName, @NonNull final byte[] row) throws IOException {
532    byte[] searchRow = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
533    Scan scan = getMetaScan(connection.getConfiguration(), 1);
534    scan.setReversed(true);
535    scan.withStartRow(searchRow);
536    try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) {
537      Result result = resultScanner.next();
538      if (result == null) {
539        throw new TableNotFoundException("Cannot find row in META " + " for table: " + tableName
540          + ", row=" + Bytes.toStringBinary(row));
541      }
542      RegionInfo regionInfo = CatalogFamilyFormat.getRegionInfo(result);
543      if (regionInfo == null) {
544        throw new IOException("RegionInfo was null or empty in Meta for " + tableName + ", row="
545          + Bytes.toStringBinary(row));
546      }
547      return regionInfo;
548    }
549  }
550
551  /**
552   * Returns the {@link ServerName} from catalog table {@link Result} where the region is
553   * transitioning on. It should be the same as
554   * {@link CatalogFamilyFormat#getServerName(Result,int)} if the server is at OPEN state.
555   * @param r Result to pull the transitioning server name from
556   * @return A ServerName instance or {@link CatalogFamilyFormat#getServerName(Result,int)} if
557   *         necessary fields not found or empty.
558   */
559  @Nullable
560  public static ServerName getTargetServerName(final Result r, final int replicaId) {
561    final Cell cell = r.getColumnLatestCell(HConstants.CATALOG_FAMILY,
562      CatalogFamilyFormat.getServerNameColumn(replicaId));
563    if (cell == null || cell.getValueLength() == 0) {
564      RegionLocations locations = CatalogFamilyFormat.getRegionLocations(r);
565      if (locations != null) {
566        HRegionLocation location = locations.getRegionLocation(replicaId);
567        if (location != null) {
568          return location.getServerName();
569        }
570      }
571      return null;
572    }
573    return ServerName.parseServerName(
574      Bytes.toString(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
575  }
576
577  /**
578   * Returns the daughter regions by reading the corresponding columns of the catalog table Result.
579   * @param data a Result object from the catalog table scan
580   * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent
581   */
582  public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) {
583    RegionInfo splitA = CatalogFamilyFormat.getRegionInfo(data, HConstants.SPLITA_QUALIFIER);
584    RegionInfo splitB = CatalogFamilyFormat.getRegionInfo(data, HConstants.SPLITB_QUALIFIER);
585    return new PairOfSameType<>(splitA, splitB);
586  }
587
588  /**
589   * Fetch table state for given table from META table
590   * @param conn      connection to use
591   * @param tableName table to fetch state for
592   */
593  @Nullable
594  public static TableState getTableState(Connection conn, TableName tableName) throws IOException {
595    if (tableName.equals(TableName.META_TABLE_NAME)) {
596      return new TableState(tableName, TableState.State.ENABLED);
597    }
598    Table metaHTable = getMetaHTable(conn);
599    Get get = new Get(tableName.getName()).addColumn(HConstants.TABLE_FAMILY,
600      HConstants.TABLE_STATE_QUALIFIER);
601    Result result = metaHTable.get(get);
602    return CatalogFamilyFormat.getTableState(result);
603  }
604
605  /**
606   * Fetch table states from META table
607   * @param conn connection to use
608   * @return map {tableName -&gt; state}
609   */
610  public static Map<TableName, TableState> getTableStates(Connection conn) throws IOException {
611    final Map<TableName, TableState> states = new LinkedHashMap<>();
612    ClientMetaTableAccessor.Visitor collector = r -> {
613      TableState state = CatalogFamilyFormat.getTableState(r);
614      if (state != null) {
615        states.put(state.getTableName(), state);
616      }
617      return true;
618    };
619    fullScanTables(conn, collector);
620    return states;
621  }
622
623  /**
624   * Updates state in META Do not use. For internal use only.
625   * @param conn      connection to use
626   * @param tableName table to look for
627   */
628  public static void updateTableState(Connection conn, TableName tableName, TableState.State actual)
629    throws IOException {
630    updateTableState(conn, new TableState(tableName, actual));
631  }
632
633  ////////////////////////
634  // Editing operations //
635  ////////////////////////
636
637  /**
638   * Generates and returns a {@link Put} containing the {@link RegionInfo} for the catalog table.
639   * @throws IllegalArgumentException when the provided RegionInfo is not the default replica.
640   */
641  public static Put makePutFromRegionInfo(RegionInfo regionInfo) throws IOException {
642    return makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime());
643  }
644
645  /**
646   * Generates and returns a {@link Put} containing the {@link RegionInfo} for the catalog table.
647   * @throws IllegalArgumentException when the provided RegionInfo is not the default replica.
648   */
649  public static Put makePutFromRegionInfo(RegionInfo regionInfo, long ts) throws IOException {
650    byte[] metaKeyForRegion = CatalogFamilyFormat.getMetaKeyForRegion(regionInfo);
651    try {
652      Put put = new Put(metaKeyForRegion, ts);
653      return addRegionInfo(put, regionInfo);
654    } catch (IllegalArgumentException ex) {
655      LOG.error(
656        "Got exception while creating put for regioninfo {}." + "meta key for regioninfo is {}",
657        regionInfo.getRegionNameAsString(), metaKeyForRegion);
658      throw ex;
659    }
660  }
661
662  /**
663   * Generates and returns a Delete containing the region info for the catalog table
664   */
665  public static Delete makeDeleteFromRegionInfo(RegionInfo regionInfo, long ts) {
666    if (regionInfo == null) {
667      throw new IllegalArgumentException("Can't make a delete for null region");
668    }
669    if (regionInfo.getReplicaId() != RegionInfo.DEFAULT_REPLICA_ID) {
670      throw new IllegalArgumentException(
671        "Can't make delete for a replica region. Operate on the primary");
672    }
673    Delete delete = new Delete(CatalogFamilyFormat.getMetaKeyForRegion(regionInfo));
674    delete.addFamily(HConstants.CATALOG_FAMILY, ts);
675    return delete;
676  }
677
678  /**
679   * Adds split daughters to the Put
680   */
681  public static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB)
682    throws IOException {
683    if (splitA != null) {
684      put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow())
685        .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.SPLITA_QUALIFIER)
686        .setTimestamp(put.getTimestamp()).setType(Type.Put).setValue(RegionInfo.toByteArray(splitA))
687        .build());
688    }
689    if (splitB != null) {
690      put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow())
691        .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.SPLITB_QUALIFIER)
692        .setTimestamp(put.getTimestamp()).setType(Type.Put).setValue(RegionInfo.toByteArray(splitB))
693        .build());
694    }
695    return put;
696  }
697
698  /**
699   * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
700   * @param connection connection we're using
701   * @param p          Put to add to hbase:meta
702   */
703  private static void putToMetaTable(Connection connection, Put p) throws IOException {
704    try (Table table = getMetaHTable(connection)) {
705      put(table, p);
706    }
707  }
708
709  /**
710   * @param t Table to use
711   * @param p put to make
712   */
713  private static void put(Table t, Put p) throws IOException {
714    debugLogMutation(p);
715    t.put(p);
716  }
717
718  /**
719   * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
720   * @param connection connection we're using
721   * @param ps         Put to add to hbase:meta
722   */
723  public static void putsToMetaTable(final Connection connection, final List<Put> ps)
724    throws IOException {
725    if (ps.isEmpty()) {
726      return;
727    }
728    try (Table t = getMetaHTable(connection)) {
729      debugLogMutations(ps);
730      // the implementation for putting a single Put is much simpler so here we do a check first.
731      if (ps.size() == 1) {
732        t.put(ps.get(0));
733      } else {
734        t.put(ps);
735      }
736    }
737  }
738
739  /**
740   * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
741   * @param connection connection we're using
742   * @param d          Delete to add to hbase:meta
743   */
744  private static void deleteFromMetaTable(final Connection connection, final Delete d)
745    throws IOException {
746    List<Delete> dels = new ArrayList<>(1);
747    dels.add(d);
748    deleteFromMetaTable(connection, dels);
749  }
750
751  /**
752   * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
753   * @param connection connection we're using
754   * @param deletes    Deletes to add to hbase:meta This list should support #remove.
755   */
756  private static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes)
757    throws IOException {
758    try (Table t = getMetaHTable(connection)) {
759      debugLogMutations(deletes);
760      t.delete(deletes);
761    }
762  }
763
764  /**
765   * Set the column value corresponding to this {@code replicaId}'s {@link RegionState} to the
766   * provided {@code state}. Mutates the provided {@link Put}.
767   */
768  public static Put addRegionStateToPut(Put put, int replicaId, RegionState.State state)
769    throws IOException {
770    put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(put.getRow())
771      .setFamily(HConstants.CATALOG_FAMILY)
772      .setQualifier(CatalogFamilyFormat.getRegionStateColumn(replicaId))
773      .setTimestamp(put.getTimestamp()).setType(Cell.Type.Put).setValue(Bytes.toBytes(state.name()))
774      .build());
775    return put;
776  }
777
778  /**
779   * Update state column in hbase:meta.
780   */
781  public static void updateRegionState(Connection connection, RegionInfo ri,
782    RegionState.State state) throws IOException {
783    final Put put = makePutFromRegionInfo(ri);
784    addRegionStateToPut(put, ri.getReplicaId(), state);
785    putsToMetaTable(connection, Collections.singletonList(put));
786  }
787
788  /**
789   * Adds daughter region infos to hbase:meta row for the specified region.
790   * <p/>
791   * Note that this does not add its daughter's as different rows, but adds information about the
792   * daughters in the same row as the parent. Now only used in snapshot. Use
793   * {@link org.apache.hadoop.hbase.master.assignment.RegionStateStore} if you want to split a
794   * region.
795   * @param connection connection we're using
796   * @param regionInfo RegionInfo of parent region
797   * @param splitA     first split daughter of the parent regionInfo
798   * @param splitB     second split daughter of the parent regionInfo
799   * @throws IOException if problem connecting or updating meta
800   */
801  public static void addSplitsToParent(Connection connection, RegionInfo regionInfo,
802    RegionInfo splitA, RegionInfo splitB) throws IOException {
803    try (Table meta = getMetaHTable(connection)) {
804      Put put = makePutFromRegionInfo(regionInfo);
805      addDaughtersToPut(put, splitA, splitB);
806      meta.put(put);
807      debugLogMutation(put);
808      LOG.debug("Added region {}", regionInfo.getRegionNameAsString());
809    }
810  }
811
812  /**
813   * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is
814   * CLOSED.
815   * @param connection  connection we're using
816   * @param regionInfos region information list
817   * @throws IOException if problem connecting or updating meta
818   */
819  public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
820    int regionReplication) throws IOException {
821    addRegionsToMeta(connection, regionInfos, regionReplication,
822      EnvironmentEdgeManager.currentTime());
823  }
824
825  /**
826   * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions is
827   * CLOSED.
828   * @param connection  connection we're using
829   * @param regionInfos region information list
830   * @param ts          desired timestamp
831   * @throws IOException if problem connecting or updating meta
832   */
833  public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
834    int regionReplication, long ts) throws IOException {
835    List<Put> puts = new ArrayList<>();
836    for (RegionInfo regionInfo : regionInfos) {
837      if (!RegionReplicaUtil.isDefaultReplica(regionInfo)) {
838        continue;
839      }
840      Put put = makePutFromRegionInfo(regionInfo, ts);
841      // New regions are added with initial state of CLOSED.
842      addRegionStateToPut(put, regionInfo.getReplicaId(), RegionState.State.CLOSED);
843      // Add empty locations for region replicas so that number of replicas can be cached
844      // whenever the primary region is looked up from meta
845      for (int i = 1; i < regionReplication; i++) {
846        addEmptyLocation(put, i);
847      }
848      puts.add(put);
849    }
850    putsToMetaTable(connection, puts);
851    LOG.info("Added {} regions to meta.", puts.size());
852  }
853
854  /**
855   * Update state of the table in meta.
856   * @param connection what we use for update
857   * @param state      new state
858   */
859  private static void updateTableState(Connection connection, TableState state) throws IOException {
860    Put put = makePutFromTableState(state, EnvironmentEdgeManager.currentTime());
861    putToMetaTable(connection, put);
862    LOG.info("Updated {} in hbase:meta", state);
863  }
864
865  /**
866   * Construct PUT for given state
867   * @param state new state
868   */
869  public static Put makePutFromTableState(TableState state, long ts) {
870    Put put = new Put(state.getTableName().getName(), ts);
871    put.addColumn(HConstants.TABLE_FAMILY, HConstants.TABLE_STATE_QUALIFIER,
872      state.convert().toByteArray());
873    return put;
874  }
875
876  /**
877   * Remove state for table from meta
878   * @param connection to use for deletion
879   * @param table      to delete state for
880   */
881  public static void deleteTableState(Connection connection, TableName table) throws IOException {
882    long time = EnvironmentEdgeManager.currentTime();
883    Delete delete = new Delete(table.getName());
884    delete.addColumns(HConstants.TABLE_FAMILY, HConstants.TABLE_STATE_QUALIFIER, time);
885    deleteFromMetaTable(connection, delete);
886    LOG.info("Deleted table " + table + " state from META");
887  }
888
889  /**
890   * Updates the location of the specified region in hbase:meta to be the specified server hostname
891   * and startcode.
892   * <p>
893   * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes
894   * edits to that region.
895   * @param connection       connection we're using
896   * @param regionInfo       region to update location of
897   * @param openSeqNum       the latest sequence number obtained when the region was open
898   * @param sn               Server name
899   * @param masterSystemTime wall clock time from master if passed in the open region RPC
900   */
901  public static void updateRegionLocation(Connection connection, RegionInfo regionInfo,
902    ServerName sn, long openSeqNum, long masterSystemTime) throws IOException {
903    updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime);
904  }
905
906  /**
907   * Updates the location of the specified region to be the specified server.
908   * <p>
909   * Connects to the specified server which should be hosting the specified catalog region name to
910   * perform the edit.
911   * @param connection       connection we're using
912   * @param regionInfo       region to update location of
913   * @param sn               Server name
914   * @param openSeqNum       the latest sequence number obtained when the region was open
915   * @param masterSystemTime wall clock time from master if passed in the open region RPC
916   * @throws IOException In particular could throw {@link java.net.ConnectException} if the server
917   *                     is down on other end.
918   */
919  private static void updateLocation(Connection connection, RegionInfo regionInfo, ServerName sn,
920    long openSeqNum, long masterSystemTime) throws IOException {
921    // region replicas are kept in the primary region's row
922    Put put = new Put(CatalogFamilyFormat.getMetaKeyForRegion(regionInfo), masterSystemTime);
923    addRegionInfo(put, regionInfo);
924    addLocation(put, sn, openSeqNum, regionInfo.getReplicaId());
925    putToMetaTable(connection, put);
926    LOG.info("Updated row {} with server = {}", regionInfo.getRegionNameAsString(), sn);
927  }
928
929  public static Put addRegionInfo(final Put p, final RegionInfo hri) throws IOException {
930    p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).setRow(p.getRow())
931      .setFamily(HConstants.CATALOG_FAMILY).setQualifier(HConstants.REGIONINFO_QUALIFIER)
932      .setTimestamp(p.getTimestamp()).setType(Type.Put)
933      // Serialize the Default Replica HRI otherwise scan of hbase:meta
934      // shows an info:regioninfo value with encoded name and region
935      // name that differs from that of the hbase;meta row.
936      .setValue(RegionInfo.toByteArray(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri)))
937      .build());
938    return p;
939  }
940
941  public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId)
942    throws IOException {
943    CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
944    return p
945      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
946        .setQualifier(CatalogFamilyFormat.getServerColumn(replicaId)).setTimestamp(p.getTimestamp())
947        .setType(Cell.Type.Put).setValue(Bytes.toBytes(sn.getAddress().toString())).build())
948      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
949        .setQualifier(CatalogFamilyFormat.getStartCodeColumn(replicaId))
950        .setTimestamp(p.getTimestamp()).setType(Cell.Type.Put)
951        .setValue(Bytes.toBytes(sn.getStartCode())).build())
952      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
953        .setQualifier(CatalogFamilyFormat.getSeqNumColumn(replicaId)).setTimestamp(p.getTimestamp())
954        .setType(Type.Put).setValue(Bytes.toBytes(openSeqNum)).build());
955  }
956
957  public static Put addEmptyLocation(Put p, int replicaId) throws IOException {
958    CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
959    return p
960      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
961        .setQualifier(CatalogFamilyFormat.getServerColumn(replicaId)).setTimestamp(p.getTimestamp())
962        .setType(Type.Put).build())
963      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
964        .setQualifier(CatalogFamilyFormat.getStartCodeColumn(replicaId))
965        .setTimestamp(p.getTimestamp()).setType(Cell.Type.Put).build())
966      .add(builder.clear().setRow(p.getRow()).setFamily(HConstants.CATALOG_FAMILY)
967        .setQualifier(CatalogFamilyFormat.getSeqNumColumn(replicaId)).setTimestamp(p.getTimestamp())
968        .setType(Cell.Type.Put).build());
969  }
970
971  private static void debugLogMutations(List<? extends Mutation> mutations) throws IOException {
972    if (!METALOG.isDebugEnabled()) {
973      return;
974    }
975    // Logging each mutation in separate line makes it easier to see diff between them visually
976    // because of common starting indentation.
977    for (Mutation mutation : mutations) {
978      debugLogMutation(mutation);
979    }
980  }
981
982  private static void debugLogMutation(Mutation p) throws IOException {
983    METALOG.debug("{} {}", p.getClass().getSimpleName(), p.toJSON());
984  }
985}