View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase;
19  
20  import com.google.common.annotations.VisibleForTesting;
21  import com.google.protobuf.ServiceException;
22
23  import java.io.Closeable;
24  import java.io.IOException;
25  import java.io.InterruptedIOException;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.HashMap;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.NavigableMap;
33  import java.util.Set;
34  import java.util.SortedMap;
35  import java.util.TreeMap;
36  import java.util.regex.Matcher;
37  import java.util.regex.Pattern;
38
39  import edu.umd.cs.findbugs.annotations.NonNull;
40  import edu.umd.cs.findbugs.annotations.Nullable;
41  import org.apache.commons.logging.Log;
42  import org.apache.commons.logging.LogFactory;
43  import org.apache.hadoop.conf.Configuration;
44  import org.apache.hadoop.hbase.classification.InterfaceAudience;
45  import org.apache.hadoop.hbase.client.Connection;
46  import org.apache.hadoop.hbase.client.ConnectionFactory;
47  import org.apache.hadoop.hbase.client.Consistency;
48  import org.apache.hadoop.hbase.client.Delete;
49  import org.apache.hadoop.hbase.client.Get;
50  import org.apache.hadoop.hbase.client.Mutation;
51  import org.apache.hadoop.hbase.client.Put;
52  import org.apache.hadoop.hbase.client.RegionLocator;
53  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
54  import org.apache.hadoop.hbase.client.Result;
55  import org.apache.hadoop.hbase.client.ResultScanner;
56  import org.apache.hadoop.hbase.client.Scan;
57  import org.apache.hadoop.hbase.client.Table;
58  import org.apache.hadoop.hbase.client.TableState;
59  import org.apache.hadoop.hbase.exceptions.DeserializationException;
60  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
61  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
62  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
63  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos;
64  import org.apache.hadoop.hbase.util.Bytes;
65  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
66  import org.apache.hadoop.hbase.util.ExceptionUtil;
67  import org.apache.hadoop.hbase.util.Pair;
68  import org.apache.hadoop.hbase.util.PairOfSameType;
69
70  /**
71   * Read/write operations on region and assignment information store in
72   * <code>hbase:meta</code>.
73   *
74   * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason
75   * for this is because when used on client-side (like from HBaseAdmin), we want to use
76   * short-living connection (opened before each operation, closed right after), while
77   * when used on HM or HRS (like in AssignmentManager) we want permanent connection.
78   */
79  @InterfaceAudience.Private
80  public class MetaTableAccessor {
81
82    /*
83     * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the
84     * same table range (table, startKey, endKey). For every range, there will be at least one
85     * HRI defined which is called default replica.
86     *
87     * Meta layout (as of 0.98 + HBASE-10070) is like:
88     *
89     * For each table there is single row in column family 'table' formatted:
90     * <tableName> including namespace and columns are:
91     * table: state             => contains table state
92     *
93     * For each table range, there is a single row, formatted like:
94     * <tableName>,<startKey>,<regionId>,<encodedRegionName>. This row corresponds to the regionName
95     * of the default region replica.
96     * Columns are:
97     * info:regioninfo         => contains serialized HRI for the default region replica
98     * info:server             => contains hostname:port (in string form) for the server hosting
99     *                            the default regionInfo replica
100    * info:server_<replicaId> => contains hostname:port (in string form) for the server hosting the
101    *                            regionInfo replica with replicaId
102    * info:serverstartcode    => contains server start code (in binary long form) for the server
103    *                            hosting the default regionInfo replica
104    * info:serverstartcode_<replicaId> => contains server start code (in binary long form) for the
105    *                                     server hosting the regionInfo replica with replicaId
106    * info:seqnumDuringOpen    => contains seqNum (in binary long form) for the region at the time
107    *                             the server opened the region with default replicaId
108    * info:seqnumDuringOpen_<replicaId> => contains seqNum (in binary long form) for the region at
109    *                             the time the server opened the region with replicaId
110    * info:splitA              => contains a serialized HRI for the first daughter region if the
111    *                             region is split
112    * info:splitB              => contains a serialized HRI for the second daughter region if the
113    *                             region is split
114    * info:mergeA              => contains a serialized HRI for the first parent region if the
115    *                             region is the result of a merge
116    * info:mergeB              => contains a serialized HRI for the second parent region if the
117    *                             region is the result of a merge
118    * The actual layout of meta should be encapsulated inside MetaTableAccessor methods,
119    * and should not leak out of it (through Result objects, etc)
120    *
121    * For replication serially, there are two column families "rep_barrier", "rep_position" whose
122    * row key is encodedRegionName.
123    * rep_barrier:{seqid}      => in each time a RS opens a region, it saves the open sequence
124    *                                  id in this region
125    * rep_position:{peerid}    => to save the max sequence id we have pushed for each peer
126    * rep_position:_TABLENAME_ => a special cell to save this region's table name, will used when
127    *                             we clean old data
128    * rep_position:_DAUGHTER_  => a special cell to present this region is split or merged, in this
129    *                             cell the value is merged encoded name or two split encoded names
130    *                             separated by ","
131    */
132
133   private static final Log LOG = LogFactory.getLog(MetaTableAccessor.class);
134   private static final Log METALOG = LogFactory.getLog("org.apache.hadoop.hbase.META");
135
136   // Save its daughter region(s) when split/merge
137   private static final byte[] daughterNamePosCq = Bytes.toBytes("_DAUGHTER_");
138
139   // Save its table name because we only know region's encoded name
140   private static final String tableNamePeer = "_TABLENAME_";
141   private static final byte[] tableNamePosCq = Bytes.toBytes(tableNamePeer);
142
143   static final byte [] META_REGION_PREFIX;
144   static {
145     // Copy the prefix from FIRST_META_REGIONINFO into META_REGION_PREFIX.
146     // FIRST_META_REGIONINFO == 'hbase:meta,,1'.  META_REGION_PREFIX == 'hbase:meta,'
147     int len = HRegionInfo.FIRST_META_REGIONINFO.getRegionName().length - 2;
148     META_REGION_PREFIX = new byte [len];
149     System.arraycopy(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), 0,
150       META_REGION_PREFIX, 0, len);
151   }
152
153   /**
154    * Lists all of the table regions currently in META.
155    * Deprecated, keep there until some test use this.
156    * @param connection what we will use
157    * @param tableName table to list
158    * @return Map of all user-space regions to servers
159    * @throws java.io.IOException
160    * @deprecated use {@link #getTableRegionsAndLocations}, region can have multiple locations
161    */
162   @Deprecated
163   public static NavigableMap<HRegionInfo, ServerName> allTableRegions(
164       Connection connection, final TableName tableName) throws IOException {
165     final NavigableMap<HRegionInfo, ServerName> regions =
166       new TreeMap<HRegionInfo, ServerName>();
167     Visitor visitor = new TableVisitorBase(tableName) {
168       @Override
169       public boolean visitInternal(Result result) throws IOException {
170         RegionLocations locations = getRegionLocations(result);
171         if (locations == null) return true;
172         for (HRegionLocation loc : locations.getRegionLocations()) {
173           if (loc != null) {
174             HRegionInfo regionInfo = loc.getRegionInfo();
175             regions.put(regionInfo, loc.getServerName());
176           }
177         }
178         return true;
179       }
180     };
181     scanMetaForTableRegions(connection, visitor, tableName);
182     return regions;
183   }
184
185   @InterfaceAudience.Private
186   public enum QueryType {
187     ALL(HConstants.TABLE_FAMILY, HConstants.CATALOG_FAMILY),
188     REGION(HConstants.CATALOG_FAMILY),
189     TABLE(HConstants.TABLE_FAMILY);
190
191     private final byte[][] families;
192
193     QueryType(byte[]... families) {
194       this.families = families;
195     }
196
197     byte[][] getFamilies() {
198       return this.families;
199     }
200   }
201
202   /** The delimiter for meta columns for replicaIds &gt; 0 */
203   protected static final char META_REPLICA_ID_DELIMITER = '_';
204
205   /** A regex for parsing server columns from meta. See above javadoc for meta layout */
206   private static final Pattern SERVER_COLUMN_PATTERN
207     = Pattern.compile("^server(_[0-9a-fA-F]{4})?$");
208
209   ////////////////////////
210   // Reading operations //
211   ////////////////////////
212
213   /**
214    * Performs a full scan of <code>hbase:meta</code> for regions.
215    * @param connection connection we're using
216    * @param visitor Visitor invoked against each row in regions family.
217    * @throws IOException
218    */
219   public static void fullScanRegions(Connection connection,
220       final Visitor visitor)
221       throws IOException {
222     scanMeta(connection, null, null, QueryType.REGION, visitor);
223   }
224
225   /**
226    * Performs a full scan of <code>hbase:meta</code> for regions.
227    * @param connection connection we're using
228    * @throws IOException
229    */
230   public static List<Result> fullScanRegions(Connection connection)
231       throws IOException {
232     return fullScan(connection, QueryType.REGION);
233   }
234
235   /**
236    * Performs a full scan of <code>hbase:meta</code> for tables.
237    * @param connection connection we're using
238    * @param visitor Visitor invoked against each row in tables family.
239    * @throws IOException
240    */
241   public static void fullScanTables(Connection connection,
242       final Visitor visitor)
243       throws IOException {
244     scanMeta(connection, null, null, QueryType.TABLE, visitor);
245   }
246
247   /**
248    * Performs a full scan of <code>hbase:meta</code>.
249    * @param connection connection we're using
250    * @param type scanned part of meta
251    * @return List of {@link Result}
252    * @throws IOException
253    */
254   public static List<Result> fullScan(Connection connection, QueryType type)
255     throws IOException {
256     CollectAllVisitor v = new CollectAllVisitor();
257     scanMeta(connection, null, null, type, v);
258     return v.getResults();
259   }
260
261   /**
262    * Callers should call close on the returned {@link Table} instance.
263    * @param connection connection we're using to access Meta
264    * @return An {@link Table} for <code>hbase:meta</code>
265    * @throws IOException
266    */
267   static Table getMetaHTable(final Connection connection)
268   throws IOException {
269     // We used to pass whole CatalogTracker in here, now we just pass in Connection
270     if (connection == null) {
271       throw new NullPointerException("No connection");
272     } else if (connection.isClosed()) {
273       throw new IOException("connection is closed");
274     }
275     return connection.getTable(TableName.META_TABLE_NAME);
276   }
277
278   /**
279    * @param t Table to use (will be closed when done).
280    * @param g Get to run
281    * @throws IOException
282    */
283   private static Result get(final Table t, final Get g) throws IOException {
284     if (t == null) return null;
285     try {
286       return t.get(g);
287     } finally {
288       t.close();
289     }
290   }
291
292   /**
293    * Gets the region info and assignment for the specified region.
294    * @param connection connection we're using
295    * @param regionName Region to lookup.
296    * @return Location and HRegionInfo for <code>regionName</code>
297    * @throws IOException
298    * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
299    */
300   @Deprecated
301   public static Pair<HRegionInfo, ServerName> getRegion(Connection connection, byte [] regionName)
302     throws IOException {
303     HRegionLocation location = getRegionLocation(connection, regionName);
304     return location == null
305       ? null
306       : new Pair<HRegionInfo, ServerName>(location.getRegionInfo(), location.getServerName());
307   }
308
309   /**
310    * Returns the HRegionLocation from meta for the given region
311    * @param connection connection we're using
312    * @param regionName region we're looking for
313    * @return HRegionLocation for the given region
314    * @throws IOException
315    */
316   public static HRegionLocation getRegionLocation(Connection connection,
317                                                   byte[] regionName) throws IOException {
318     byte[] row = regionName;
319     HRegionInfo parsedInfo = null;
320     try {
321       parsedInfo = parseRegionInfoFromRegionName(regionName);
322       row = getMetaKeyForRegion(parsedInfo);
323     } catch (Exception parseEx) {
324       ; // Ignore. This is used with tableName passed as regionName.
325     }
326     Get get = new Get(row);
327     get.addFamily(HConstants.CATALOG_FAMILY);
328     Result r = get(getMetaHTable(connection), get);
329     RegionLocations locations = getRegionLocations(r);
330     return locations == null
331       ? null
332       : locations.getRegionLocation(parsedInfo == null ? 0 : parsedInfo.getReplicaId());
333   }
334
335   /**
336    * Returns the HRegionLocation from meta for the given region
337    * @param connection connection we're using
338    * @param regionInfo region information
339    * @return HRegionLocation for the given region
340    * @throws IOException
341    */
342   public static HRegionLocation getRegionLocation(Connection connection,
343                                                   HRegionInfo regionInfo) throws IOException {
344     byte[] row = getMetaKeyForRegion(regionInfo);
345     Get get = new Get(row);
346     get.addFamily(HConstants.CATALOG_FAMILY);
347     Result r = get(getMetaHTable(connection), get);
348     return getRegionLocation(r, regionInfo, regionInfo.getReplicaId());
349   }
350
351   /** Returns the row key to use for this regionInfo */
352   public static byte[] getMetaKeyForRegion(HRegionInfo regionInfo) {
353     return RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo).getRegionName();
354   }
355
356   /** Returns an HRI parsed from this regionName. Not all the fields of the HRI
357    * is stored in the name, so the returned object should only be used for the fields
358    * in the regionName.
359    */
360   protected static HRegionInfo parseRegionInfoFromRegionName(byte[] regionName)
361     throws IOException {
362     byte[][] fields = HRegionInfo.parseRegionName(regionName);
363     long regionId =  Long.parseLong(Bytes.toString(fields[2]));
364     int replicaId = fields.length > 3 ? Integer.parseInt(Bytes.toString(fields[3]), 16) : 0;
365     return new HRegionInfo(
366       TableName.valueOf(fields[0]), fields[1], fields[1], false, regionId, replicaId);
367   }
368
369   /**
370    * Gets the result in hbase:meta for the specified region.
371    * @param connection connection we're using
372    * @param regionName region we're looking for
373    * @return result of the specified region
374    * @throws IOException
375    */
376   public static Result getRegionResult(Connection connection,
377       byte[] regionName) throws IOException {
378     Get get = new Get(regionName);
379     get.addFamily(HConstants.CATALOG_FAMILY);
380     return get(getMetaHTable(connection), get);
381   }
382
383   /**
384    * Get regions from the merge qualifier of the specified merged region
385    * @return null if it doesn't contain merge qualifier, else two merge regions
386    * @throws IOException
387    */
388   @Nullable
389   public static Pair<HRegionInfo, HRegionInfo> getRegionsFromMergeQualifier(
390       Connection connection, byte[] regionName) throws IOException {
391     Result result = getRegionResult(connection, regionName);
392     HRegionInfo mergeA = getHRegionInfo(result, HConstants.MERGEA_QUALIFIER);
393     HRegionInfo mergeB = getHRegionInfo(result, HConstants.MERGEB_QUALIFIER);
394     if (mergeA == null && mergeB == null) {
395       return null;
396     }
397     return new Pair<HRegionInfo, HRegionInfo>(mergeA, mergeB);
398  }
399
400   /**
401    * Checks if the specified table exists.  Looks at the hbase:meta table hosted on
402    * the specified server.
403    * @param connection connection we're using
404    * @param tableName table to check
405    * @return true if the table exists in meta, false if not
406    * @throws IOException
407    */
408   public static boolean tableExists(Connection connection,
409       final TableName tableName)
410   throws IOException {
411     // Catalog tables always exist.
412     return tableName.equals(TableName.META_TABLE_NAME)
413         || getTableState(connection, tableName) != null;
414   }
415
416   /**
417    * Lists all of the regions currently in META.
418    *
419    * @param connection to connect with
420    * @param excludeOfflinedSplitParents False if we are to include offlined/splitparents regions,
421    *                                    true and we'll leave out offlined regions from returned list
422    * @return List of all user-space regions.
423    * @throws IOException
424    */
425   @VisibleForTesting
426   public static List<HRegionInfo> getAllRegions(Connection connection,
427       boolean excludeOfflinedSplitParents)
428       throws IOException {
429     List<Pair<HRegionInfo, ServerName>> result;
430
431     result = getTableRegionsAndLocations(connection, null,
432         excludeOfflinedSplitParents);
433
434     return getListOfHRegionInfos(result);
435
436   }
437
438   /**
439    * Gets all of the regions of the specified table. Do not use this method
440    * to get meta table regions, use methods in MetaTableLocator instead.
441    * @param connection connection we're using
442    * @param tableName table we're looking for
443    * @return Ordered list of {@link HRegionInfo}.
444    * @throws IOException
445    */
446   public static List<HRegionInfo> getTableRegions(Connection connection, TableName tableName)
447   throws IOException {
448     return getTableRegions(connection, tableName, false);
449   }
450
451   /**
452    * Gets all of the regions of the specified table. Do not use this method
453    * to get meta table regions, use methods in MetaTableLocator instead.
454    * @param connection connection we're using
455    * @param tableName table we're looking for
456    * @param excludeOfflinedSplitParents If true, do not include offlined split
457    * parents in the return.
458    * @return Ordered list of {@link HRegionInfo}.
459    * @throws IOException
460    */
461   public static List<HRegionInfo> getTableRegions(Connection connection,
462       TableName tableName, final boolean excludeOfflinedSplitParents)
463   throws IOException {
464     List<Pair<HRegionInfo, ServerName>> result =
465         getTableRegionsAndLocations(connection, tableName, excludeOfflinedSplitParents);
466     return getListOfHRegionInfos(result);
467   }
468
469   @Nullable
470   static List<HRegionInfo> getListOfHRegionInfos(final List<Pair<HRegionInfo, ServerName>> pairs) {
471     if (pairs == null || pairs.isEmpty()) return null;
472     List<HRegionInfo> result = new ArrayList<HRegionInfo>(pairs.size());
473     for (Pair<HRegionInfo, ServerName> pair: pairs) {
474       result.add(pair.getFirst());
475     }
476     return result;
477   }
478
479   /**
480    * @param current region of current table we're working with
481    * @param tableName table we're checking against
482    * @return True if <code>current</code> tablename is equal to
483    * <code>tableName</code>
484    */
485   static boolean isInsideTable(final HRegionInfo current, final TableName tableName) {
486     return tableName.equals(current.getTable());
487   }
488
489   /**
490    * @param tableName table we're working with
491    * @return start row for scanning META according to query type
492    */
493   public static byte[] getTableStartRowForMeta(TableName tableName, QueryType type) {
494     if (tableName == null) {
495       return null;
496     }
497     switch (type) {
498     case REGION:
499       byte[] startRow = new byte[tableName.getName().length + 2];
500       System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length);
501       startRow[startRow.length - 2] = HConstants.DELIMITER;
502       startRow[startRow.length - 1] = HConstants.DELIMITER;
503       return startRow;
504     case ALL:
505     case TABLE:
506     default:
507       return tableName.getName();
508     }
509   }
510
511   /**
512    * @param tableName table we're working with
513    * @return stop row for scanning META according to query type
514    */
515   public static byte[] getTableStopRowForMeta(TableName tableName, QueryType type) {
516     if (tableName == null) {
517       return null;
518     }
519     final byte[] stopRow;
520     switch (type) {
521     case REGION:
522       stopRow = new byte[tableName.getName().length + 3];
523       System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
524       stopRow[stopRow.length - 3] = ' ';
525       stopRow[stopRow.length - 2] = HConstants.DELIMITER;
526       stopRow[stopRow.length - 1] = HConstants.DELIMITER;
527       break;
528     case ALL:
529     case TABLE:
530     default:
531       stopRow = new byte[tableName.getName().length + 1];
532       System.arraycopy(tableName.getName(), 0, stopRow, 0, tableName.getName().length);
533       stopRow[stopRow.length - 1] = ' ';
534       break;
535     }
536     return stopRow;
537   }
538 
539   /**
540    * This method creates a Scan object that will only scan catalog rows that
541    * belong to the specified table. It doesn't specify any columns.
542    * This is a better alternative to just using a start row and scan until
543    * it hits a new table since that requires parsing the HRI to get the table
544    * name.
545    * @param tableName bytes of table's name
546    * @return configured Scan object
547    */
548   @Deprecated
549   public static Scan getScanForTableName(Connection connection, TableName tableName) {
550     // Start key is just the table name with delimiters
551     byte[] startKey = getTableStartRowForMeta(tableName, QueryType.REGION);
552     // Stop key appends the smallest possible char to the table name
553     byte[] stopKey = getTableStopRowForMeta(tableName, QueryType.REGION);
554
555     Scan scan = getMetaScan(connection);
556     scan.setStartRow(startKey);
557     scan.setStopRow(stopKey);
558     return scan;
559   }
560
561   private static Scan getMetaScan(Connection connection) {
562     return getMetaScan(connection, Integer.MAX_VALUE);
563   }
564
565   private static Scan getMetaScan(Connection connection, int rowUpperLimit) {
566     Scan scan = new Scan();
567     int scannerCaching = connection.getConfiguration()
568         .getInt(HConstants.HBASE_META_SCANNER_CACHING,
569             HConstants.DEFAULT_HBASE_META_SCANNER_CACHING);
570     if (connection.getConfiguration().getBoolean(HConstants.USE_META_REPLICAS,
571         HConstants.DEFAULT_USE_META_REPLICAS)) {
572       scan.setConsistency(Consistency.TIMELINE);
573     }
574     if (rowUpperLimit <= scannerCaching) {
575       scan.setSmall(true);
576     }
577     int rows = Math.min(rowUpperLimit, scannerCaching);
578     scan.setCaching(rows);
579     return scan;
580   }
581   /**
582    * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
583    * @param connection connection we're using
584    * @param tableName table we're looking for
585    * @return Return list of regioninfos and server.
586    * @throws IOException
587    */
588   public static List<Pair<HRegionInfo, ServerName>>
589     getTableRegionsAndLocations(Connection connection, TableName tableName)
590       throws IOException {
591     return getTableRegionsAndLocations(connection, tableName, true);
592   }
593
594   /**
595    * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
596    * @param connection connection we're using
597    * @param tableName table to work with, can be null for getting all regions
598    * @param excludeOfflinedSplitParents don't return split parents
599    * @return Return list of regioninfos and server addresses.
600    * @throws IOException
601    */
602   public static List<Pair<HRegionInfo, ServerName>> getTableRegionsAndLocations(
603       Connection connection, @Nullable final TableName tableName,
604       final boolean excludeOfflinedSplitParents) throws IOException {
605     if (tableName != null && tableName.equals(TableName.META_TABLE_NAME)) {
606       throw new IOException("This method can't be used to locate meta regions;"
607         + " use MetaTableLocator instead");
608     }
609     // Make a version of CollectingVisitor that collects HRegionInfo and ServerAddress
610     CollectingVisitor<Pair<HRegionInfo, ServerName>> visitor =
611       new CollectingVisitor<Pair<HRegionInfo, ServerName>>() {
612         private RegionLocations current = null;
613
614         @Override
615         public boolean visit(Result r) throws IOException {
616           current = getRegionLocations(r);
617           if (current == null || current.getRegionLocation().getRegionInfo() == null) {
618             LOG.warn("No serialized HRegionInfo in " + r);
619             return true;
620           }
621           HRegionInfo hri = current.getRegionLocation().getRegionInfo();
622           if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
623           // Else call super and add this Result to the collection.
624           return super.visit(r);
625         }
626
627         @Override
628         void add(Result r) {
629           if (current == null) {
630             return;
631           }
632           for (HRegionLocation loc : current.getRegionLocations()) {
633             if (loc != null) {
634               this.results.add(new Pair<HRegionInfo, ServerName>(
635                 loc.getRegionInfo(), loc.getServerName()));
636             }
637           }
638         }
639       };
640     scanMeta(connection,
641         getTableStartRowForMeta(tableName, QueryType.REGION),
642         getTableStopRowForMeta(tableName, QueryType.REGION),
643         QueryType.REGION, visitor);
644     return visitor.getResults();
645   }
646
647   /**
648    * @param connection connection we're using
649    * @param serverName server whose regions we're interested in
650    * @return List of user regions installed on this server (does not include
651    * catalog regions).
652    * @throws IOException
653    */
654   public static NavigableMap<HRegionInfo, Result>
655   getServerUserRegions(Connection connection, final ServerName serverName)
656     throws IOException {
657     final NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
658     // Fill the above hris map with entries from hbase:meta that have the passed
659     // servername.
660     CollectingVisitor<Result> v = new CollectingVisitor<Result>() {
661       @Override
662       void add(Result r) {
663         if (r == null || r.isEmpty()) return;
664         RegionLocations locations = getRegionLocations(r);
665         if (locations == null) return;
666         for (HRegionLocation loc : locations.getRegionLocations()) {
667           if (loc != null) {
668             if (loc.getServerName() != null && loc.getServerName().equals(serverName)) {
669               hris.put(loc.getRegionInfo(), r);
670             }
671           }
672         }
673       }
674     };
675     scanMeta(connection, null, null, QueryType.REGION, v);
676     return hris;
677   }
678
679   public static void fullScanMetaAndPrint(Connection connection)
680     throws IOException {
681     Visitor v = new Visitor() {
682       @Override
683       public boolean visit(Result r) throws IOException {
684         if (r ==  null || r.isEmpty()) return true;
685         LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
686         TableState state = getTableState(r);
687         if (state != null) {
688           LOG.info("Table State: " + state);
689         } else {
690           RegionLocations locations = getRegionLocations(r);
691           if (locations == null) return true;
692           for (HRegionLocation loc : locations.getRegionLocations()) {
693             if (loc != null) {
694               LOG.info("fullScanMetaAndPrint.HRI Print= " + loc.getRegionInfo());
695             }
696           }
697         }
698         return true;
699       }
700     };
701     scanMeta(connection, null, null, QueryType.ALL, v);
702   }
703
704   public static void scanMetaForTableRegions(Connection connection,
705       Visitor visitor, TableName tableName) throws IOException {
706     scanMeta(connection, tableName, QueryType.REGION, Integer.MAX_VALUE, visitor);
707   }
708
709   public static void scanMeta(Connection connection, TableName table,
710       QueryType type, int maxRows, final Visitor visitor) throws IOException {
711     scanMeta(connection, getTableStartRowForMeta(table, type), getTableStopRowForMeta(table, type),
712         type, maxRows, visitor);
713   }
714
715   public static void scanMeta(Connection connection,
716       @Nullable final byte[] startRow, @Nullable final byte[] stopRow,
717       QueryType type, final Visitor visitor) throws IOException {
718     scanMeta(connection, startRow, stopRow, type, Integer.MAX_VALUE, visitor);
719   }
720 
721   /**
722    * Performs a scan of META table for given table starting from
723    * given row.
724    *
725    * @param connection connection we're using
726    * @param visitor    visitor to call
727    * @param tableName  table withing we scan
728    * @param row        start scan from this row
729    * @param rowLimit   max number of rows to return
730    * @throws IOException
731    */
732   public static void scanMeta(Connection connection,
733       final Visitor visitor, final TableName tableName,
734       final byte[] row, final int rowLimit)
735       throws IOException {
736
737     byte[] startRow = null;
738     byte[] stopRow = null;
739     if (tableName != null) {
740       startRow =
741           getTableStartRowForMeta(tableName, QueryType.REGION);
742       if (row != null) {
743         HRegionInfo closestRi =
744             getClosestRegionInfo(connection, tableName, row);
745         startRow = HRegionInfo
746             .createRegionName(tableName, closestRi.getStartKey(), HConstants.ZEROES, false);
747       }
748       stopRow =
749           getTableStopRowForMeta(tableName, QueryType.REGION);
750     }
751     scanMeta(connection, startRow, stopRow, QueryType.REGION, rowLimit, visitor);
752   }
753
754
755   /**
756    * Performs a scan of META table.
757    * @param connection connection we're using
758    * @param startRow Where to start the scan. Pass null if want to begin scan
759    *                 at first row.
760    * @param stopRow Where to stop the scan. Pass null if want to scan all rows
761    *                from the start one
762    * @param type scanned part of meta
763    * @param maxRows maximum rows to return
764    * @param visitor Visitor invoked against each row.
765    * @throws IOException
766    */
767   public static void scanMeta(Connection connection,
768       @Nullable final byte[] startRow, @Nullable final byte[] stopRow,
769       QueryType type, int maxRows, final Visitor visitor)
770   throws IOException {
771     int rowUpperLimit = maxRows > 0 ? maxRows : Integer.MAX_VALUE;
772     Scan scan = getMetaScan(connection, rowUpperLimit);
773
774     for (byte[] family : type.getFamilies()) {
775       scan.addFamily(family);
776     }
777     if (startRow != null) scan.setStartRow(startRow);
778     if (stopRow != null) scan.setStopRow(stopRow);
779
780     if (LOG.isTraceEnabled()) {
781       LOG.trace("Scanning META"
782           + " starting at row=" + Bytes.toStringBinary(startRow)
783           + " stopping at row=" + Bytes.toStringBinary(stopRow)
784           + " for max=" + rowUpperLimit
785           + " with caching=" + scan.getCaching());
786     }
787
788     int currentRow = 0;
789     try (Table metaTable = getMetaHTable(connection)) {
790       try (ResultScanner scanner = metaTable.getScanner(scan)) {
791         Result data;
792         while ((data = scanner.next()) != null) {
793           if (data.isEmpty()) continue;
794           // Break if visit returns false.
795           if (!visitor.visit(data)) break;
796           if (++currentRow >= rowUpperLimit) break;
797         }
798       }
799     }
800     if (visitor != null && visitor instanceof Closeable) {
801       try {
802         ((Closeable) visitor).close();
803       } catch (Throwable t) {
804         ExceptionUtil.rethrowIfInterrupt(t);
805         LOG.debug("Got exception in closing the meta scanner visitor", t);
806       }
807     }
808   }
809
810   /**
811    * @return Get closest metatable region row to passed <code>row</code>
812    * @throws java.io.IOException
813    */
814   @NonNull
815   public static HRegionInfo getClosestRegionInfo(Connection connection,
816       @NonNull final TableName tableName,
817       @NonNull final byte[] row)
818       throws IOException {
819     byte[] searchRow = HRegionInfo.createRegionName(tableName, row, HConstants.NINES, false);
820     Scan scan = getMetaScan(connection, 1);
821     scan.setReversed(true);
822     scan.setStartRow(searchRow);
823     try (ResultScanner resultScanner = getMetaHTable(connection).getScanner(scan)) {
824       Result result = resultScanner.next();
825       if (result == null) {
826         throw new TableNotFoundException("Cannot find row in META " +
827             " for table: " + tableName + ", row=" + Bytes.toStringBinary(row));
828       }
829       HRegionInfo regionInfo = getHRegionInfo(result);
830       if (regionInfo == null) {
831         throw new IOException("HRegionInfo was null or empty in Meta for " +
832             tableName + ", row=" + Bytes.toStringBinary(row));
833       }
834       return regionInfo;
835     }
836   }
837 
838   /**
839    * Returns the column family used for meta columns.
840    * @return HConstants.CATALOG_FAMILY.
841    */
842   protected static byte[] getCatalogFamily() {
843     return HConstants.CATALOG_FAMILY;
844   }
845 
846   /**
847    * Returns the column family used for table columns.
848    * @return HConstants.TABLE_FAMILY.
849    */
850   protected static byte[] getTableFamily() {
851     return HConstants.TABLE_FAMILY;
852   }
853
854   /**
855    * Returns the column qualifier for serialized region info
856    * @return HConstants.REGIONINFO_QUALIFIER
857    */
858   protected static byte[] getRegionInfoColumn() {
859     return HConstants.REGIONINFO_QUALIFIER;
860   }
861
862   /**
863    * Returns the column qualifier for serialized table state
864    *
865    * @return HConstants.TABLE_STATE_QUALIFIER
866    */
867   protected static byte[] getStateColumn() {
868     return HConstants.TABLE_STATE_QUALIFIER;
869   }
870
871   /**
872    * Returns the column qualifier for server column for replicaId
873    * @param replicaId the replicaId of the region
874    * @return a byte[] for server column qualifier
875    */
876   @VisibleForTesting
877   public static byte[] getServerColumn(int replicaId) {
878     return replicaId == 0
879       ? HConstants.SERVER_QUALIFIER
880       : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
881       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
882   }
883
884   /**
885    * Returns the column qualifier for server start code column for replicaId
886    * @param replicaId the replicaId of the region
887    * @return a byte[] for server start code column qualifier
888    */
889   @VisibleForTesting
890   public static byte[] getStartCodeColumn(int replicaId) {
891     return replicaId == 0
892       ? HConstants.STARTCODE_QUALIFIER
893       : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
894       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
895   }
896
897   /**
898    * Returns the column qualifier for seqNum column for replicaId
899    * @param replicaId the replicaId of the region
900    * @return a byte[] for seqNum column qualifier
901    */
902   @VisibleForTesting
903   public static byte[] getSeqNumColumn(int replicaId) {
904     return replicaId == 0
905       ? HConstants.SEQNUM_QUALIFIER
906       : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
907       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
908   }
909
910   /**
911    * Parses the replicaId from the server column qualifier. See top of the class javadoc
912    * for the actual meta layout
913    * @param serverColumn the column qualifier
914    * @return an int for the replicaId
915    */
916   @VisibleForTesting
917   static int parseReplicaIdFromServerColumn(byte[] serverColumn) {
918     String serverStr = Bytes.toString(serverColumn);
919
920     Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr);
921     if (matcher.matches() && matcher.groupCount() > 0) {
922       String group = matcher.group(1);
923       if (group != null && group.length() > 0) {
924         return Integer.parseInt(group.substring(1), 16);
925       } else {
926         return 0;
927       }
928     }
929     return -1;
930   }
931
932   /**
933    * Returns a {@link ServerName} from catalog table {@link Result}.
934    * @param r Result to pull from
935    * @return A ServerName instance or null if necessary fields not found or empty.
936    */
937   @Nullable
938   @InterfaceAudience.Private // for use by HMaster#getTableRegionRow which is used for testing only
939   public static ServerName getServerName(final Result r, final int replicaId) {
940     byte[] serverColumn = getServerColumn(replicaId);
941     Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn);
942     if (cell == null || cell.getValueLength() == 0) return null;
943     String hostAndPort = Bytes.toString(
944       cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
945     byte[] startcodeColumn = getStartCodeColumn(replicaId);
946     cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn);
947     if (cell == null || cell.getValueLength() == 0) return null;
948     try {
949       return ServerName.valueOf(hostAndPort,
950           Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
951     } catch (IllegalArgumentException e) {
952       LOG.error("Ignoring invalid region for server " + hostAndPort + "; cell=" + cell, e);
953       return null;
954     }
955   }
956
957   /**
958    * The latest seqnum that the server writing to meta observed when opening the region.
959    * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written.
960    * @param r Result to pull the seqNum from
961    * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
962    */
963   private static long getSeqNumDuringOpen(final Result r, final int replicaId) {
964     Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId));
965     if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM;
966     return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
967   }
968 
969   /**
970    * Returns an HRegionLocationList extracted from the result.
971    * @return an HRegionLocationList containing all locations for the region range or null if
972    *  we can't deserialize the result.
973    */
974   @Nullable
975   public static RegionLocations getRegionLocations(final Result r) {
976     if (r == null) return null;
977     HRegionInfo regionInfo = getHRegionInfo(r, getRegionInfoColumn());
978     if (regionInfo == null) return null;
979 
980     List<HRegionLocation> locations = new ArrayList<HRegionLocation>(1);
981     NavigableMap<byte[],NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap();
982
983     locations.add(getRegionLocation(r, regionInfo, 0));
984
985     NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily());
986     if (infoMap == null) return new RegionLocations(locations);
987
988     // iterate until all serverName columns are seen
989     int replicaId = 0;
990     byte[] serverColumn = getServerColumn(replicaId);
991     SortedMap<byte[], byte[]> serverMap = null;
992     serverMap = infoMap.tailMap(serverColumn, false);
993
994     if (serverMap.isEmpty()) return new RegionLocations(locations);
995
996     for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) {
997       replicaId = parseReplicaIdFromServerColumn(entry.getKey());
998       if (replicaId < 0) {
999         break;
1000       }
1001       HRegionLocation location = getRegionLocation(r, regionInfo, replicaId);
1002       // In case the region replica is newly created, it's location might be null. We usually do not
1003       // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs.
1004       if (location == null || location.getServerName() == null) {
1005         locations.add(null);
1006       } else {
1007         locations.add(location);
1008       }
1009     }
1010
1011     return new RegionLocations(locations);
1012   }
1013
1014   /**
1015    * Returns the HRegionLocation parsed from the given meta row Result
1016    * for the given regionInfo and replicaId. The regionInfo can be the default region info
1017    * for the replica.
1018    * @param r the meta row result
1019    * @param regionInfo RegionInfo for default replica
1020    * @param replicaId the replicaId for the HRegionLocation
1021    * @return HRegionLocation parsed from the given meta row Result for the given replicaId
1022    */
1023   private static HRegionLocation getRegionLocation(final Result r, final HRegionInfo regionInfo,
1024                                                    final int replicaId) {
1025     ServerName serverName = getServerName(r, replicaId);
1026     long seqNum = getSeqNumDuringOpen(r, replicaId);
1027     HRegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId);
1028     return new HRegionLocation(replicaInfo, serverName, seqNum);
1029   }
1030
1031   /**
1032    * Returns HRegionInfo object from the column
1033    * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
1034    * table Result.
1035    * @param data a Result object from the catalog table scan
1036    * @return HRegionInfo or null
1037    */
1038   public static HRegionInfo getHRegionInfo(Result data) {
1039     return getHRegionInfo(data, HConstants.REGIONINFO_QUALIFIER);
1040   }
1041
1042   /**
1043    * Returns the HRegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
1044    * <code>qualifier</code> of the catalog table result.
1045    * @param r a Result object from the catalog table scan
1046    * @param qualifier Column family qualifier
1047    * @return An HRegionInfo instance or null.
1048    */
1049   @Nullable
1050   private static HRegionInfo getHRegionInfo(final Result r, byte [] qualifier) {
1051     Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier);
1052     if (cell == null) return null;
1053     return HRegionInfo.parseFromOrNull(cell.getValueArray(),
1054       cell.getValueOffset(), cell.getValueLength());
1055   }
1056
1057   /**
1058    * Returns the daughter regions by reading the corresponding columns of the catalog table
1059    * Result.
1060    * @param data a Result object from the catalog table scan
1061    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
1062    * parent
1063    */
1064   public static PairOfSameType<HRegionInfo> getDaughterRegions(Result data) {
1065     HRegionInfo splitA = getHRegionInfo(data, HConstants.SPLITA_QUALIFIER);
1066     HRegionInfo splitB = getHRegionInfo(data, HConstants.SPLITB_QUALIFIER);
1067
1068     return new PairOfSameType<HRegionInfo>(splitA, splitB);
1069   }
1070
1071   /**
1072    * Returns the merge regions by reading the corresponding columns of the catalog table
1073    * Result.
1074    * @param data a Result object from the catalog table scan
1075    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
1076    * parent
1077    */
1078   public static PairOfSameType<HRegionInfo> getMergeRegions(Result data) {
1079     HRegionInfo mergeA = getHRegionInfo(data, HConstants.MERGEA_QUALIFIER);
1080     HRegionInfo mergeB = getHRegionInfo(data, HConstants.MERGEB_QUALIFIER);
1081
1082     return new PairOfSameType<HRegionInfo>(mergeA, mergeB);
1083   }
1084
1085   /**
1086    * Fetch table state for given table from META table
1087    * @param conn connection to use
1088    * @param tableName table to fetch state for
1089    * @return state
1090    * @throws IOException
1091    */
1092   @Nullable
1093   public static TableState getTableState(Connection conn, TableName tableName)
1094       throws IOException {
1095     Table metaHTable = getMetaHTable(conn);
1096     Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getStateColumn());
1097     long time = EnvironmentEdgeManager.currentTime();
1098     get.setTimeRange(0, time);
1099     Result result =
1100         metaHTable.get(get);
1101     return getTableState(result);
1102   }
1103
1104   /**
1105    * Fetch table states from META table
1106    * @param conn connection to use
1107    * @return map {tableName -&gt; state}
1108    * @throws IOException
1109    */
1110   public static Map<TableName, TableState> getTableStates(Connection conn)
1111       throws IOException {
1112     final Map<TableName, TableState> states = new LinkedHashMap<>();
1113     Visitor collector = new Visitor() {
1114       @Override
1115       public boolean visit(Result r) throws IOException {
1116         TableState state = getTableState(r);
1117         if (state != null)
1118           states.put(state.getTableName(), state);
1119         return true;
1120       }
1121     };
1122     fullScanTables(conn, collector);
1123     return states;
1124   }
1125
1126   /**
1127    * Updates state in META
1128    * @param conn connection to use
1129    * @param tableName table to look for
1130    * @throws IOException
1131    */
1132   public static void updateTableState(Connection conn, TableName tableName,
1133       TableState.State actual) throws IOException {
1134     updateTableState(conn, new TableState(tableName, actual));
1135   }
1136
1137   /**
1138    * Decode table state from META Result.
1139    * Should contain cell from HConstants.TABLE_FAMILY
1140    * @param r result
1141    * @return null if not found
1142    * @throws IOException
1143    */
1144   @Nullable
1145   public static TableState getTableState(Result r)
1146       throws IOException {
1147     Cell cell = r.getColumnLatestCell(getTableFamily(), getStateColumn());
1148     if (cell == null) return null;
1149     try {
1150       return TableState.parseFrom(TableName.valueOf(r.getRow()),
1151           Arrays.copyOfRange(cell.getValueArray(),
1152           cell.getValueOffset(), cell.getValueOffset() + cell.getValueLength()));
1153     } catch (DeserializationException e) {
1154       throw new IOException(e);
1155     }
1156
1157   }
1158
1159   /**
1160    * Implementations 'visit' a catalog table row.
1161    */
1162   public interface Visitor {
1163     /**
1164      * Visit the catalog table row.
1165      * @param r A row from catalog table
1166      * @return True if we are to proceed scanning the table, else false if
1167      * we are to stop now.
1168      */
1169     boolean visit(final Result r) throws IOException;
1170   }
1171
1172   /**
1173    * Implementations 'visit' a catalog table row but with close() at the end.
1174    */
1175   public interface CloseableVisitor extends Visitor, Closeable {
1176   }
1177
1178   /**
1179    * A {@link Visitor} that collects content out of passed {@link Result}.
1180    */
1181   static abstract class CollectingVisitor<T> implements Visitor {
1182     final List<T> results = new ArrayList<T>();
1183     @Override
1184     public boolean visit(Result r) throws IOException {
1185       if (r ==  null || r.isEmpty()) return true;
1186       add(r);
1187       return true;
1188     }
1189
1190     abstract void add(Result r);
1191
1192     /**
1193      * @return Collected results; wait till visits complete to collect all
1194      * possible results
1195      */
1196     List<T> getResults() {
1197       return this.results;
1198     }
1199   }
1200
1201   /**
1202    * Collects all returned.
1203    */
1204   static class CollectAllVisitor extends CollectingVisitor<Result> {
1205     @Override
1206     void add(Result r) {
1207       this.results.add(r);
1208     }
1209   }
1210
1211   /**
1212    * A Visitor that skips offline regions and split parents
1213    */
1214   public static abstract class DefaultVisitorBase implements Visitor {
1215
1216     public DefaultVisitorBase() {
1217       super();
1218     }
1219
1220     public abstract boolean visitInternal(Result rowResult) throws IOException;
1221
1222     @Override
1223     public boolean visit(Result rowResult) throws IOException {
1224       HRegionInfo info = getHRegionInfo(rowResult);
1225       if (info == null) {
1226         return true;
1227       }
1228
1229       //skip over offline and split regions
1230       if (!(info.isOffline() || info.isSplit())) {
1231         return visitInternal(rowResult);
1232       }
1233       return true;
1234     }
1235   }
1236
1237   /**
1238    * A Visitor for a table. Provides a consistent view of the table's
1239    * hbase:meta entries during concurrent splits (see HBASE-5986 for details). This class
1240    * does not guarantee ordered traversal of meta entries, and can block until the
1241    * hbase:meta entries for daughters are available during splits.
1242    */
1243   public static abstract class TableVisitorBase extends DefaultVisitorBase {
1244     private TableName tableName;
1245
1246     public TableVisitorBase(TableName tableName) {
1247       super();
1248       this.tableName = tableName;
1249     }
1250
1251     @Override
1252     public final boolean visit(Result rowResult) throws IOException {
1253       HRegionInfo info = getHRegionInfo(rowResult);
1254       if (info == null) {
1255         return true;
1256       }
1257       if (!(info.getTable().equals(tableName))) {
1258         return false;
1259       }
1260       return super.visit(rowResult);
1261     }
1262   }
1263
1264   /**
1265    * Count regions in <code>hbase:meta</code> for passed table.
1266    * @param c Configuration object
1267    * @param tableName table name to count regions for
1268    * @return Count or regions in table <code>tableName</code>
1269    * @throws IOException
1270    */
1271   public static int getRegionCount(final Configuration c, final TableName tableName)
1272   throws IOException {
1273     try (Connection connection = ConnectionFactory.createConnection(c)) {
1274       return getRegionCount(connection, tableName);
1275     }
1276   }
1277
1278   /**
1279    * Count regions in <code>hbase:meta</code> for passed table.
1280    * @param connection Connection object
1281    * @param tableName table name to count regions for
1282    * @return Count or regions in table <code>tableName</code>
1283    * @throws IOException
1284    */
1285   public static int getRegionCount(final Connection connection, final TableName tableName)
1286   throws IOException {
1287     try (RegionLocator locator = connection.getRegionLocator(tableName)) {
1288       List<HRegionLocation> locations = locator.getAllRegionLocations();
1289       return locations == null? 0: locations.size();
1290     }
1291   }
1292
1293   ////////////////////////
1294   // Editing operations //
1295   ////////////////////////
1296
1297   /**
1298    * Generates and returns a Put containing the region into for the catalog table
1299    */
1300   public static Put makePutFromRegionInfo(HRegionInfo regionInfo)
1301     throws IOException {
1302     return makePutFromRegionInfo(regionInfo, EnvironmentEdgeManager.currentTime());
1303   }
1304
1305   /**
1306    * Generates and returns a Put containing the region into for the catalog table
1307    */
1308   public static Put makePutFromRegionInfo(HRegionInfo regionInfo, long ts)
1309     throws IOException {
1310     Put put = new Put(regionInfo.getRegionName(), ts);
1311     addRegionInfo(put, regionInfo);
1312     return put;
1313   }
1314
1315   /**
1316    * Generates and returns a Delete containing the region info for the catalog
1317    * table
1318    */
1319   public static Delete makeDeleteFromRegionInfo(HRegionInfo regionInfo) {
1320     long now = EnvironmentEdgeManager.currentTime();
1321     return makeDeleteFromRegionInfo(regionInfo, now);
1322   }
1323
1324   /**
1325    * Generates and returns a Delete containing the region info for the catalog
1326    * table
1327    */
1328   public static Delete makeDeleteFromRegionInfo(HRegionInfo regionInfo, long ts) {
1329     if (regionInfo == null) {
1330       throw new IllegalArgumentException("Can't make a delete for null region");
1331     }
1332     Delete delete = new Delete(regionInfo.getRegionName());
1333     delete.addFamily(getCatalogFamily(), ts);
1334     return delete;
1335   }
1336
1337   public static Put makeBarrierPut(byte[] encodedRegionName, long seq, byte[] tableName) {
1338     byte[] seqBytes = Bytes.toBytes(seq);
1339     return new Put(encodedRegionName)
1340         .addImmutable(HConstants.REPLICATION_BARRIER_FAMILY, seqBytes, seqBytes)
1341         .addImmutable(HConstants.REPLICATION_POSITION_FAMILY, tableNamePosCq, tableName);
1342   }
1343
1344
1345   public static Put makeSerialDaughterPut(byte[] encodedRegionName, byte[] value) {
1346     return new Put(encodedRegionName).addImmutable(HConstants.REPLICATION_POSITION_FAMILY,
1347         daughterNamePosCq, value);
1348   }
1349
1350   /**
1351    * Adds split daughters to the Put
1352    */
1353   public static Put addDaughtersToPut(Put put, HRegionInfo splitA, HRegionInfo splitB) {
1354     if (splitA != null) {
1355       put.addImmutable(
1356         HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, splitA.toByteArray());
1357     }
1358     if (splitB != null) {
1359       put.addImmutable(
1360         HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, splitB.toByteArray());
1361     }
1362     return put;
1363   }
1364
1365   /**
1366    * Put the passed <code>puts</code> to the <code>hbase:meta</code> table.
1367    * Non-atomic for multi puts.
1368    * @param connection connection we're using
1369    * @param puts Put to add to hbase:meta
1370    * @throws IOException
1371    */
1372   static void putToMetaTable(final Connection connection, final Put... puts)
1373     throws IOException {
1374     put(getMetaHTable(connection), Arrays.asList(puts));
1375   }
1376
1377   /**
1378    * @param t Table to use (will be closed when done).
1379    * @param puts puts to make
1380    * @throws IOException
1381    */
1382   private static void put(final Table t, final List<Put> puts) throws IOException {
1383     try {
1384       if (METALOG.isDebugEnabled()) {
1385         METALOG.debug(mutationsToString(puts));
1386       }
1387       t.put(puts);
1388     } finally {
1389       t.close();
1390     }
1391   }
1392
1393   /**
1394    * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
1395    * @param connection connection we're using
1396    * @param ps Put to add to hbase:meta
1397    * @throws IOException
1398    */
1399   public static void putsToMetaTable(final Connection connection, final List<Put> ps)
1400     throws IOException {
1401     Table t = getMetaHTable(connection);
1402     try {
1403       if (METALOG.isDebugEnabled()) {
1404         METALOG.debug(mutationsToString(ps));
1405       }
1406       t.put(ps);
1407     } finally {
1408       t.close();
1409     }
1410   }
1411
1412   /**
1413    * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
1414    * @param connection connection we're using
1415    * @param d Delete to add to hbase:meta
1416    * @throws IOException
1417    */
1418   static void deleteFromMetaTable(final Connection connection, final Delete d)
1419     throws IOException {
1420     List<Delete> dels = new ArrayList<Delete>(1);
1421     dels.add(d);
1422     deleteFromMetaTable(connection, dels);
1423   }
1424
1425   /**
1426    * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
1427    * @param connection connection we're using
1428    * @param deletes Deletes to add to hbase:meta  This list should support #remove.
1429    * @throws IOException
1430    */
1431   public static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes)
1432     throws IOException {
1433     Table t = getMetaHTable(connection);
1434     try {
1435       if (METALOG.isDebugEnabled()) {
1436         METALOG.debug(mutationsToString(deletes));
1437       }
1438       t.delete(deletes);
1439     } finally {
1440       t.close();
1441     }
1442   }
1443
1444   /**
1445    * Deletes some replica columns corresponding to replicas for the passed rows
1446    * @param metaRows rows in hbase:meta
1447    * @param replicaIndexToDeleteFrom the replica ID we would start deleting from
1448    * @param numReplicasToRemove how many replicas to remove
1449    * @param connection connection we're using to access meta table
1450    * @throws IOException
1451    */
1452   public static void removeRegionReplicasFromMeta(Set<byte[]> metaRows,
1453     int replicaIndexToDeleteFrom, int numReplicasToRemove, Connection connection)
1454       throws IOException {
1455     int absoluteIndex = replicaIndexToDeleteFrom + numReplicasToRemove;
1456     for (byte[] row : metaRows) {
1457       long now = EnvironmentEdgeManager.currentTime();
1458       Delete deleteReplicaLocations = new Delete(row);
1459       for (int i = replicaIndexToDeleteFrom; i < absoluteIndex; i++) {
1460         deleteReplicaLocations.addColumns(getCatalogFamily(),
1461           getServerColumn(i), now);
1462         deleteReplicaLocations.addColumns(getCatalogFamily(),
1463           getSeqNumColumn(i), now);
1464         deleteReplicaLocations.addColumns(getCatalogFamily(),
1465           getStartCodeColumn(i), now);
1466       }
1467       deleteFromMetaTable(connection, deleteReplicaLocations);
1468     }
1469   }
1470
1471   /**
1472    * Execute the passed <code>mutations</code> against <code>hbase:meta</code> table.
1473    * @param connection connection we're using
1474    * @param mutations Puts and Deletes to execute on hbase:meta
1475    * @throws IOException
1476    */
1477   public static void mutateMetaTable(final Connection connection,
1478                                      final List<Mutation> mutations)
1479     throws IOException {
1480     Table t = getMetaHTable(connection);
1481     try {
1482       if (METALOG.isDebugEnabled()) {
1483         METALOG.debug(mutationsToString(mutations));
1484       }
1485       t.batch(mutations, null);
1486     } catch (InterruptedException e) {
1487       InterruptedIOException ie = new InterruptedIOException(e.getMessage());
1488       ie.initCause(e);
1489       throw ie;
1490     } finally {
1491       t.close();
1492     }
1493   }
1494
1495   /**
1496    * Adds a hbase:meta row for the specified new region.
1497    * @param connection connection we're using
1498    * @param regionInfo region information
1499    * @throws IOException if problem connecting or updating meta
1500    */
1501   public static void addRegionToMeta(Connection connection,
1502                                      HRegionInfo regionInfo)
1503     throws IOException {
1504     putToMetaTable(connection, makePutFromRegionInfo(regionInfo));
1505     LOG.info("Added " + regionInfo.getRegionNameAsString());
1506   }
1507
1508   /**
1509    * Adds a hbase:meta row for the specified new region to the given catalog table. The
1510    * Table is not flushed or closed.
1511    * @param meta the Table for META
1512    * @param regionInfo region information
1513    * @throws IOException if problem connecting or updating meta
1514    */
1515   public static void addRegionToMeta(Table meta, HRegionInfo regionInfo) throws IOException {
1516     addRegionToMeta(meta, regionInfo, null, null);
1517   }
1518
1519   /**
1520    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1521    * does not add its daughter's as different rows, but adds information about the daughters
1522    * in the same row as the parent. Use
1523    * {@link #splitRegion(Connection, HRegionInfo, HRegionInfo, HRegionInfo, ServerName,int,boolean)}
1524    * if you want to do that.
1525    * @param meta the Table for META
1526    * @param regionInfo region information
1527    * @param splitA first split daughter of the parent regionInfo
1528    * @param splitB second split daughter of the parent regionInfo
1529    * @throws IOException if problem connecting or updating meta
1530    */
1531   public static void addRegionToMeta(Table meta, HRegionInfo regionInfo,
1532                                      HRegionInfo splitA, HRegionInfo splitB) throws IOException {
1533     Put put = makePutFromRegionInfo(regionInfo);
1534     addDaughtersToPut(put, splitA, splitB);
1535     meta.put(put);
1536     if (METALOG.isDebugEnabled()) {
1537       METALOG.debug(mutationToString(put));
1538     }
1539     if (LOG.isDebugEnabled()) {
1540       LOG.debug("Added " + regionInfo.getRegionNameAsString());
1541     }
1542   }
1543
1544   /**
1545    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1546    * does not add its daughter's as different rows, but adds information about the daughters
1547    * in the same row as the parent. Use
1548    * {@link #splitRegion(Connection, HRegionInfo, HRegionInfo, HRegionInfo, ServerName,int,boolean)}
1549    * if you want to do that.
1550    * @param connection connection we're using
1551    * @param regionInfo region information
1552    * @param splitA first split daughter of the parent regionInfo
1553    * @param splitB second split daughter of the parent regionInfo
1554    * @throws IOException if problem connecting or updating meta
1555    */
1556   public static void addRegionToMeta(Connection connection, HRegionInfo regionInfo,
1557                                      HRegionInfo splitA, HRegionInfo splitB) throws IOException {
1558     Table meta = getMetaHTable(connection);
1559     try {
1560       addRegionToMeta(meta, regionInfo, splitA, splitB);
1561     } finally {
1562       meta.close();
1563     }
1564   }
1565
1566   /**
1567    * Adds a hbase:meta row for each of the specified new regions.
1568    * @param connection connection we're using
1569    * @param regionInfos region information list
1570    * @throws IOException if problem connecting or updating meta
1571    */
1572   public static void addRegionsToMeta(Connection connection,
1573                                       List<HRegionInfo> regionInfos, int regionReplication)
1574     throws IOException {
1575     addRegionsToMeta(connection, regionInfos, regionReplication, HConstants.LATEST_TIMESTAMP);
1576   }
1577   /**
1578    * Adds a hbase:meta row for each of the specified new regions.
1579    * @param connection connection we're using
1580    * @param regionInfos region information list
1581    * @param regionReplication
1582    * @param ts desired timestamp
1583    * @throws IOException if problem connecting or updating meta
1584    */
1585   public static void addRegionsToMeta(Connection connection,
1586       List<HRegionInfo> regionInfos, int regionReplication, long ts)
1587           throws IOException {
1588     List<Put> puts = new ArrayList<Put>();
1589     for (HRegionInfo regionInfo : regionInfos) {
1590       if (RegionReplicaUtil.isDefaultReplica(regionInfo)) {
1591         Put put = makePutFromRegionInfo(regionInfo, ts);
1592         // Add empty locations for region replicas so that number of replicas can be cached
1593         // whenever the primary region is looked up from meta
1594         for (int i = 1; i < regionReplication; i++) {
1595           addEmptyLocation(put, i);
1596         }
1597         puts.add(put);
1598       }
1599     }
1600     putsToMetaTable(connection, puts);
1601     LOG.info("Added " + puts.size());
1602   }
1603
1604   /**
1605    * Adds a daughter region entry to meta.
1606    * @param regionInfo the region to put
1607    * @param sn the location of the region
1608    * @param openSeqNum the latest sequence number obtained when the region was open
1609    */
1610   public static void addDaughter(final Connection connection,
1611       final HRegionInfo regionInfo, final ServerName sn, final long openSeqNum)
1612       throws NotAllMetaRegionsOnlineException, IOException {
1613     long now = EnvironmentEdgeManager.currentTime();
1614     Put put = new Put(regionInfo.getRegionName(), now);
1615     addRegionInfo(put, regionInfo);
1616     if (sn != null) {
1617       addLocation(put, sn, openSeqNum, -1, regionInfo.getReplicaId());
1618     }
1619     putToMetaTable(connection, put);
1620     LOG.info("Added daughter " + regionInfo.getEncodedName() +
1621       (sn == null? ", serverName=null": ", serverName=" + sn.toString()));
1622   }
1623 
1624   /**
1625    * Merge the two regions into one in an atomic operation. Deletes the two
1626    * merging regions in hbase:meta and adds the merged region with the information of
1627    * two merging regions.
1628    * @param connection connection we're using
1629    * @param mergedRegion the merged region
1630    * @param regionA
1631    * @param regionB
1632    * @param sn the location of the region
1633    * @param masterSystemTime
1634    * @param saveBarrier true if need save replication barrier in meta, used for serial replication
1635    * @throws IOException
1636    */
1637   public static void mergeRegions(final Connection connection, HRegionInfo mergedRegion,
1638       HRegionInfo regionA, HRegionInfo regionB, ServerName sn, int regionReplication,
1639       long masterSystemTime, boolean saveBarrier)
1640           throws IOException {
1641     Table meta = getMetaHTable(connection);
1642     try {
1643       HRegionInfo copyOfMerged = new HRegionInfo(mergedRegion);
1644 
1645       // use the maximum of what master passed us vs local time.
1646       long time = Math.max(EnvironmentEdgeManager.currentTime(), masterSystemTime);
1647
1648       // Put for parent
1649       Put putOfMerged = makePutFromRegionInfo(copyOfMerged, time);
1650       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER,
1651         regionA.toByteArray());
1652       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEB_QUALIFIER,
1653         regionB.toByteArray());
1654
1655       // Deletes for merging regions
1656       Delete deleteA = makeDeleteFromRegionInfo(regionA, time);
1657       Delete deleteB = makeDeleteFromRegionInfo(regionB, time);
1658
1659       // The merged is a new region, openSeqNum = 1 is fine.
1660       addLocation(putOfMerged, sn, 1, -1, mergedRegion.getReplicaId());
1661
1662       // Add empty locations for region replicas of the merged region so that number of replicas can
1663       // be cached whenever the primary region is looked up from meta
1664       for (int i = 1; i < regionReplication; i++) {
1665         addEmptyLocation(putOfMerged, i);
1666       }
1667
1668       byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString()
1669         + HConstants.DELIMITER);
1670       Mutation[] mutations;
1671       if (saveBarrier) {
1672         Put putBarrierA = makeSerialDaughterPut(regionA.getEncodedNameAsBytes(),
1673             Bytes.toBytes(mergedRegion.getEncodedName()));
1674         Put putBarrierB = makeSerialDaughterPut(regionB.getEncodedNameAsBytes(),
1675             Bytes.toBytes(mergedRegion.getEncodedName()));
1676         mutations = new Mutation[] { putOfMerged, deleteA, deleteB, putBarrierA, putBarrierB };
1677       } else {
1678         mutations = new Mutation[] { putOfMerged, deleteA, deleteB };
1679       }
1680       multiMutate(meta, tableRow, mutations);
1681     } finally {
1682       meta.close();
1683     }
1684   }
1685
1686   /**
1687    * Splits the region into two in an atomic operation. Offlines the parent
1688    * region with the information that it is split into two, and also adds
1689    * the daughter regions. Does not add the location information to the daughter
1690    * regions since they are not open yet.
1691    * @param connection connection we're using
1692    * @param parent the parent region which is split
1693    * @param splitA Split daughter region A
1694    * @param splitB Split daughter region A
1695    * @param sn the location of the region
1696    * @param saveBarrier true if need save replication barrier in meta, used for serial replication
1697    */
1698   public static void splitRegion(final Connection connection, HRegionInfo parent,
1699       HRegionInfo splitA, HRegionInfo splitB, ServerName sn, int regionReplication,
1700       boolean saveBarrier) throws IOException {
1701     Table meta = getMetaHTable(connection);
1702     try {
1703       HRegionInfo copyOfParent = new HRegionInfo(parent);
1704       copyOfParent.setOffline(true);
1705       copyOfParent.setSplit(true);
1706
1707       //Put for parent
1708       Put putParent = makePutFromRegionInfo(copyOfParent);
1709       addDaughtersToPut(putParent, splitA, splitB);
1710
1711       //Puts for daughters
1712       Put putA = makePutFromRegionInfo(splitA);
1713       Put putB = makePutFromRegionInfo(splitB);
1714 
1715       addLocation(putA, sn, 1, -1, splitA.getReplicaId()); //new regions, openSeqNum = 1 is fine.
1716       addLocation(putB, sn, 1, -1, splitB.getReplicaId());
1717
1718       // Add empty locations for region replicas of daughters so that number of replicas can be
1719       // cached whenever the primary region is looked up from meta
1720       for (int i = 1; i < regionReplication; i++) {
1721         addEmptyLocation(putA, i);
1722         addEmptyLocation(putB, i);
1723       }
1724
1725       Mutation[] mutations;
1726       if (saveBarrier) {
1727         Put putBarrier = makeSerialDaughterPut(parent.getEncodedNameAsBytes(),
1728             Bytes
1729                 .toBytes(splitA.getEncodedName() + HConstants.DELIMITER + splitB.getEncodedName()));
1730         mutations = new Mutation[]{putParent, putA, putB, putBarrier};
1731       } else {
1732         mutations = new Mutation[]{putParent, putA, putB};
1733       }
1734       byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER);
1735       multiMutate(meta, tableRow, mutations);
1736     } finally {
1737       meta.close();
1738     }
1739   }
1740
1741   /**
1742    * Update state of the table in meta.
1743    * @param connection what we use for update
1744    * @param state new state
1745    * @throws IOException
1746    */
1747   public static void updateTableState(Connection connection, TableState state)
1748       throws IOException {
1749     Put put = makePutFromTableState(state);
1750     putToMetaTable(connection, put);
1751     LOG.info(
1752         "Updated table " + state.getTableName() + " state to " + state.getState() + " in META");
1753   }
1754
1755   /**
1756    * Construct PUT for given state
1757    * @param state new state
1758    */
1759   public static Put makePutFromTableState(TableState state) {
1760     long time = EnvironmentEdgeManager.currentTime();
1761     Put put = new Put(state.getTableName().getName(), time);
1762     put.addColumn(getTableFamily(), getStateColumn(), state.convert().toByteArray());
1763     return put;
1764   }
1765
1766   /**
1767    * Remove state for table from meta
1768    * @param connection to use for deletion
1769    * @param table to delete state for
1770    */
1771   public static void deleteTableState(Connection connection, TableName table)
1772       throws IOException {
1773     long time = EnvironmentEdgeManager.currentTime();
1774     Delete delete = new Delete(table.getName());
1775     delete.addColumns(getTableFamily(), getStateColumn(), time);
1776     deleteFromMetaTable(connection, delete);
1777     LOG.info("Deleted table " + table + " state from META");
1778   }
1779
1780   /**
1781    * Performs an atomic multi-Mutate operation against the given table.
1782    */
1783   private static void multiMutate(Table table, byte[] row, Mutation... mutations)
1784       throws IOException {
1785     CoprocessorRpcChannel channel = table.coprocessorService(row);
1786     MultiRowMutationProtos.MutateRowsRequest.Builder mmrBuilder
1787       = MultiRowMutationProtos.MutateRowsRequest.newBuilder();
1788     if (METALOG.isDebugEnabled()) {
1789       METALOG.debug(mutationsToString(mutations));
1790     }
1791     for (Mutation mutation : mutations) {
1792       if (mutation instanceof Put) {
1793         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
1794           ClientProtos.MutationProto.MutationType.PUT, mutation));
1795       } else if (mutation instanceof Delete) {
1796         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
1797           ClientProtos.MutationProto.MutationType.DELETE, mutation));
1798       } else {
1799         throw new DoNotRetryIOException("multi in MetaEditor doesn't support "
1800           + mutation.getClass().getName());
1801       }
1802     }
1803
1804     MultiRowMutationProtos.MultiRowMutationService.BlockingInterface service =
1805       MultiRowMutationProtos.MultiRowMutationService.newBlockingStub(channel);
1806     try {
1807       service.mutateRows(null, mmrBuilder.build());
1808     } catch (ServiceException ex) {
1809       ProtobufUtil.toIOException(ex);
1810     }
1811   }
1812 
1813   /**
1814    * Updates the location of the specified region in hbase:meta to be the specified
1815    * server hostname and startcode.
1816    * <p>
1817    * Uses passed catalog tracker to get a connection to the server hosting
1818    * hbase:meta and makes edits to that region.
1819    *
1820    * @param connection connection we're using
1821    * @param regionInfo region to update location of
1822    * @param openSeqNum the latest sequence number obtained when the region was open
1823    * @param sn Server name
1824    * @param masterSystemTime wall clock time from master if passed in the open region RPC or -1
1825    * @throws IOException
1826    */
1827   public static void updateRegionLocation(Connection connection,
1828                                           HRegionInfo regionInfo, ServerName sn, long openSeqNum,
1829                                           long masterSystemTime)
1830     throws IOException {
1831     updateLocation(connection, regionInfo, sn, openSeqNum, masterSystemTime);
1832   }
1833
1834   /**
1835    * Updates the progress of pushing entries to peer cluster. Skip entry if value is -1.
1836    * @param connection connection we're using
1837    * @param peerId the peerId to push
1838    * @param positions map that saving positions for each region
1839    * @throws IOException
1840    */
1841   public static void updateReplicationPositions(Connection connection, String peerId,
1842       Map<String, Long> positions) throws IOException {
1843     List<Put> puts = new ArrayList<>();
1844     for (Map.Entry<String, Long> entry : positions.entrySet()) {
1845       long value = Math.abs(entry.getValue());
1846       Put put = new Put(Bytes.toBytes(entry.getKey()));
1847       put.addImmutable(HConstants.REPLICATION_POSITION_FAMILY, Bytes.toBytes(peerId),
1848           Bytes.toBytes(value));
1849       puts.add(put);
1850     }
1851     getMetaHTable(connection).put(puts);
1852   }
1853
1854
1855   /**
1856    * Updates the location of the specified region to be the specified server.
1857    * <p>
1858    * Connects to the specified server which should be hosting the specified
1859    * catalog region name to perform the edit.
1860    *
1861    * @param connection connection we're using
1862    * @param regionInfo region to update location of
1863    * @param sn Server name
1864    * @param openSeqNum the latest sequence number obtained when the region was open
1865    * @param masterSystemTime wall clock time from master if passed in the open region RPC or -1
1866    * @throws IOException In particular could throw {@link java.net.ConnectException}
1867    * if the server is down on other end.
1868    */
1869   private static void updateLocation(final Connection connection,
1870                                      HRegionInfo regionInfo, ServerName sn, long openSeqNum,
1871                                      long masterSystemTime)
1872     throws IOException {
1873
1874     // use the maximum of what master passed us vs local time.
1875     long time = Math.max(EnvironmentEdgeManager.currentTime(), masterSystemTime);
1876
1877     // region replicas are kept in the primary region's row
1878     Put put = new Put(getMetaKeyForRegion(regionInfo), time);
1879     addLocation(put, sn, openSeqNum, time, regionInfo.getReplicaId());
1880     putToMetaTable(connection, put);
1881     LOG.info("Updated row " + regionInfo.getRegionNameAsString() +
1882       " with server=" + sn);
1883   }
1884
1885   /**
1886    * Deletes the specified region from META.
1887    * @param connection connection we're using
1888    * @param regionInfo region to be deleted from META
1889    * @throws IOException
1890    */
1891   public static void deleteRegion(Connection connection,
1892                                   HRegionInfo regionInfo)
1893     throws IOException {
1894     long time = EnvironmentEdgeManager.currentTime();
1895     Delete delete = new Delete(regionInfo.getRegionName());
1896     delete.addFamily(getCatalogFamily(), time);
1897     deleteFromMetaTable(connection, delete);
1898     LOG.info("Deleted " + regionInfo.getRegionNameAsString());
1899   }
1900
1901   /**
1902    * Deletes the specified regions from META.
1903    * @param connection connection we're using
1904    * @param regionsInfo list of regions to be deleted from META
1905    * @throws IOException
1906    */
1907   public static void deleteRegions(Connection connection,
1908                                    List<HRegionInfo> regionsInfo) throws IOException {
1909     deleteRegions(connection, regionsInfo, EnvironmentEdgeManager.currentTime());
1910   }
1911   /**
1912    * Deletes the specified regions from META.
1913    * @param connection connection we're using
1914    * @param regionsInfo list of regions to be deleted from META
1915    * @throws IOException
1916    */
1917   public static void deleteRegions(Connection connection,
1918                                    List<HRegionInfo> regionsInfo, long ts) throws IOException {
1919     List<Delete> deletes = new ArrayList<Delete>(regionsInfo.size());
1920     for (HRegionInfo hri: regionsInfo) {
1921       Delete e = new Delete(hri.getRegionName());
1922       e.addFamily(getCatalogFamily(), ts);
1923       deletes.add(e);
1924     }
1925     deleteFromMetaTable(connection, deletes);
1926     LOG.info("Deleted " + regionsInfo);
1927   }
1928
1929   /**
1930    * Adds and Removes the specified regions from hbase:meta
1931    * @param connection connection we're using
1932    * @param regionsToRemove list of regions to be deleted from META
1933    * @param regionsToAdd list of regions to be added to META
1934    * @throws IOException
1935    */
1936   public static void mutateRegions(Connection connection,
1937                                    final List<HRegionInfo> regionsToRemove,
1938                                    final List<HRegionInfo> regionsToAdd)
1939     throws IOException {
1940     List<Mutation> mutation = new ArrayList<Mutation>();
1941     if (regionsToRemove != null) {
1942       for (HRegionInfo hri: regionsToRemove) {
1943         mutation.add(makeDeleteFromRegionInfo(hri));
1944       }
1945     }
1946     if (regionsToAdd != null) {
1947       for (HRegionInfo hri: regionsToAdd) {
1948         mutation.add(makePutFromRegionInfo(hri));
1949       }
1950     }
1951     mutateMetaTable(connection, mutation);
1952     if (regionsToRemove != null && regionsToRemove.size() > 0) {
1953       LOG.debug("Deleted " + regionsToRemove);
1954     }
1955     if (regionsToAdd != null && regionsToAdd.size() > 0) {
1956       LOG.debug("Added " + regionsToAdd);
1957     }
1958   }
1959
1960   /**
1961    * Overwrites the specified regions from hbase:meta
1962    * @param connection connection we're using
1963    * @param regionInfos list of regions to be added to META
1964    * @throws IOException
1965    */
1966   public static void overwriteRegions(Connection connection,
1967       List<HRegionInfo> regionInfos, int regionReplication) throws IOException {
1968     // use master time for delete marker and the Put
1969     long now = EnvironmentEdgeManager.currentTime();
1970     deleteRegions(connection, regionInfos, now);
1971     // Why sleep? This is the easiest way to ensure that the previous deletes does not
1972     // eclipse the following puts, that might happen in the same ts from the server.
1973     // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed,
1974     // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep.
1975     //
1976     // HBASE-13875 uses master timestamp for the mutations. The 20ms sleep is not needed
1977     addRegionsToMeta(connection, regionInfos, regionReplication, now+1);
1978     LOG.info("Overwritten " + regionInfos);
1979   }
1980
1981   /**
1982    * Deletes merge qualifiers for the specified merged region.
1983    * @param connection connection we're using
1984    * @param mergedRegion
1985    * @throws IOException
1986    */
1987   public static void deleteMergeQualifiers(Connection connection,
1988                                            final HRegionInfo mergedRegion) throws IOException {
1989     long time = EnvironmentEdgeManager.currentTime();
1990     Delete delete = new Delete(mergedRegion.getRegionName());
1991     delete.addColumns(getCatalogFamily(), HConstants.MERGEA_QUALIFIER, time);
1992     delete.addColumns(getCatalogFamily(), HConstants.MERGEB_QUALIFIER, time);
1993     deleteFromMetaTable(connection, delete);
1994     LOG.info("Deleted references in merged region "
1995       + mergedRegion.getRegionNameAsString() + ", qualifier="
1996       + Bytes.toStringBinary(HConstants.MERGEA_QUALIFIER) + " and qualifier="
1997       + Bytes.toStringBinary(HConstants.MERGEB_QUALIFIER));
1998   }
1999
2000   private static Put addRegionInfo(final Put p, final HRegionInfo hri)
2001     throws IOException {
2002     p.addImmutable(getCatalogFamily(), HConstants.REGIONINFO_QUALIFIER,
2003       hri.toByteArray());
2004     return p;
2005   }
2006
2007   public static Put addLocation(final Put p, final ServerName sn, long openSeqNum,
2008       long time, int replicaId){
2009     if (time <= 0) {
2010       time = EnvironmentEdgeManager.currentTime();
2011     }
2012     p.addImmutable(getCatalogFamily(), getServerColumn(replicaId), time,
2013       Bytes.toBytes(sn.getHostAndPort()));
2014     p.addImmutable(getCatalogFamily(), getStartCodeColumn(replicaId), time,
2015       Bytes.toBytes(sn.getStartcode()));
2016     p.addImmutable(getCatalogFamily(), getSeqNumColumn(replicaId), time,
2017       Bytes.toBytes(openSeqNum));
2018     return p;
2019   }
2020
2021   public static Put addEmptyLocation(final Put p, int replicaId) {
2022     long now = EnvironmentEdgeManager.currentTime();
2023     p.addImmutable(getCatalogFamily(), getServerColumn(replicaId), now, null);
2024     p.addImmutable(getCatalogFamily(), getStartCodeColumn(replicaId), now, null);
2025     p.addImmutable(getCatalogFamily(), getSeqNumColumn(replicaId), now, null);
2026     return p;
2027   }
2028
2029   private static String mutationsToString(Mutation ... mutations) throws IOException {
2030     StringBuilder sb = new StringBuilder();
2031     String prefix = "";
2032     for (Mutation mutation : mutations) {
2033       sb.append(prefix).append(mutationToString(mutation));
2034       prefix = ", ";
2035     }
2036     return sb.toString();
2037   }
2038
2039   private static String mutationsToString(List<? extends Mutation> mutations) throws IOException {
2040     StringBuilder sb = new StringBuilder();
2041     String prefix = "";
2042     for (Mutation mutation : mutations) {
2043       sb.append(prefix).append(mutationToString(mutation));
2044       prefix = ", ";
2045     }
2046     return sb.toString();
2047   }
2048
2049   private static String mutationToString(Mutation p) throws IOException {
2050     return p.getClass().getSimpleName() + p.toJSON();
2051   }
2052
2053   /**
2054    * Get replication position for a peer in a region.
2055    * @param connection connection we're using
2056    * @return the position of this peer, -1 if no position in meta.
2057    */
2058   public static long getReplicationPositionForOnePeer(Connection connection,
2059       byte[] encodedRegionName, String peerId) throws IOException {
2060     Get get = new Get(encodedRegionName);
2061     get.addColumn(HConstants.REPLICATION_POSITION_FAMILY, Bytes.toBytes(peerId));
2062     Result r = get(getMetaHTable(connection), get);
2063     if (r.isEmpty()) {
2064       return -1;
2065     }
2066     Cell cell = r.rawCells()[0];
2067     return Bytes.toLong(cell.getValueArray(),cell.getValueOffset(),cell.getValueLength());
2068   }
2069
2070   /**
2071    * Get replication positions for all peers in a region.
2072    * @param connection connection we're using
2073    * @param encodedRegionName region's encoded name
2074    * @return the map of positions for each peer
2075    */
2076   public static Map<String, Long> getReplicationPositionForAllPeer(Connection connection,
2077       byte[] encodedRegionName) throws IOException {
2078     Get get = new Get(encodedRegionName);
2079     get.addFamily(HConstants.REPLICATION_POSITION_FAMILY);
2080     Result r = get(getMetaHTable(connection), get);
2081     Map<String, Long> map = new HashMap<>((int) (r.size() / 0.75 + 1));
2082     for (Cell c : r.listCells()) {
2083       if (!Bytes.equals(tableNamePosCq, 0, tableNamePosCq.length, c.getQualifierArray(),
2084           c.getQualifierOffset(), c.getQualifierLength()) &&
2085           !Bytes.equals(daughterNamePosCq, 0, daughterNamePosCq.length, c.getQualifierArray(),
2086           c.getQualifierOffset(), c.getQualifierLength())) {
2087         map.put(
2088             Bytes.toString(c.getQualifierArray(), c.getQualifierOffset(), c.getQualifierLength()),
2089             Bytes.toLong(c.getValueArray(), c.getValueOffset(), c.getValueLength()));
2090       }
2091     }
2092     return map;
2093   }
2094
2095   /**
2096    * Get replication barriers for all peers in a region.
2097    * @param encodedRegionName region's encoded name
2098    * @return a list of barrier sequence numbers.
2099    * @throws IOException
2100    */
2101   public static List<Long> getReplicationBarriers(Connection connection, byte[] encodedRegionName)
2102       throws IOException {
2103     Get get = new Get(encodedRegionName);
2104     get.addFamily(HConstants.REPLICATION_BARRIER_FAMILY);
2105     Result r = get(getMetaHTable(connection), get);
2106     List<Long> list = new ArrayList<>();
2107     if (!r.isEmpty()) {
2108       for (Cell cell : r.rawCells()) {
2109         list.add(Bytes.toLong(cell.getQualifierArray(), cell.getQualifierOffset(),
2110             cell.getQualifierLength()));
2111       }
2112     }
2113     return list;
2114   }
2115
2116   /**
2117    * Get all barriers in all regions.
2118    * @return a map of barrier lists in all regions
2119    * @throws IOException
2120    */
2121   public static Map<String, List<Long>> getAllBarriers(Connection connection) throws IOException {
2122     Map<String, List<Long>> map = new HashMap<>();
2123     Scan scan = new Scan();
2124     scan.addFamily(HConstants.REPLICATION_BARRIER_FAMILY);
2125     try (Table t = getMetaHTable(connection);
2126         ResultScanner scanner = t.getScanner(scan)) {
2127       Result result;
2128       while ((result = scanner.next()) != null) {
2129         String key = Bytes.toString(result.getRow());
2130         List<Long> list = new ArrayList<>();
2131         for (Cell cell : result.rawCells()) {
2132           list.add(Bytes.toLong(cell.getQualifierArray(), cell.getQualifierOffset(),
2133               cell.getQualifierLength()));
2134         }
2135         map.put(key, list);
2136       }
2137     }
2138     return map;
2139   }
2140
2141   /**
2142    * Get daughter region(s) for a region, only used in serial replication.
2143    * @throws IOException
2144    */
2145   public static String getSerialReplicationDaughterRegion(Connection connection, byte[] encodedName)
2146       throws IOException {
2147     Get get = new Get(encodedName);
2148     get.addColumn(HConstants.REPLICATION_POSITION_FAMILY, daughterNamePosCq);
2149     Result result = get(getMetaHTable(connection), get);
2150     if (!result.isEmpty()) {
2151       Cell c = result.rawCells()[0];
2152       return Bytes.toString(c.getValueArray(), c.getValueOffset(), c.getValueLength());
2153     }
2154     return null;
2155   }
2156
2157   /**
2158    * Get the table name for a region, only used in serial replication.
2159    * @throws IOException
2160    */
2161   public static String getSerialReplicationTableName(Connection connection, byte[] encodedName)
2162       throws IOException {
2163     Get get = new Get(encodedName);
2164     get.addColumn(HConstants.REPLICATION_POSITION_FAMILY, tableNamePosCq);
2165     Result result = get(getMetaHTable(connection), get);
2166     if (!result.isEmpty()) {
2167       Cell c = result.rawCells()[0];
2168       return Bytes.toString(c.getValueArray(), c.getValueOffset(), c.getValueLength());
2169     }
2170     return null;
2171   }
2172
2173 }