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.Arrays;
026import java.util.Collection;
027import java.util.Collections;
028import java.util.LinkedHashMap;
029import java.util.List;
030import java.util.Map;
031import java.util.NavigableMap;
032import java.util.Set;
033import java.util.SortedMap;
034import java.util.TreeMap;
035import java.util.regex.Matcher;
036import java.util.regex.Pattern;
037import java.util.stream.Collectors;
038
039import org.apache.hadoop.conf.Configuration;
040import org.apache.hadoop.hbase.Cell.Type;
041import org.apache.hadoop.hbase.client.Connection;
042import org.apache.hadoop.hbase.client.ConnectionFactory;
043import org.apache.hadoop.hbase.client.Consistency;
044import org.apache.hadoop.hbase.client.Delete;
045import org.apache.hadoop.hbase.client.Get;
046import org.apache.hadoop.hbase.client.Mutation;
047import org.apache.hadoop.hbase.client.Put;
048import org.apache.hadoop.hbase.client.RegionInfo;
049import org.apache.hadoop.hbase.client.RegionInfoBuilder;
050import org.apache.hadoop.hbase.client.RegionLocator;
051import org.apache.hadoop.hbase.client.RegionReplicaUtil;
052import org.apache.hadoop.hbase.client.RegionServerCallable;
053import org.apache.hadoop.hbase.client.Result;
054import org.apache.hadoop.hbase.client.ResultScanner;
055import org.apache.hadoop.hbase.client.Scan;
056import org.apache.hadoop.hbase.client.Table;
057import org.apache.hadoop.hbase.client.TableState;
058import org.apache.hadoop.hbase.exceptions.DeserializationException;
059import org.apache.hadoop.hbase.filter.QualifierFilter;
060import org.apache.hadoop.hbase.filter.RegexStringComparator;
061import org.apache.hadoop.hbase.filter.RowFilter;
062import org.apache.hadoop.hbase.filter.SubstringComparator;
063import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
064import org.apache.hadoop.hbase.master.RegionState;
065import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
066import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
067import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier;
068import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
069import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos;
070import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
071import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsResponse;
072import org.apache.hadoop.hbase.util.Bytes;
073import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
074import org.apache.hadoop.hbase.util.ExceptionUtil;
075import org.apache.hadoop.hbase.util.Pair;
076import org.apache.hadoop.hbase.util.PairOfSameType;
077import org.apache.yetus.audience.InterfaceAudience;
078import org.slf4j.Logger;
079import org.slf4j.LoggerFactory;
080
081import org.apache.hbase.thirdparty.com.google.common.annotations.VisibleForTesting;
082
083/**
084 * <p>
085 * Read/write operations on <code>hbase:meta</code> region as well as assignment information stored
086 * to <code>hbase:meta</code>.
087 * </p>
088 * <p>
089 * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason for this is
090 * when this class is used on client-side (e.g. HBaseAdmin), we want to use short-lived connection
091 * (opened before each operation, closed right after), while when used on HM or HRS (like in
092 * AssignmentManager) we want permanent connection.
093 * </p>
094 * <p>
095 * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the same table
096 * range (table, startKey, endKey). For every range, there will be at least one HRI defined which is
097 * called default replica.
098 * </p>
099 * <p>
100 * <h2>Meta layout</h2>
101 *
102 * <pre>
103 * For each table there is single row named for the table with a 'table' column family.
104 * The column family currently has one column in it, the 'state' column:
105 *
106 * table:state             => contains table state
107 *
108 * Then for each table range ('Region'), there is a single row, formatted as:
109 * &lt;tableName&gt;,&lt;startKey&gt;,&lt;regionId&gt;,&lt;encodedRegionName&gt;.
110 * This row is the serialized regionName of the default region replica.
111 * Columns are:
112 * info:regioninfo         => contains serialized HRI for the default region replica
113 * info:server             => contains hostname:port (in string form) for the server hosting
114 *                            the default regionInfo replica
115 * info:server_&lt;replicaId&gt => contains hostname:port (in string form) for the server hosting
116 *                                 the regionInfo replica with replicaId
117 * info:serverstartcode    => contains server start code (in binary long form) for the server
118 *                            hosting the default regionInfo replica
119 * info:serverstartcode_&lt;replicaId&gt => contains server start code (in binary long form) for
120 *                                          the server hosting the regionInfo replica with
121 *                                          replicaId
122 * info:seqnumDuringOpen   => contains seqNum (in binary long form) for the region at the time
123 *                             the server opened the region with default replicaId
124 * info:seqnumDuringOpen_&lt;replicaId&gt => contains seqNum (in binary long form) for the region
125 *                                           at the time the server opened the region with
126 *                                           replicaId
127 * info:splitA             => contains a serialized HRI for the first daughter region if the
128 *                             region is split
129 * info:splitB             => contains a serialized HRI for the second daughter region if the
130 *                             region is split
131 * info:merge*             => contains a serialized HRI for a merge parent region. There will be two
132 *                             or more of these columns in a row. A row that has these columns is
133 *                             undergoing a merge and is the result of the merge. Columns listed
134 *                             in marge* columns are the parents of this merged region. Example
135 *                             columns: info:merge0001, info:merge0002. You make also see 'mergeA',
136 *                             and 'mergeB'. This is old form replaced by the new format that allows
137 *                             for more than two parents to be merged at a time.
138 * TODO: Add rep_barrier for serial replication explaination.
139 * </pre>
140 * </p>
141 * <p>
142 * The actual layout of meta should be encapsulated inside MetaTableAccessor methods, and should not
143 * leak out of it (through Result objects, etc)
144 * </p>
145 */
146@InterfaceAudience.Private
147public class MetaTableAccessor {
148
149  private static final Logger LOG = LoggerFactory.getLogger(MetaTableAccessor.class);
150  private static final Logger METALOG = LoggerFactory.getLogger("org.apache.hadoop.hbase.META");
151
152  /**
153   * Lists all of the table regions currently in META.
154   * Deprecated, keep there until some test use this.
155   * @param connection what we will use
156   * @param tableName table to list
157   * @return Map of all user-space regions to servers
158   * @deprecated use {@link #getTableRegionsAndLocations}, region can have multiple locations
159   */
160  @Deprecated
161  public static NavigableMap<RegionInfo, ServerName> allTableRegions(
162      Connection connection, final TableName tableName) throws IOException {
163    final NavigableMap<RegionInfo, ServerName> regions = new TreeMap<>();
164    Visitor visitor = new TableVisitorBase(tableName) {
165      @Override
166      public boolean visitInternal(Result result) throws IOException {
167        RegionLocations locations = getRegionLocations(result);
168        if (locations == null) return true;
169        for (HRegionLocation loc : locations.getRegionLocations()) {
170          if (loc != null) {
171            RegionInfo regionInfo = loc.getRegionInfo();
172            regions.put(regionInfo, loc.getServerName());
173          }
174        }
175        return true;
176      }
177    };
178    scanMetaForTableRegions(connection, visitor, tableName);
179    return regions;
180  }
181  @VisibleForTesting
182  public static final byte[] REPLICATION_PARENT_QUALIFIER = Bytes.toBytes("parent");
183
184  private static final byte ESCAPE_BYTE = (byte) 0xFF;
185
186  private static final byte SEPARATED_BYTE = 0x00;
187
188  @InterfaceAudience.Private
189  public enum QueryType {
190    ALL(HConstants.TABLE_FAMILY, HConstants.CATALOG_FAMILY),
191    REGION(HConstants.CATALOG_FAMILY),
192    TABLE(HConstants.TABLE_FAMILY);
193
194    private final byte[][] families;
195
196    QueryType(byte[]... families) {
197      this.families = families;
198    }
199
200    byte[][] getFamilies() {
201      return this.families;
202    }
203  }
204
205  /** The delimiter for meta columns for replicaIds &gt; 0 */
206  static final char META_REPLICA_ID_DELIMITER = '_';
207
208  /** A regex for parsing server columns from meta. See above javadoc for meta layout */
209  private static final Pattern SERVER_COLUMN_PATTERN
210    = Pattern.compile("^server(_[0-9a-fA-F]{4})?$");
211
212  ////////////////////////
213  // Reading operations //
214  ////////////////////////
215
216  /**
217   * Performs a full scan of <code>hbase:meta</code> for regions.
218   * @param connection connection we're using
219   * @param visitor Visitor invoked against each row in regions family.
220   */
221  public static void fullScanRegions(Connection connection, final Visitor visitor)
222      throws IOException {
223    scanMeta(connection, null, null, QueryType.REGION, visitor);
224  }
225
226  /**
227   * Performs a full scan of <code>hbase:meta</code> for regions.
228   * @param connection connection we're using
229   */
230  public static List<Result> fullScanRegions(Connection connection) throws IOException {
231    return fullScan(connection, QueryType.REGION);
232  }
233
234  /**
235   * Performs a full scan of <code>hbase:meta</code> for tables.
236   * @param connection connection we're using
237   * @param visitor Visitor invoked against each row in tables family.
238   */
239  public static void fullScanTables(Connection connection, final Visitor visitor)
240      throws IOException {
241    scanMeta(connection, null, null, QueryType.TABLE, visitor);
242  }
243
244  /**
245   * Performs a full scan of <code>hbase:meta</code>.
246   * @param connection connection we're using
247   * @param type scanned part of meta
248   * @return List of {@link Result}
249   */
250  private static List<Result> fullScan(Connection connection, QueryType type) throws IOException {
251    CollectAllVisitor v = new CollectAllVisitor();
252    scanMeta(connection, null, null, type, v);
253    return v.getResults();
254  }
255
256  /**
257   * Callers should call close on the returned {@link Table} instance.
258   * @param connection connection we're using to access Meta
259   * @return An {@link Table} for <code>hbase:meta</code>
260   */
261  public static Table getMetaHTable(final Connection connection)
262  throws IOException {
263    // We used to pass whole CatalogTracker in here, now we just pass in Connection
264    if (connection == null) {
265      throw new NullPointerException("No connection");
266    } else if (connection.isClosed()) {
267      throw new IOException("connection is closed");
268    }
269    return connection.getTable(TableName.META_TABLE_NAME);
270  }
271
272  /**
273   * @param t Table to use (will be closed when done).
274   * @param g Get to run
275   */
276  private static Result get(final Table t, final Get g) throws IOException {
277    if (t == null) return null;
278    try {
279      return t.get(g);
280    } finally {
281      t.close();
282    }
283  }
284
285  /**
286   * Gets the region info and assignment for the specified region.
287   * @param connection connection we're using
288   * @param regionName Region to lookup.
289   * @return Location and RegionInfo for <code>regionName</code>
290   * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
291   */
292  @Deprecated
293  public static Pair<RegionInfo, ServerName> getRegion(Connection connection, byte [] regionName)
294    throws IOException {
295    HRegionLocation location = getRegionLocation(connection, regionName);
296    return location == null
297      ? null
298      : new Pair<>(location.getRegionInfo(), location.getServerName());
299  }
300
301  /**
302   * Returns the HRegionLocation from meta for the given region
303   * @param connection connection we're using
304   * @param regionName region we're looking for
305   * @return HRegionLocation for the given region
306   */
307  public static HRegionLocation getRegionLocation(Connection connection, byte[] regionName)
308      throws IOException {
309    byte[] row = regionName;
310    RegionInfo parsedInfo = null;
311    try {
312      parsedInfo = parseRegionInfoFromRegionName(regionName);
313      row = getMetaKeyForRegion(parsedInfo);
314    } catch (Exception parseEx) {
315      // Ignore. This is used with tableName passed as regionName.
316    }
317    Get get = new Get(row);
318    get.addFamily(HConstants.CATALOG_FAMILY);
319    Result r = get(getMetaHTable(connection), get);
320    RegionLocations locations = getRegionLocations(r);
321    return locations == null ? null
322      : locations.getRegionLocation(parsedInfo == null ? 0 : parsedInfo.getReplicaId());
323  }
324
325  /**
326   * Returns the HRegionLocation from meta for the given region
327   * @param connection connection we're using
328   * @param regionInfo region information
329   * @return HRegionLocation for the given region
330   */
331  public static HRegionLocation getRegionLocation(Connection connection, RegionInfo regionInfo)
332      throws IOException {
333    byte[] row = getMetaKeyForRegion(regionInfo);
334    Get get = new Get(row);
335    get.addFamily(HConstants.CATALOG_FAMILY);
336    Result r = get(getMetaHTable(connection), get);
337    return getRegionLocation(r, regionInfo, regionInfo.getReplicaId());
338  }
339
340  /** Returns the row key to use for this regionInfo */
341  public static byte[] getMetaKeyForRegion(RegionInfo regionInfo) {
342    return RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo).getRegionName();
343  }
344
345  /** Returns an HRI parsed from this regionName. Not all the fields of the HRI
346   * is stored in the name, so the returned object should only be used for the fields
347   * in the regionName.
348   */
349  public static RegionInfo parseRegionInfoFromRegionName(byte[] regionName) throws IOException {
350    byte[][] fields = RegionInfo.parseRegionName(regionName);
351    long regionId = Long.parseLong(Bytes.toString(fields[2]));
352    int replicaId = fields.length > 3 ? Integer.parseInt(Bytes.toString(fields[3]), 16) : 0;
353    return RegionInfoBuilder.newBuilder(TableName.valueOf(fields[0]))
354              .setStartKey(fields[1])
355              .setEndKey(fields[2])
356              .setSplit(false)
357              .setRegionId(regionId)
358              .setReplicaId(replicaId)
359              .build();
360  }
361
362  /**
363   * Gets the result in hbase:meta for the specified region.
364   * @param connection connection we're using
365   * @param regionName region we're looking for
366   * @return result of the specified region
367   */
368  public static Result getRegionResult(Connection connection,
369      byte[] regionName) throws IOException {
370    Get get = new Get(regionName);
371    get.addFamily(HConstants.CATALOG_FAMILY);
372    return get(getMetaHTable(connection), get);
373  }
374
375  /**
376   * Scans META table for a row whose key contains the specified <B>regionEncodedName</B>,
377   * returning a single related <code>Result</code> instance if any row is found, null otherwise.
378   *
379   * @param connection the connection to query META table.
380   * @param regionEncodedName the region encoded name to look for at META.
381   * @return <code>Result</code> instance with the row related info in META, null otherwise.
382   * @throws IOException if any errors occur while querying META.
383   */
384  public static Result scanByRegionEncodedName(Connection connection,
385      String regionEncodedName) throws IOException {
386    RowFilter rowFilter = new RowFilter(CompareOperator.EQUAL,
387      new SubstringComparator(regionEncodedName));
388    Scan scan = getMetaScan(connection, 1);
389    scan.setFilter(rowFilter);
390    ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan);
391    return resultScanner.next();
392  }
393
394  /**
395   * @return Return all regioninfos listed in the 'info:merge*' columns of
396   *   the <code>regionName</code> row.
397   */
398  @Nullable
399  public static List<RegionInfo> getMergeRegions(Connection connection, byte[] regionName)
400      throws IOException {
401    return getMergeRegions(getMergeRegionsRaw(connection, regionName));
402  }
403
404  /**
405   * @return Deserialized regioninfo values taken from column values that match
406   *   the regex 'info:merge.*' in array of <code>cells</code>.
407   */
408  @Nullable
409  public static List<RegionInfo> getMergeRegions(Cell [] cells) {
410    if (cells == null) {
411      return null;
412    }
413    List<RegionInfo> regionsToMerge = null;
414    for (Cell cell: cells) {
415      if (!isMergeQualifierPrefix(cell)) {
416        continue;
417      }
418      // Ok. This cell is that of a info:merge* column.
419      RegionInfo ri = RegionInfo.parseFromOrNull(cell.getValueArray(), cell.getValueOffset(),
420         cell.getValueLength());
421      if (ri != null) {
422        if (regionsToMerge == null) {
423          regionsToMerge = new ArrayList<>();
424        }
425        regionsToMerge.add(ri);
426      }
427    }
428    return regionsToMerge;
429  }
430
431  /**
432   * @return True if any merge regions present in <code>cells</code>; i.e.
433   *   the column in <code>cell</code> matches the regex 'info:merge.*'.
434   */
435  public static boolean hasMergeRegions(Cell [] cells) {
436    for (Cell cell: cells) {
437      if (!isMergeQualifierPrefix(cell)) {
438        continue;
439      }
440      return true;
441    }
442    return false;
443  }
444
445  /**
446   * @return True if the column in <code>cell</code> matches the regex 'info:merge.*'.
447   */
448  private static boolean isMergeQualifierPrefix(Cell cell) {
449    // Check to see if has family and that qualifier starts with the merge qualifier 'merge'
450    return CellUtil.matchingFamily(cell, HConstants.CATALOG_FAMILY) &&
451      PrivateCellUtil.qualifierStartsWith(cell, HConstants.MERGE_QUALIFIER_PREFIX);
452  }
453
454  /**
455   * @return Array of Cells made from all columns on the <code>regionName</code> row
456   *   that match the regex 'info:merge.*'.
457   */
458  @Nullable
459  private static Cell [] getMergeRegionsRaw(Connection connection, byte [] regionName)
460      throws IOException {
461    Scan scan = new Scan().withStartRow(regionName).
462        setOneRowLimit().
463        readVersions(1).
464        addFamily(HConstants.CATALOG_FAMILY).
465        setFilter(new QualifierFilter(CompareOperator.EQUAL,
466          new RegexStringComparator(HConstants.MERGE_QUALIFIER_PREFIX_STR+ ".*")));
467    try (Table m = getMetaHTable(connection); ResultScanner scanner = m.getScanner(scan)) {
468      // Should be only one result in this scanner if any.
469      Result result = scanner.next();
470      if (result == null) {
471        return null;
472      }
473      // Should be safe to just return all Cells found since we had filter in place.
474      // All values should be RegionInfos or something wrong.
475      return result.rawCells();
476    }
477  }
478
479  /**
480   * Checks if the specified table exists.  Looks at the hbase:meta table hosted on
481   * the specified server.
482   * @param connection connection we're using
483   * @param tableName table to check
484   * @return true if the table exists in meta, false if not
485   */
486  public static boolean tableExists(Connection connection,
487      final TableName tableName)
488  throws IOException {
489    // Catalog tables always exist.
490    return tableName.equals(TableName.META_TABLE_NAME) ||
491        getTableState(connection, tableName) != null;
492  }
493
494  /**
495   * Lists all of the regions currently in META.
496   *
497   * @param connection to connect with
498   * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions,
499   *                                    true and we'll leave out offlined regions from returned list
500   * @return List of all user-space regions.
501   */
502  @VisibleForTesting
503  public static List<RegionInfo> getAllRegions(Connection connection,
504      boolean excludeOfflinedSplitParents)
505      throws IOException {
506    List<Pair<RegionInfo, ServerName>> result;
507
508    result = getTableRegionsAndLocations(connection, null,
509        excludeOfflinedSplitParents);
510
511    return getListOfRegionInfos(result);
512
513  }
514
515  /**
516   * Gets all of the regions of the specified table. Do not use this method
517   * to get meta table regions, use methods in MetaTableLocator instead.
518   * @param connection connection we're using
519   * @param tableName table we're looking for
520   * @return Ordered list of {@link RegionInfo}.
521   */
522  public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName)
523  throws IOException {
524    return getTableRegions(connection, tableName, false);
525  }
526
527  /**
528   * Gets all of the regions of the specified table. Do not use this method
529   * to get meta table regions, use methods in MetaTableLocator instead.
530   * @param connection connection we're using
531   * @param tableName table we're looking for
532   * @param excludeOfflinedSplitParents If true, do not include offlined split
533   * parents in the return.
534   * @return Ordered list of {@link RegionInfo}.
535   */
536  public static List<RegionInfo> getTableRegions(Connection connection, TableName tableName,
537      final boolean excludeOfflinedSplitParents) throws IOException {
538    List<Pair<RegionInfo, ServerName>> result =
539      getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents);
540    return getListOfRegionInfos(result);
541  }
542
543  private static List<RegionInfo> getListOfRegionInfos(
544      final List<Pair<RegionInfo, ServerName>> pairs) {
545    if (pairs == null || pairs.isEmpty()) {
546      return Collections.emptyList();
547    }
548    List<RegionInfo> result = new ArrayList<>(pairs.size());
549    for (Pair<RegionInfo, ServerName> pair : pairs) {
550      result.add(pair.getFirst());
551    }
552    return result;
553  }
554
555  /**
556   * @param tableName table we're working with
557   * @return start row for scanning META according to query type
558   */
559  public static byte[] getTableStartRowForMeta(TableName tableName, QueryType type) {
560    if (tableName == null) {
561      return null;
562    }
563    switch (type) {
564    case REGION:
565      byte[] startRow = new byte[tableName.getName().length + 2];
566      System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length);
567      startRow[startRow.length - 2] = HConstants.DELIMITER;
568      startRow[startRow.length - 1] = HConstants.DELIMITER;
569      return startRow;
570    case ALL:
571    case TABLE:
572    default:
573      return tableName.getName();
574    }
575  }
576
577  /**
578   * @param tableName table we're working with
579   * @return stop row for scanning META according to query type
580   */
581  public static byte[] getTableStopRowForMeta(TableName tableName, QueryType type) {
582    if (tableName == null) {
583      return null;
584    }
585    final byte[] stopRow;
586    switch (type) {
587    case REGION:
588      stopRow = new byte[tableName.getName().length + 3];
589      System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
590      stopRow[stopRow.length - 3] = ' ';
591      stopRow[stopRow.length - 2] = HConstants.DELIMITER;
592      stopRow[stopRow.length - 1] = HConstants.DELIMITER;
593      break;
594    case ALL:
595    case TABLE:
596    default:
597      stopRow = new byte[tableName.getName().length + 1];
598      System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
599      stopRow[stopRow.length - 1] = ' ';
600      break;
601    }
602    return stopRow;
603  }
604
605  /**
606   * This method creates a Scan object that will only scan catalog rows that
607   * belong to the specified table. It doesn't specify any columns.
608   * This is a better alternative to just using a start row and scan until
609   * it hits a new table since that requires parsing the HRI to get the table
610   * name.
611   * @param tableName bytes of table's name
612   * @return configured Scan object
613   */
614  @Deprecated
615  public static Scan getScanForTableName(Connection connection, TableName tableName) {
616    // Start key is just the table name with delimiters
617    byte[] startKey = getTableStartRowForMeta(tableName, QueryType.REGION);
618    // Stop key appends the smallest possible char to the table name
619    byte[] stopKey = getTableStopRowForMeta(tableName, QueryType.REGION);
620
621    Scan scan = getMetaScan(connection, -1);
622    scan.setStartRow(startKey);
623    scan.setStopRow(stopKey);
624    return scan;
625  }
626
627  private static Scan getMetaScan(Connection connection, int rowUpperLimit) {
628    Scan scan = new Scan();
629    int scannerCaching = connection.getConfiguration()
630        .getInt(HConstants.HBASE_META_SCANNER_CACHING,
631            HConstants.DEFAULT_HBASE_META_SCANNER_CACHING);
632    if (connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS,
633        HConstants.DEFAULT_USE_META_REPLICAS)) {
634      scan.setConsistency(Consistency.TIMELINE);
635    }
636    if (rowUpperLimit > 0) {
637      scan.setLimit(rowUpperLimit);
638      scan.setReadType(Scan.ReadType.PREAD);
639    }
640    scan.setCaching(scannerCaching);
641    return scan;
642  }
643  /**
644   * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
645   * @param connection connection we're using
646   * @param tableName table we're looking for
647   * @return Return list of regioninfos and server.
648   */
649  public static List<Pair<RegionInfo, ServerName>>
650    getTableRegionsAndLocations(Connection connection, TableName tableName)
651      throws IOException {
652    return getTableRegionsAndLocations(connection, tableName, true);
653  }
654
655  /**
656   * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
657   * @param connection connection we're using
658   * @param tableName table to work with, can be null for getting all regions
659   * @param excludeOfflinedSplitParents don't return split parents
660   * @return Return list of regioninfos and server addresses.
661   */
662  public static List<Pair<RegionInfo, ServerName>> getTableRegionsAndLocations(
663      Connection connection, @Nullable final TableName tableName,
664      final boolean excludeOfflinedSplitParents) throws IOException {
665    if (tableName != null && tableName.equals(TableName.META_TABLE_NAME)) {
666      throw new IOException("This method can't be used to locate meta regions;"
667        + " use MetaTableLocator instead");
668    }
669    // Make a version of CollectingVisitor that collects RegionInfo and ServerAddress
670    CollectingVisitor<Pair<RegionInfo, ServerName>> visitor =
671      new CollectingVisitor<Pair<RegionInfo, ServerName>>() {
672        private RegionLocations current = null;
673
674        @Override
675        public boolean visit(Result r) throws IOException {
676          current = getRegionLocations(r);
677          if (current == null || current.getRegionLocation().getRegion() == null) {
678            LOG.warn("No serialized RegionInfo in " + r);
679            return true;
680          }
681          RegionInfo hri = current.getRegionLocation().getRegion();
682          if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
683          // Else call super and add this Result to the collection.
684          return super.visit(r);
685        }
686
687        @Override
688        void add(Result r) {
689          if (current == null) {
690            return;
691          }
692          for (HRegionLocation loc : current.getRegionLocations()) {
693            if (loc != null) {
694              this.results.add(new Pair<>(loc.getRegion(), loc.getServerName()));
695            }
696          }
697        }
698      };
699    scanMeta(connection,
700        getTableStartRowForMeta(tableName, QueryType.REGION),
701        getTableStopRowForMeta(tableName, QueryType.REGION),
702        QueryType.REGION, visitor);
703    return visitor.getResults();
704  }
705
706  /**
707   * @param connection connection we're using
708   * @param serverName server whose regions we're interested in
709   * @return List of user regions installed on this server (does not include
710   * catalog regions).
711   * @throws IOException
712   */
713  public static NavigableMap<RegionInfo, Result>
714  getServerUserRegions(Connection connection, final ServerName serverName)
715    throws IOException {
716    final NavigableMap<RegionInfo, Result> hris = new TreeMap<>();
717    // Fill the above hris map with entries from hbase:meta that have the passed
718    // servername.
719    CollectingVisitor<Result> v = new CollectingVisitor<Result>() {
720      @Override
721      void add(Result r) {
722        if (r == null || r.isEmpty()) return;
723        RegionLocations locations = getRegionLocations(r);
724        if (locations == null) return;
725        for (HRegionLocation loc : locations.getRegionLocations()) {
726          if (loc != null) {
727            if (loc.getServerName() != null && loc.getServerName().equals(serverName)) {
728              hris.put(loc.getRegion(), r);
729            }
730          }
731        }
732      }
733    };
734    scanMeta(connection, null, null, QueryType.REGION, v);
735    return hris;
736  }
737
738  public static void fullScanMetaAndPrint(Connection connection)
739    throws IOException {
740    Visitor v = r -> {
741      if (r ==  null || r.isEmpty()) {
742        return true;
743      }
744      LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
745      TableState state = getTableState(r);
746      if (state != null) {
747        LOG.info("fullScanMetaAndPrint.Table State={}" + state);
748      } else {
749        RegionLocations locations = getRegionLocations(r);
750        if (locations == null) {
751          return true;
752        }
753        for (HRegionLocation loc : locations.getRegionLocations()) {
754          if (loc != null) {
755            LOG.info("fullScanMetaAndPrint.HRI Print={}", loc.getRegion());
756          }
757        }
758      }
759      return true;
760    };
761    scanMeta(connection, null, null, QueryType.ALL, v);
762  }
763
764  public static void scanMetaForTableRegions(Connection connection,
765      Visitor visitor, TableName tableName) throws IOException {
766    scanMeta(connection, tableName, QueryType.REGION, Integer.MAX_VALUE, visitor);
767  }
768
769  private static void scanMeta(Connection connection, TableName table, QueryType type, int maxRows,
770      final Visitor visitor) throws IOException {
771    scanMeta(connection, getTableStartRowForMeta(table, type), getTableStopRowForMeta(table, type),
772        type, maxRows, visitor);
773  }
774
775  private static void scanMeta(Connection connection, @Nullable final byte[] startRow,
776      @Nullable final byte[] stopRow, QueryType type, final Visitor visitor) throws IOException {
777    scanMeta(connection, startRow, stopRow, type, Integer.MAX_VALUE, visitor);
778  }
779
780  /**
781   * Performs a scan of META table for given table starting from given row.
782   * @param connection connection we're using
783   * @param visitor    visitor to call
784   * @param tableName  table withing we scan
785   * @param row        start scan from this row
786   * @param rowLimit   max number of rows to return
787   * @throws IOException
788   */
789  public static void scanMeta(Connection connection,
790      final Visitor visitor, final TableName tableName,
791      final byte[] row, final int rowLimit)
792      throws IOException {
793
794    byte[] startRow = null;
795    byte[] stopRow = null;
796    if (tableName != null) {
797      startRow =
798          getTableStartRowForMeta(tableName, QueryType.REGION);
799      if (row != null) {
800        RegionInfo closestRi =
801            getClosestRegionInfo(connection, tableName, row);
802        startRow = RegionInfo
803            .createRegionName(tableName, closestRi.getStartKey(), HConstants.ZEROES, false);
804      }
805      stopRow =
806          getTableStopRowForMeta(tableName, QueryType.REGION);
807    }
808    scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor);
809  }
810
811  /**
812   * Performs a scan of META table.
813   * @param connection connection we're using
814   * @param startRow Where to start the scan. Pass null if want to begin scan
815   *                 at first row.
816   * @param stopRow Where to stop the scan. Pass null if want to scan all rows
817   *                from the start one
818   * @param type scanned part of meta
819   * @param maxRows maximum rows to return
820   * @param visitor Visitor invoked against each row.
821   * @throws IOException
822   */
823  static void scanMeta(Connection connection, @Nullable final byte[] startRow,
824        @Nullable final byte[] stopRow, QueryType type, int maxRows, final Visitor visitor)
825      throws IOException {
826    int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE;
827    Scan scan = getMetaScan(connection, rowUpperLimit);
828
829    for (byte[] family : type.getFamilies()) {
830      scan.addFamily(family);
831    }
832    if (startRow != null) {
833      scan.withStartRow(startRow);
834    }
835    if (stopRow != null) {
836      scan.withStopRow(stopRow);
837    }
838
839    if (LOG.isTraceEnabled()) {
840      LOG.trace("Scanning META"
841          + " starting at row=" + Bytes.toStringBinary(startRow)
842          + " stopping at row=" + Bytes.toStringBinary(stopRow)
843          + " for max=" + rowUpperLimit
844          + " with caching=" + scan.getCaching());
845    }
846
847    int currentRow = 0;
848    try (Table metaTable = getMetaHTable(connection)) {
849      try (ResultScanner scanner = metaTable.getScanner(scan)) {
850        Result data;
851        while ((data = scanner.next()) != null) {
852          if (data.isEmpty()) continue;
853          // Break if visit returns false.
854          if (!visitor.visit(data)) break;
855          if (++currentRow >= rowUpperLimit) break;
856        }
857      }
858    }
859    if (visitor instanceof Closeable) {
860      try {
861        ((Closeable) visitor).close();
862      } catch (Throwable t) {
863        ExceptionUtil.rethrowIfInterrupt(t);
864        LOG.debug("Got exception in closing the meta scanner visitor", t);
865      }
866    }
867  }
868
869  /**
870   * @return Get closest metatable region row to passed <code>row</code>
871   */
872  @NonNull
873  private static RegionInfo getClosestRegionInfo(Connection connection,
874      @NonNull final TableName tableName, @NonNull final byte[] row) throws IOException {
875    byte[] searchRow = RegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
876    Scan scan = getMetaScan(connection, 1);
877    scan.setReversed(true);
878    scan.withStartRow(searchRow);
879    try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) {
880      Result result = resultScanner.next();
881      if (result == null) {
882        throw new TableNotFoundException("Cannot find row in META " +
883            " for table: " + tableName + ", row=" + Bytes.toStringBinary(row));
884      }
885      RegionInfo regionInfo = getRegionInfo(result);
886      if (regionInfo == null) {
887        throw new IOException("RegionInfo was null or empty in Meta for " +
888            tableName + ", row=" + Bytes.toStringBinary(row));
889      }
890      return regionInfo;
891    }
892  }
893
894  /**
895   * Returns the column family used for meta columns.
896   * @return HConstants.CATALOG_FAMILY.
897   */
898  public static byte[] getCatalogFamily() {
899    return HConstants.CATALOG_FAMILY;
900  }
901
902  /**
903   * Returns the column family used for table columns.
904   * @return HConstants.TABLE_FAMILY.
905   */
906  private static byte[] getTableFamily() {
907    return HConstants.TABLE_FAMILY;
908  }
909
910  /**
911   * Returns the column qualifier for serialized region info
912   * @return HConstants.REGIONINFO_QUALIFIER
913   */
914  public static byte[] getRegionInfoColumn() {
915    return HConstants.REGIONINFO_QUALIFIER;
916  }
917
918  /**
919   * Returns the column qualifier for serialized table state
920   * @return HConstants.TABLE_STATE_QUALIFIER
921   */
922  private static byte[] getTableStateColumn() {
923    return HConstants.TABLE_STATE_QUALIFIER;
924  }
925
926  /**
927   * Returns the column qualifier for serialized region state
928   * @return HConstants.TABLE_STATE_QUALIFIER
929   */
930  private static byte[] getRegionStateColumn() {
931    return HConstants.STATE_QUALIFIER;
932  }
933
934  /**
935   * Returns the column qualifier for serialized region state
936   * @param replicaId the replicaId of the region
937   * @return a byte[] for state qualifier
938   */
939  @VisibleForTesting
940  static byte[] getRegionStateColumn(int replicaId) {
941    return replicaId == 0 ? HConstants.STATE_QUALIFIER
942        : Bytes.toBytes(HConstants.STATE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
943            + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
944  }
945
946  /**
947   * Returns the column qualifier for serialized region state
948   * @param replicaId the replicaId of the region
949   * @return a byte[] for sn column qualifier
950   */
951  @VisibleForTesting
952  static byte[] getServerNameColumn(int replicaId) {
953    return replicaId == 0 ? HConstants.SERVERNAME_QUALIFIER
954        : Bytes.toBytes(HConstants.SERVERNAME_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
955            + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
956  }
957
958  /**
959   * Returns the column qualifier for server column for replicaId
960   * @param replicaId the replicaId of the region
961   * @return a byte[] for server column qualifier
962   */
963  @VisibleForTesting
964  public static byte[] getServerColumn(int replicaId) {
965    return replicaId == 0
966      ? HConstants.SERVER_QUALIFIER
967      : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
968      + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
969  }
970
971  /**
972   * Returns the column qualifier for server start code column for replicaId
973   * @param replicaId the replicaId of the region
974   * @return a byte[] for server start code column qualifier
975   */
976  @VisibleForTesting
977  public static byte[] getStartCodeColumn(int replicaId) {
978    return replicaId == 0
979      ? HConstants.STARTCODE_QUALIFIER
980      : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
981      + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
982  }
983
984  /**
985   * Returns the column qualifier for seqNum column for replicaId
986   * @param replicaId the replicaId of the region
987   * @return a byte[] for seqNum column qualifier
988   */
989  @VisibleForTesting
990  public static byte[] getSeqNumColumn(int replicaId) {
991    return replicaId == 0
992      ? HConstants.SEQNUM_QUALIFIER
993      : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
994      + String.format(RegionInfo.REPLICA_ID_FORMAT, replicaId));
995  }
996
997  /**
998   * Parses the replicaId from the server column qualifier. See top of the class javadoc
999   * for the actual meta layout
1000   * @param serverColumn the column qualifier
1001   * @return an int for the replicaId
1002   */
1003  @VisibleForTesting
1004  static int parseReplicaIdFromServerColumn(byte[] serverColumn) {
1005    String serverStr = Bytes.toString(serverColumn);
1006
1007    Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr);
1008    if (matcher.matches() && matcher.groupCount() > 0) {
1009      String group = matcher.group(1);
1010      if (group != null && group.length() > 0) {
1011        return Integer.parseInt(group.substring(1), 16);
1012      } else {
1013        return 0;
1014      }
1015    }
1016    return -1;
1017  }
1018
1019  /**
1020   * Returns a {@link ServerName} from catalog table {@link Result}.
1021   * @param r Result to pull from
1022   * @return A ServerName instance or null if necessary fields not found or empty.
1023   */
1024  @Nullable
1025  @InterfaceAudience.Private // for use by HMaster#getTableRegionRow which is used for testing only
1026  public static ServerName getServerName(final Result r, final int replicaId) {
1027    byte[] serverColumn = getServerColumn(replicaId);
1028    Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn);
1029    if (cell == null || cell.getValueLength() == 0) return null;
1030    String hostAndPort = Bytes.toString(
1031      cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1032    byte[] startcodeColumn = getStartCodeColumn(replicaId);
1033    cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn);
1034    if (cell == null || cell.getValueLength() == 0) return null;
1035    try {
1036      return ServerName.valueOf(hostAndPort,
1037          Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
1038    } catch (IllegalArgumentException e) {
1039      LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e);
1040      return null;
1041    }
1042  }
1043
1044  /**
1045   * The latest seqnum that the server writing to meta observed when opening the region.
1046   * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written.
1047   * @param r Result to pull the seqNum from
1048   * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
1049   */
1050  private static long getSeqNumDuringOpen(final Result r, final int replicaId) {
1051    Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId));
1052    if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM;
1053    return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
1054  }
1055
1056  /**
1057   * Returns the daughter regions by reading the corresponding columns of the catalog table
1058   * Result.
1059   * @param data a Result object from the catalog table scan
1060   * @return pair of RegionInfo or PairOfSameType(null, null) if region is not a split parent
1061   */
1062  public static PairOfSameType<RegionInfo> getDaughterRegions(Result data) {
1063    RegionInfo splitA = getRegionInfo(data, HConstants.SPLITA_QUALIFIER);
1064    RegionInfo splitB = getRegionInfo(data, HConstants.SPLITB_QUALIFIER);
1065    return new PairOfSameType<>(splitA, splitB);
1066  }
1067
1068  /**
1069   * Returns an HRegionLocationList extracted from the result.
1070   * @return an HRegionLocationList containing all locations for the region range or null if
1071   *  we can't deserialize the result.
1072   */
1073  @Nullable
1074  public static RegionLocations getRegionLocations(final Result r) {
1075    if (r == null) return null;
1076    RegionInfo regionInfo = getRegionInfo(r, getRegionInfoColumn());
1077    if (regionInfo == null) return null;
1078
1079    List<HRegionLocation> locations = new ArrayList<>(1);
1080    NavigableMap<byte[],NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap();
1081
1082    locations.add(getRegionLocation(r, regionInfo, 0));
1083
1084    NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily());
1085    if (infoMap == null) return new RegionLocations(locations);
1086
1087    // iterate until all serverName columns are seen
1088    int replicaId = 0;
1089    byte[] serverColumn = getServerColumn(replicaId);
1090    SortedMap<byte[], byte[]> serverMap;
1091    serverMap = infoMap.tailMap(serverColumn, false);
1092
1093    if (serverMap.isEmpty()) return new RegionLocations(locations);
1094
1095    for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) {
1096      replicaId = parseReplicaIdFromServerColumn(entry.getKey());
1097      if (replicaId < 0) {
1098        break;
1099      }
1100      HRegionLocation location = getRegionLocation(r, regionInfo, replicaId);
1101      // In case the region replica is newly created, it's location might be null. We usually do not
1102      // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs.
1103      if (location.getServerName() == null) {
1104        locations.add(null);
1105      } else {
1106        locations.add(location);
1107      }
1108    }
1109
1110    return new RegionLocations(locations);
1111  }
1112
1113  /**
1114   * Returns the HRegionLocation parsed from the given meta row Result
1115   * for the given regionInfo and replicaId. The regionInfo can be the default region info
1116   * for the replica.
1117   * @param r the meta row result
1118   * @param regionInfo RegionInfo for default replica
1119   * @param replicaId the replicaId for the HRegionLocation
1120   * @return HRegionLocation parsed from the given meta row Result for the given replicaId
1121   */
1122  private static HRegionLocation getRegionLocation(final Result r, final RegionInfo regionInfo,
1123                                                   final int replicaId) {
1124    ServerName serverName = getServerName(r, replicaId);
1125    long seqNum = getSeqNumDuringOpen(r, replicaId);
1126    RegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId);
1127    return new HRegionLocation(replicaInfo, serverName, seqNum);
1128  }
1129
1130  /**
1131   * Returns RegionInfo object from the column
1132   * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
1133   * table Result.
1134   * @param data a Result object from the catalog table scan
1135   * @return RegionInfo or null
1136   */
1137  public static RegionInfo getRegionInfo(Result data) {
1138    return getRegionInfo(data, HConstants.REGIONINFO_QUALIFIER);
1139  }
1140
1141  /**
1142   * Returns the RegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
1143   * <code>qualifier</code> of the catalog table result.
1144   * @param r a Result object from the catalog table scan
1145   * @param qualifier Column family qualifier
1146   * @return An RegionInfo instance or null.
1147   */
1148  @Nullable
1149  public static RegionInfo getRegionInfo(final Result r, byte [] qualifier) {
1150    Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier);
1151    if (cell == null) return null;
1152    return RegionInfo.parseFromOrNull(cell.getValueArray(),
1153      cell.getValueOffset(), cell.getValueLength());
1154  }
1155
1156  /**
1157   * Fetch table state for given table from META table
1158   * @param conn connection to use
1159   * @param tableName table to fetch state for
1160   */
1161  @Nullable
1162  public static TableState getTableState(Connection conn, TableName tableName)
1163      throws IOException {
1164    if (tableName.equals(TableName.META_TABLE_NAME)) {
1165      return new TableState(tableName, TableState.State.ENABLED);
1166    }
1167    Table metaHTable = getMetaHTable(conn);
1168    Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getTableStateColumn());
1169    Result result = metaHTable.get(get);
1170    return getTableState(result);
1171  }
1172
1173  /**
1174   * Fetch table states from META table
1175   * @param conn connection to use
1176   * @return map {tableName -&gt; state}
1177   */
1178  public static Map<TableName, TableState> getTableStates(Connection conn)
1179      throws IOException {
1180    final Map<TableName, TableState> states = new LinkedHashMap<>();
1181    Visitor collector = r -> {
1182      TableState state = getTableState(r);
1183      if (state != null) {
1184        states.put(state.getTableName(), state);
1185      }
1186      return true;
1187    };
1188    fullScanTables(conn, collector);
1189    return states;
1190  }
1191
1192  /**
1193   * Updates state in META
1194   * @param conn connection to use
1195   * @param tableName table to look for
1196   */
1197  public static void updateTableState(Connection conn, TableName tableName,
1198      TableState.State actual) throws IOException {
1199    updateTableState(conn, new TableState(tableName, actual));
1200  }
1201
1202  /**
1203   * Decode table state from META Result.
1204   * Should contain cell from HConstants.TABLE_FAMILY
1205   * @return null if not found
1206   */
1207  @Nullable
1208  public static TableState getTableState(Result r) throws IOException {
1209    Cell cell = r.getColumnLatestCell(getTableFamily(), getTableStateColumn());
1210    if (cell == null) {
1211      return null;
1212    }
1213    try {
1214      return TableState.parseFrom(TableName.valueOf(r.getRow()),
1215        Arrays.copyOfRange(cell.getValueArray(), cell.getValueOffset(),
1216          cell.getValueOffset() + cell.getValueLength()));
1217    } catch (DeserializationException e) {
1218      throw new IOException(e);
1219    }
1220  }
1221
1222  /**
1223   * Implementations 'visit' a catalog table row.
1224   */
1225  public interface Visitor {
1226    /**
1227     * Visit the catalog table row.
1228     * @param r A row from catalog table
1229     * @return True if we are to proceed scanning the table, else false if
1230     * we are to stop now.
1231     */
1232    boolean visit(final Result r) throws IOException;
1233  }
1234
1235  /**
1236   * Implementations 'visit' a catalog table row but with close() at the end.
1237   */
1238  public interface CloseableVisitor extends Visitor, Closeable {
1239  }
1240
1241  /**
1242   * A {@link Visitor} that collects content out of passed {@link Result}.
1243   */
1244  static abstract class CollectingVisitor<T> implements Visitor {
1245    final List<T> results = new ArrayList<>();
1246    @Override
1247    public boolean visit(Result r) throws IOException {
1248      if (r ==  null || r.isEmpty()) return true;
1249      add(r);
1250      return true;
1251    }
1252
1253    abstract void add(Result r);
1254
1255    /**
1256     * @return Collected results; wait till visits complete to collect all
1257     * possible results
1258     */
1259    List<T> getResults() {
1260      return this.results;
1261    }
1262  }
1263
1264  /**
1265   * Collects all returned.
1266   */
1267  static class CollectAllVisitor extends CollectingVisitor<Result> {
1268    @Override
1269    void add(Result r) {
1270      this.results.add(r);
1271    }
1272  }
1273
1274  /**
1275   * A Visitor that skips offline regions and split parents
1276   */
1277  public static abstract class DefaultVisitorBase implements Visitor {
1278
1279    public DefaultVisitorBase() {
1280      super();
1281    }
1282
1283    public abstract boolean visitInternal(Result rowResult) throws IOException;
1284
1285    @Override
1286    public boolean visit(Result rowResult) throws IOException {
1287      RegionInfo info = getRegionInfo(rowResult);
1288      if (info == null) {
1289        return true;
1290      }
1291
1292      //skip over offline and split regions
1293      if (!(info.isOffline() || info.isSplit())) {
1294        return visitInternal(rowResult);
1295      }
1296      return true;
1297    }
1298  }
1299
1300  /**
1301   * A Visitor for a table. Provides a consistent view of the table's
1302   * hbase:meta entries during concurrent splits (see HBASE-5986 for details). This class
1303   * does not guarantee ordered traversal of meta entries, and can block until the
1304   * hbase:meta entries for daughters are available during splits.
1305   */
1306  public static abstract class TableVisitorBase extends DefaultVisitorBase {
1307    private TableName tableName;
1308
1309    public TableVisitorBase(TableName tableName) {
1310      super();
1311      this.tableName = tableName;
1312    }
1313
1314    @Override
1315    public final boolean visit(Result rowResult) throws IOException {
1316      RegionInfo info = getRegionInfo(rowResult);
1317      if (info == null) {
1318        return true;
1319      }
1320      if (!(info.getTable().equals(tableName))) {
1321        return false;
1322      }
1323      return super.visit(rowResult);
1324    }
1325  }
1326
1327  /**
1328   * Count regions in <code>hbase:meta</code> for passed table.
1329   * @param c Configuration object
1330   * @param tableName table name to count regions for
1331   * @return Count or regions in table <code>tableName</code>
1332   */
1333  public static int getRegionCount(final Configuration c, final TableName tableName)
1334      throws IOException {
1335    try (Connection connection = ConnectionFactory.createConnection(c)) {
1336      return getRegionCount(connection, tableName);
1337    }
1338  }
1339
1340  /**
1341   * Count regions in <code>hbase:meta</code> for passed table.
1342   * @param connection Connection object
1343   * @param tableName table name to count regions for
1344   * @return Count or regions in table <code>tableName</code>
1345   */
1346  public static int getRegionCount(final Connection connection, final TableName tableName)
1347      throws IOException {
1348    try (RegionLocator locator = connection.getRegionLocator(tableName)) {
1349      List<HRegionLocation> locations = locator.getAllRegionLocations();
1350      return locations == null ? 0 : locations.size();
1351    }
1352  }
1353
1354  ////////////////////////
1355  // Editing operations //
1356  ////////////////////////
1357
1358  /**
1359   * Generates and returns a Put containing the region into for the catalog table
1360   */
1361  public static Put makePutFromRegionInfo(RegionInfo regionInfo, long ts) throws IOException {
1362    Put put = new Put(regionInfo.getRegionName(), ts);
1363    addRegionInfo(put, regionInfo);
1364    return put;
1365  }
1366
1367  /**
1368   * Generates and returns a Delete containing the region info for the catalog table
1369   */
1370  private static Delete makeDeleteFromRegionInfo(RegionInfo regionInfo, long ts) {
1371    if (regionInfo == null) {
1372      throw new IllegalArgumentException("Can't make a delete for null region");
1373    }
1374    Delete delete = new Delete(regionInfo.getRegionName());
1375    delete.addFamily(getCatalogFamily(), ts);
1376    return delete;
1377  }
1378
1379  /**
1380   * Adds split daughters to the Put
1381   */
1382  private static Put addDaughtersToPut(Put put, RegionInfo splitA, RegionInfo splitB)
1383      throws IOException {
1384    if (splitA != null) {
1385      put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1386                .setRow(put.getRow())
1387                .setFamily(HConstants.CATALOG_FAMILY)
1388                .setQualifier(HConstants.SPLITA_QUALIFIER)
1389                .setTimestamp(put.getTimestamp())
1390                .setType(Type.Put)
1391                .setValue(RegionInfo.toByteArray(splitA))
1392                .build());
1393    }
1394    if (splitB != null) {
1395      put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1396                .setRow(put.getRow())
1397                .setFamily(HConstants.CATALOG_FAMILY)
1398                .setQualifier(HConstants.SPLITB_QUALIFIER)
1399                .setTimestamp(put.getTimestamp())
1400                .setType(Type.Put)
1401                .setValue(RegionInfo.toByteArray(splitB))
1402                .build());
1403    }
1404    return put;
1405  }
1406
1407  /**
1408   * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
1409   * @param connection connection we're using
1410   * @param p Put to add to hbase:meta
1411   */
1412  private static void putToMetaTable(Connection connection, Put p) throws IOException {
1413    try (Table table = getMetaHTable(connection)) {
1414      put(table, p);
1415    }
1416  }
1417
1418  /**
1419   * @param t Table to use
1420   * @param p put to make
1421   */
1422  private static void put(Table t, Put p) throws IOException {
1423    debugLogMutation(p);
1424    t.put(p);
1425  }
1426
1427  /**
1428   * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
1429   * @param connection connection we're using
1430   * @param ps Put to add to hbase:meta
1431   */
1432  public static void putsToMetaTable(final Connection connection, final List<Put> ps)
1433      throws IOException {
1434    if (ps.isEmpty()) {
1435      return;
1436    }
1437    try (Table t = getMetaHTable(connection)) {
1438      debugLogMutations(ps);
1439      // the implementation for putting a single Put is much simpler so here we do a check first.
1440      if (ps.size() == 1) {
1441        t.put(ps.get(0));
1442      } else {
1443        t.put(ps);
1444      }
1445    }
1446  }
1447
1448  /**
1449   * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
1450   * @param connection connection we're using
1451   * @param d Delete to add to hbase:meta
1452   */
1453  private static void deleteFromMetaTable(final Connection connection, final Delete d)
1454      throws IOException {
1455    List<Delete> dels = new ArrayList<>(1);
1456    dels.add(d);
1457    deleteFromMetaTable(connection, dels);
1458  }
1459
1460  /**
1461   * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
1462   * @param connection connection we're using
1463   * @param deletes Deletes to add to hbase:meta  This list should support #remove.
1464   */
1465  private static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes)
1466      throws IOException {
1467    try (Table t = getMetaHTable(connection)) {
1468      debugLogMutations(deletes);
1469      t.delete(deletes);
1470    }
1471  }
1472
1473  /**
1474   * Deletes some replica columns corresponding to replicas for the passed rows
1475   * @param metaRows rows in hbase:meta
1476   * @param replicaIndexToDeleteFrom the replica ID we would start deleting from
1477   * @param numReplicasToRemove how many replicas to remove
1478   * @param connection connection we're using to access meta table
1479   */
1480  public static void removeRegionReplicasFromMeta(Set<byte[]> metaRows,
1481    int replicaIndexToDeleteFrom, int numReplicasToRemove, Connection connection)
1482      throws IOException {
1483    int absoluteIndex = replicaIndexToDeleteFrom + numReplicasToRemove;
1484    for (byte[] row : metaRows) {
1485      long now = EnvironmentEdgeManager.currentTime();
1486      Delete deleteReplicaLocations = new Delete(row);
1487      for (int i = replicaIndexToDeleteFrom; i < absoluteIndex; i++) {
1488        deleteReplicaLocations.addColumns(getCatalogFamily(),
1489          getServerColumn(i), now);
1490        deleteReplicaLocations.addColumns(getCatalogFamily(),
1491          getSeqNumColumn(i), now);
1492        deleteReplicaLocations.addColumns(getCatalogFamily(),
1493          getStartCodeColumn(i), now);
1494      }
1495      deleteFromMetaTable(connection, deleteReplicaLocations);
1496    }
1497  }
1498
1499  private static void addRegionStateToPut(Put put, RegionState.State state) throws IOException {
1500    put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1501        .setRow(put.getRow())
1502        .setFamily(HConstants.CATALOG_FAMILY)
1503        .setQualifier(getRegionStateColumn())
1504        .setTimestamp(put.getTimestamp())
1505        .setType(Cell.Type.Put)
1506        .setValue(Bytes.toBytes(state.name()))
1507        .build());
1508  }
1509
1510  /**
1511   * Adds daughter region infos to hbase:meta row for the specified region. Note that this does not
1512   * add its daughter's as different rows, but adds information about the daughters in the same row
1513   * as the parent. Use
1514   * {@link #splitRegion(Connection, RegionInfo, RegionInfo, RegionInfo, ServerName,int)}
1515   * if you want to do that.
1516   * @param connection connection we're using
1517   * @param regionInfo RegionInfo of parent region
1518   * @param splitA first split daughter of the parent regionInfo
1519   * @param splitB second split daughter of the parent regionInfo
1520   * @throws IOException if problem connecting or updating meta
1521   */
1522  public static void addSplitsToParent(Connection connection, RegionInfo regionInfo,
1523      RegionInfo splitA, RegionInfo splitB) throws IOException {
1524    try (Table meta = getMetaHTable(connection)) {
1525      Put put = makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime());
1526      addDaughtersToPut(put, splitA, splitB);
1527      meta.put(put);
1528      debugLogMutation(put);
1529      LOG.debug("Added region {}", regionInfo.getRegionNameAsString());
1530    }
1531  }
1532
1533  /**
1534   * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1535   * does not add its daughter's as different rows, but adds information about the daughters
1536   * in the same row as the parent. Use
1537   * {@link #splitRegion(Connection, RegionInfo, RegionInfo, RegionInfo, ServerName, int)}
1538   * if you want to do that.
1539   * @param connection connection we're using
1540   * @param regionInfo region information
1541   * @throws IOException if problem connecting or updating meta
1542   */
1543  @VisibleForTesting
1544  public static void addRegionToMeta(Connection connection, RegionInfo regionInfo)
1545      throws IOException {
1546    addRegionsToMeta(connection, Collections.singletonList(regionInfo), 1);
1547  }
1548
1549  /**
1550   * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions
1551   * is CLOSED.
1552   * @param connection connection we're using
1553   * @param regionInfos region information list
1554   * @throws IOException if problem connecting or updating meta
1555   */
1556  public static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
1557      int regionReplication) throws IOException {
1558    addRegionsToMeta(connection, regionInfos, regionReplication,
1559      EnvironmentEdgeManager.currentTime());
1560  }
1561
1562  /**
1563   * Adds a hbase:meta row for each of the specified new regions. Initial state for new regions
1564   * is CLOSED.
1565   * @param connection connection we're using
1566   * @param regionInfos region information list
1567   * @param ts desired timestamp
1568   * @throws IOException if problem connecting or updating meta
1569   */
1570  private static void addRegionsToMeta(Connection connection, List<RegionInfo> regionInfos,
1571      int regionReplication, long ts) throws IOException {
1572    List<Put> puts = new ArrayList<>();
1573    for (RegionInfo regionInfo : regionInfos) {
1574      if (RegionReplicaUtil.isDefaultReplica(regionInfo)) {
1575        Put put = makePutFromRegionInfo(regionInfo, ts);
1576        // New regions are added with initial state of CLOSED.
1577        addRegionStateToPut(put, RegionState.State.CLOSED);
1578        // Add empty locations for region replicas so that number of replicas can be cached
1579        // whenever the primary region is looked up from meta
1580        for (int i = 1; i < regionReplication; i++) {
1581          addEmptyLocation(put, i);
1582        }
1583        puts.add(put);
1584      }
1585    }
1586    putsToMetaTable(connection, puts);
1587    LOG.info("Added {} regions to meta.", puts.size());
1588  }
1589
1590  static Put addMergeRegions(Put put, Collection<RegionInfo> mergeRegions) throws IOException {
1591    int limit = 10000; // Arbitrary limit. No room in our formatted 'task0000' below for more.
1592    int max = mergeRegions.size();
1593    if (max > limit) {
1594      // Should never happen!!!!! But just in case.
1595      throw new RuntimeException("Can't merge " + max + " regions in one go; " + limit +
1596          " is upper-limit.");
1597    }
1598    int counter = 0;
1599    for (RegionInfo ri: mergeRegions) {
1600      String qualifier = String.format(HConstants.MERGE_QUALIFIER_PREFIX_STR + "%04d", counter++);
1601      put.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY).
1602          setRow(put.getRow()).
1603          setFamily(HConstants.CATALOG_FAMILY).
1604          setQualifier(Bytes.toBytes(qualifier)).
1605          setTimestamp(put.getTimestamp()).
1606          setType(Type.Put).
1607          setValue(RegionInfo.toByteArray(ri)).
1608          build());
1609    }
1610    return put;
1611  }
1612
1613  /**
1614   * Merge regions into one in an atomic operation. Deletes the merging regions in
1615   * hbase:meta and adds the merged region.
1616   * @param connection connection we're using
1617   * @param mergedRegion the merged region
1618   * @param sn the location of the region
1619   */
1620  public static void mergeRegions(Connection connection, RegionInfo mergedRegion,
1621        RegionInfo [] parents, ServerName sn, int regionReplication)
1622      throws IOException {
1623    try (Table meta = getMetaHTable(connection)) {
1624      long time = HConstants.LATEST_TIMESTAMP;
1625      List<Mutation> mutations = new ArrayList<>();
1626      for (RegionInfo ri: parents) {
1627        // Deletes for merging regions
1628        mutations.add(makeDeleteFromRegionInfo(ri, time));
1629      }
1630      // Put for parent
1631      Put putOfMerged = makePutFromRegionInfo(mergedRegion, time);
1632      putOfMerged = addMergeRegions(putOfMerged, Arrays.asList(parents));
1633      // Set initial state to CLOSED.
1634      // NOTE: If initial state is not set to CLOSED then merged region gets added with the
1635      // default OFFLINE state. If Master gets restarted after this step, start up sequence of
1636      // master tries to assign this offline region. This is followed by re-assignments of the
1637      // merged region from resumed {@link MergeTableRegionsProcedure}
1638      addRegionStateToPut(putOfMerged, RegionState.State.CLOSED);
1639      mutations.add(putOfMerged);
1640      // The merged is a new region, openSeqNum = 1 is fine. ServerName may be null
1641      // if crash after merge happened but before we got to here.. means in-memory
1642      // locations of offlined merged, now-closed, regions is lost. Should be ok. We
1643      // assign the merged region later.
1644      if (sn != null) {
1645        addLocation(putOfMerged, sn, 1, mergedRegion.getReplicaId());
1646      }
1647
1648      // Add empty locations for region replicas of the merged region so that number of replicas
1649      // can be cached whenever the primary region is looked up from meta
1650      for (int i = 1; i < regionReplication; i++) {
1651        addEmptyLocation(putOfMerged, i);
1652      }
1653
1654      byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString()
1655        + HConstants.DELIMITER);
1656      multiMutate(connection, meta, tableRow, mutations);
1657    }
1658  }
1659
1660  /**
1661   * Splits the region into two in an atomic operation. Offlines the parent
1662   * region with the information that it is split into two, and also adds
1663   * the daughter regions. Does not add the location information to the daughter
1664   * regions since they are not open yet.
1665   * @param connection connection we're using
1666   * @param parent the parent region which is split
1667   * @param splitA Split daughter region A
1668   * @param splitB Split daughter region B
1669   * @param sn the location of the region
1670   */
1671  public static void splitRegion(final Connection connection, RegionInfo parent, RegionInfo splitA,
1672      RegionInfo splitB, ServerName sn, int regionReplication) throws IOException {
1673    try (Table meta = getMetaHTable(connection)) {
1674      long time = EnvironmentEdgeManager.currentTime();
1675      // Put for parent
1676      Put putParent = makePutFromRegionInfo(RegionInfoBuilder.newBuilder(parent)
1677                        .setOffline(true)
1678                        .setSplit(true).build(), time);
1679      addDaughtersToPut(putParent, splitA, splitB);
1680
1681      // Puts for daughters
1682      Put putA = makePutFromRegionInfo(splitA, time);
1683      Put putB = makePutFromRegionInfo(splitB, time);
1684
1685      // Set initial state to CLOSED
1686      // NOTE: If initial state is not set to CLOSED then daughter regions get added with the
1687      // default OFFLINE state. If Master gets restarted after this step, start up sequence of
1688      // master tries to assign these offline regions. This is followed by re-assignments of the
1689      // daughter regions from resumed {@link SplitTableRegionProcedure}
1690      addRegionStateToPut(putA, RegionState.State.CLOSED);
1691      addRegionStateToPut(putB, RegionState.State.CLOSED);
1692
1693      addSequenceNum(putA, 1, splitA.getReplicaId()); // new regions, openSeqNum = 1 is fine.
1694      addSequenceNum(putB, 1, splitB.getReplicaId());
1695
1696      // Add empty locations for region replicas of daughters so that number of replicas can be
1697      // cached whenever the primary region is looked up from meta
1698      for (int i = 1; i < regionReplication; i++) {
1699        addEmptyLocation(putA, i);
1700        addEmptyLocation(putB, i);
1701      }
1702
1703      byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER);
1704      multiMutate(connection, meta, tableRow, putParent, putA, putB);
1705    }
1706  }
1707
1708  /**
1709   * Update state of the table in meta.
1710   * @param connection what we use for update
1711   * @param state new state
1712   */
1713  private static void updateTableState(Connection connection, TableState state) throws IOException {
1714    Put put = makePutFromTableState(state, EnvironmentEdgeManager.currentTime());
1715    putToMetaTable(connection, put);
1716    LOG.info("Updated {} in hbase:meta", state);
1717  }
1718
1719  /**
1720   * Construct PUT for given state
1721   * @param state new state
1722   */
1723  public static Put makePutFromTableState(TableState state, long ts) {
1724    Put put = new Put(state.getTableName().getName(), ts);
1725    put.addColumn(getTableFamily(), getTableStateColumn(), state.convert().toByteArray());
1726    return put;
1727  }
1728
1729  /**
1730   * Remove state for table from meta
1731   * @param connection to use for deletion
1732   * @param table to delete state for
1733   */
1734  public static void deleteTableState(Connection connection, TableName table)
1735      throws IOException {
1736    long time = EnvironmentEdgeManager.currentTime();
1737    Delete delete = new Delete(table.getName());
1738    delete.addColumns(getTableFamily(), getTableStateColumn(), time);
1739    deleteFromMetaTable(connection, delete);
1740    LOG.info("Deleted table " + table + " state from META");
1741  }
1742
1743  private static void multiMutate(Connection connection, Table table, byte[] row,
1744      Mutation... mutations)
1745  throws IOException {
1746    multiMutate(connection, table, row, Arrays.asList(mutations));
1747  }
1748
1749  /**
1750   * Performs an atomic multi-mutate operation against the given table.
1751   */
1752  // Used by the RSGroup Coprocessor Endpoint. It had a copy/paste of the below. Need to reveal
1753  // this facility for CPEP use or at least those CPEPs that are on their way to becoming part of
1754  // core as is the intent for RSGroup eventually.
1755  public static void multiMutate(Connection connection, final Table table, byte[] row,
1756      final List<Mutation> mutations)
1757  throws IOException {
1758    debugLogMutations(mutations);
1759    // TODO: Need rollback!!!!
1760    // TODO: Need Retry!!!
1761    // TODO: What for a timeout? Default write timeout? GET FROM HTABLE?
1762    // TODO: Review when we come through with ProcedureV2.
1763    RegionServerCallable<MutateRowsResponse,
1764        MultiRowMutationProtos.MultiRowMutationService.BlockingInterface> callable =
1765        new RegionServerCallable<MutateRowsResponse,
1766          MultiRowMutationProtos.MultiRowMutationService.BlockingInterface>(
1767              connection, table.getName(), row, null/*RpcController not used in this CPEP!*/) {
1768      @Override
1769      protected MutateRowsResponse rpcCall() throws Exception {
1770        final MutateRowsRequest.Builder builder = MutateRowsRequest.newBuilder();
1771        for (Mutation mutation : mutations) {
1772          if (mutation instanceof Put) {
1773            builder.addMutationRequest(ProtobufUtil.toMutation(
1774              ClientProtos.MutationProto.MutationType.PUT, mutation));
1775          } else if (mutation instanceof Delete) {
1776            builder.addMutationRequest(ProtobufUtil.toMutation(
1777              ClientProtos.MutationProto.MutationType.DELETE, mutation));
1778          } else {
1779            throw new DoNotRetryIOException("multi in MetaEditor doesn't support "
1780              + mutation.getClass().getName());
1781          }
1782        }
1783        // The call to #prepare that ran before this invocation will have populated HRegionLocation.
1784        HRegionLocation hrl = getLocation();
1785        RegionSpecifier region = ProtobufUtil.buildRegionSpecifier(
1786            RegionSpecifierType.REGION_NAME, hrl.getRegion().getRegionName());
1787        builder.setRegion(region);
1788        // The rpcController here is awkward. The Coprocessor Endpoint wants an instance of a
1789        // com.google.protobuf but we are going over an rpc that is all shaded protobuf so it
1790        // wants a org.apache.h.h.shaded.com.google.protobuf.RpcController. Set up a factory
1791        // that makes com.google.protobuf.RpcController and then copy into it configs.
1792        return getStub().mutateRows(null, builder.build());
1793      }
1794
1795      @Override
1796      // Called on the end of the super.prepare call. Set the stub.
1797      protected void setStubByServiceName(ServerName serviceName/*Ignored*/) throws IOException {
1798        CoprocessorRpcChannel channel = table.coprocessorService(getRow());
1799        setStub(MultiRowMutationProtos.MultiRowMutationService.newBlockingStub(channel));
1800      }
1801    };
1802    int writeTimeout = connection.getConfiguration().getInt(HConstants.HBASE_RPC_WRITE_TIMEOUT_KEY,
1803        connection.getConfiguration().getInt(HConstants.HBASE_RPC_TIMEOUT_KEY,
1804            HConstants.DEFAULT_HBASE_RPC_TIMEOUT));
1805    // The region location should be cached in connection. Call prepare so this callable picks
1806    // up the region location (see super.prepare method).
1807    callable.prepare(false);
1808    callable.call(writeTimeout);
1809  }
1810
1811  /**
1812   * Updates the location of the specified region in hbase:meta to be the specified server hostname
1813   * and startcode.
1814   * <p>
1815   * Uses passed catalog tracker to get a connection to the server hosting hbase:meta and makes
1816   * edits to that region.
1817   * @param connection connection we're using
1818   * @param regionInfo region to update location of
1819   * @param openSeqNum the latest sequence number obtained when the region was open
1820   * @param sn Server name
1821   * @param masterSystemTime wall clock time from master if passed in the open region RPC
1822   */
1823  @VisibleForTesting
1824  public static void updateRegionLocation(Connection connection, RegionInfo regionInfo,
1825      ServerName sn, long openSeqNum, long masterSystemTime) throws IOException {
1826    updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime);
1827  }
1828
1829  /**
1830   * Updates the location of the specified region to be the specified server.
1831   * <p>
1832   * Connects to the specified server which should be hosting the specified catalog region name to
1833   * perform the edit.
1834   * @param connection connection we're using
1835   * @param regionInfo region to update location of
1836   * @param sn Server name
1837   * @param openSeqNum the latest sequence number obtained when the region was open
1838   * @param masterSystemTime wall clock time from master if passed in the open region RPC
1839   * @throws IOException In particular could throw {@link java.net.ConnectException} if the server
1840   *           is down on other end.
1841   */
1842  private static void updateLocation(Connection connection, RegionInfo regionInfo, ServerName sn,
1843      long openSeqNum, long masterSystemTime) throws IOException {
1844    // region replicas are kept in the primary region's row
1845    Put put = new Put(getMetaKeyForRegion(regionInfo), masterSystemTime);
1846    addRegionInfo(put, regionInfo);
1847    addLocation(put, sn, openSeqNum, regionInfo.getReplicaId());
1848    putToMetaTable(connection, put);
1849    LOG.info("Updated row {} with server=", regionInfo.getRegionNameAsString(), sn);
1850  }
1851
1852  /**
1853   * Deletes the specified region from META.
1854   * @param connection connection we're using
1855   * @param regionInfo region to be deleted from META
1856   */
1857  public static void deleteRegionInfo(Connection connection, RegionInfo regionInfo)
1858      throws IOException {
1859    Delete delete = new Delete(regionInfo.getRegionName());
1860    delete.addFamily(getCatalogFamily(), HConstants.LATEST_TIMESTAMP);
1861    deleteFromMetaTable(connection, delete);
1862    LOG.info("Deleted " + regionInfo.getRegionNameAsString());
1863  }
1864
1865  /**
1866   * Deletes the specified regions from META.
1867   * @param connection connection we're using
1868   * @param regionsInfo list of regions to be deleted from META
1869   */
1870  public static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo)
1871      throws IOException {
1872    deleteRegionInfos(connection, regionsInfo, EnvironmentEdgeManager.currentTime());
1873  }
1874
1875  /**
1876   * Deletes the specified regions from META.
1877   * @param connection connection we're using
1878   * @param regionsInfo list of regions to be deleted from META
1879   */
1880  private static void deleteRegionInfos(Connection connection, List<RegionInfo> regionsInfo,
1881        long ts)
1882      throws IOException {
1883    List<Delete> deletes = new ArrayList<>(regionsInfo.size());
1884    for (RegionInfo hri : regionsInfo) {
1885      Delete e = new Delete(hri.getRegionName());
1886      e.addFamily(getCatalogFamily(), ts);
1887      deletes.add(e);
1888    }
1889    deleteFromMetaTable(connection, deletes);
1890    LOG.info("Deleted {} regions from META", regionsInfo.size());
1891    LOG.debug("Deleted regions: {}", regionsInfo);
1892  }
1893
1894  /**
1895   * Overwrites the specified regions from hbase:meta. Deletes old rows for the given regions and
1896   * adds new ones. Regions added back have state CLOSED.
1897   * @param connection connection we're using
1898   * @param regionInfos list of regions to be added to META
1899   */
1900  public static void overwriteRegions(Connection connection, List<RegionInfo> regionInfos,
1901      int regionReplication) throws IOException {
1902    // use master time for delete marker and the Put
1903    long now = EnvironmentEdgeManager.currentTime();
1904    deleteRegionInfos(connection, regionInfos, now);
1905    // Why sleep? This is the easiest way to ensure that the previous deletes does not
1906    // eclipse the following puts, that might happen in the same ts from the server.
1907    // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed,
1908    // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep.
1909    //
1910    // HBASE-13875 uses master timestamp for the mutations. The 20ms sleep is not needed
1911    addRegionsToMeta(connection, regionInfos, regionReplication, now + 1);
1912    LOG.info("Overwritten " + regionInfos.size() + " regions to Meta");
1913    LOG.debug("Overwritten regions: {} ", regionInfos);
1914  }
1915
1916  /**
1917   * Deletes merge qualifiers for the specified merge region.
1918   * @param connection connection we're using
1919   * @param mergeRegion the merged region
1920   */
1921  public static void deleteMergeQualifiers(Connection connection, final RegionInfo mergeRegion)
1922      throws IOException {
1923    Delete delete = new Delete(mergeRegion.getRegionName());
1924    // NOTE: We are doing a new hbase:meta read here.
1925    Cell [] cells = getMergeRegionsRaw(connection, mergeRegion.getRegionName());
1926    if (cells == null || cells.length == 0) {
1927      return;
1928    }
1929    List<byte[]> qualifiers = new ArrayList<>(cells.length);
1930    for (Cell cell : cells) {
1931      byte[] qualifier = CellUtil.cloneQualifier(cell);
1932      qualifiers.add(qualifier);
1933      delete.addColumns(getCatalogFamily(), qualifier, HConstants.LATEST_TIMESTAMP);
1934    }
1935    deleteFromMetaTable(connection, delete);
1936    LOG.info("Deleted merge references in " + mergeRegion.getRegionNameAsString() +
1937        ", deleted qualifiers " +
1938        qualifiers.stream().map(Bytes::toStringBinary).
1939            collect(Collectors.joining(", ")));
1940  }
1941
1942  public static Put addRegionInfo(final Put p, final RegionInfo hri)
1943    throws IOException {
1944    p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
1945        .setRow(p.getRow())
1946        .setFamily(getCatalogFamily())
1947        .setQualifier(HConstants.REGIONINFO_QUALIFIER)
1948        .setTimestamp(p.getTimestamp())
1949        .setType(Type.Put)
1950        .setValue(RegionInfo.toByteArray(hri))
1951        .build());
1952    return p;
1953  }
1954
1955  public static Put addLocation(Put p, ServerName sn, long openSeqNum, int replicaId)
1956      throws IOException {
1957    CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
1958    return p.add(builder.clear()
1959              .setRow(p.getRow())
1960              .setFamily(getCatalogFamily())
1961              .setQualifier(getServerColumn(replicaId))
1962              .setTimestamp(p.getTimestamp())
1963              .setType(Cell.Type.Put)
1964              .setValue(Bytes.toBytes(sn.getAddress().toString()))
1965              .build())
1966            .add(builder.clear()
1967              .setRow(p.getRow())
1968              .setFamily(getCatalogFamily())
1969              .setQualifier(getStartCodeColumn(replicaId))
1970              .setTimestamp(p.getTimestamp())
1971              .setType(Cell.Type.Put)
1972              .setValue(Bytes.toBytes(sn.getStartcode()))
1973              .build())
1974            .add(builder.clear()
1975              .setRow(p.getRow())
1976              .setFamily(getCatalogFamily())
1977              .setQualifier(getSeqNumColumn(replicaId))
1978              .setTimestamp(p.getTimestamp())
1979              .setType(Type.Put)
1980              .setValue(Bytes.toBytes(openSeqNum))
1981              .build());
1982  }
1983
1984  private static Put addEmptyLocation(Put p, int replicaId) throws IOException {
1985    CellBuilder builder = CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY);
1986    return p.add(builder.clear()
1987                .setRow(p.getRow())
1988                .setFamily(getCatalogFamily())
1989                .setQualifier(getServerColumn(replicaId))
1990                .setTimestamp(p.getTimestamp())
1991                .setType(Type.Put)
1992                .build())
1993            .add(builder.clear()
1994                .setRow(p.getRow())
1995                .setFamily(getCatalogFamily())
1996                .setQualifier(getStartCodeColumn(replicaId))
1997                .setTimestamp(p.getTimestamp())
1998                .setType(Cell.Type.Put)
1999                .build())
2000            .add(builder.clear()
2001                .setRow(p.getRow())
2002                .setFamily(getCatalogFamily())
2003                .setQualifier(getSeqNumColumn(replicaId))
2004                .setTimestamp(p.getTimestamp())
2005                .setType(Cell.Type.Put)
2006                .build());
2007  }
2008
2009
2010  private static void debugLogMutations(List<? extends Mutation> mutations) throws IOException {
2011    if (!METALOG.isDebugEnabled()) {
2012      return;
2013    }
2014    // Logging each mutation in separate line makes it easier to see diff between them visually
2015    // because of common starting indentation.
2016    for (Mutation mutation : mutations) {
2017      debugLogMutation(mutation);
2018    }
2019  }
2020
2021  private static void debugLogMutation(Mutation p) throws IOException {
2022    METALOG.debug("{} {}", p.getClass().getSimpleName(), p.toJSON());
2023  }
2024
2025  private static Put addSequenceNum(Put p, long openSeqNum, int replicaId) throws IOException {
2026    return p.add(CellBuilderFactory.create(CellBuilderType.SHALLOW_COPY)
2027              .setRow(p.getRow())
2028              .setFamily(HConstants.CATALOG_FAMILY)
2029              .setQualifier(getSeqNumColumn(replicaId))
2030              .setTimestamp(p.getTimestamp())
2031              .setType(Type.Put)
2032              .setValue(Bytes.toBytes(openSeqNum))
2033              .build());
2034  }
2035}