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