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 javax.annotation.Nullable;
21  import java.io.IOException;
22  import java.io.InterruptedIOException;
23  import java.util.ArrayList;
24  import java.util.Arrays;
25  import java.util.LinkedHashMap;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.NavigableMap;
29  import java.util.Set;
30  import java.util.SortedMap;
31  import java.util.TreeMap;
32  import java.util.regex.Matcher;
33  import java.util.regex.Pattern;
34  
35  import com.google.common.annotations.VisibleForTesting;
36  import com.google.protobuf.ServiceException;
37  import org.apache.commons.logging.Log;
38  import org.apache.commons.logging.LogFactory;
39  import org.apache.hadoop.conf.Configuration;
40  import org.apache.hadoop.hbase.classification.InterfaceAudience;
41  import org.apache.hadoop.hbase.client.ClusterConnection;
42  import org.apache.hadoop.hbase.client.Connection;
43  import org.apache.hadoop.hbase.client.ConnectionFactory;
44  import org.apache.hadoop.hbase.client.Delete;
45  import org.apache.hadoop.hbase.client.Get;
46  import org.apache.hadoop.hbase.client.HTable;
47  import org.apache.hadoop.hbase.client.Mutation;
48  import org.apache.hadoop.hbase.client.Put;
49  import org.apache.hadoop.hbase.client.RegionLocator;
50  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
51  import org.apache.hadoop.hbase.client.Result;
52  import org.apache.hadoop.hbase.client.ResultScanner;
53  import org.apache.hadoop.hbase.client.Scan;
54  import org.apache.hadoop.hbase.client.Table;
55  import org.apache.hadoop.hbase.client.TableState;
56  import org.apache.hadoop.hbase.exceptions.DeserializationException;
57  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
58  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
59  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
60  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos;
61  import org.apache.hadoop.hbase.util.Bytes;
62  import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
63  import org.apache.hadoop.hbase.util.Pair;
64  import org.apache.hadoop.hbase.util.PairOfSameType;
65  import org.apache.hadoop.hbase.util.Threads;
66  
67  /**
68   * Read/write operations on region and assignment information store in
69   * <code>hbase:meta</code>.
70   *
71   * Some of the methods of this class take ZooKeeperWatcher as a param. The only reason
72   * for this is because when used on client-side (like from HBaseAdmin), we want to use
73   * short-living connection (opened before each operation, closed right after), while
74   * when used on HM or HRS (like in AssignmentManager) we want permanent connection.
75   */
76  @InterfaceAudience.Private
77  public class MetaTableAccessor {
78  
79    /*
80     * HBASE-10070 adds a replicaId to HRI, meaning more than one HRI can be defined for the
81     * same table range (table, startKey, endKey). For every range, there will be at least one
82     * HRI defined which is called default replica.
83     *
84     * Meta layout (as of 0.98 + HBASE-10070) is like:
85     *
86     * For each table there is single row in column family 'table' formatted:
87     * <tableName> including namespace and columns are:
88     * table: state             => contains table state
89     *
90     * For each table range, there is a single row, formatted like:
91     * <tableName>,<startKey>,<regionId>,<encodedRegionName>. This row corresponds to the regionName
92     * of the default region replica.
93     * Columns are:
94     * info:regioninfo         => contains serialized HRI for the default region replica
95     * info:server             => contains hostname:port (in string form) for the server hosting
96     *                            the default regionInfo replica
97     * info:server_<replicaId> => contains hostname:port (in string form) for the server hosting the
98     *                            regionInfo replica with replicaId
99     * info:serverstartcode    => contains server start code (in binary long form) for the server
100    *                            hosting the default regionInfo replica
101    * info:serverstartcode_<replicaId> => contains server start code (in binary long form) for the
102    *                                     server hosting the regionInfo replica with replicaId
103    * info:seqnumDuringOpen    => contains seqNum (in binary long form) for the region at the time
104    *                             the server opened the region with default replicaId
105    * info:seqnumDuringOpen_<replicaId> => contains seqNum (in binary long form) for the region at
106    *                             the time the server opened the region with replicaId
107    * info:splitA              => contains a serialized HRI for the first daughter region if the
108    *                             region is split
109    * info:splitB              => contains a serialized HRI for the second daughter region if the
110    *                             region is split
111    * info:mergeA              => contains a serialized HRI for the first parent region if the
112    *                             region is the result of a merge
113    * info:mergeB              => contains a serialized HRI for the second parent region if the
114    *                             region is the result of a merge
115    *
116    * The actual layout of meta should be encapsulated inside MetaTableAccessor methods,
117    * and should not leak out of it (through Result objects, etc)
118    */
119 
120   private static final Log LOG = LogFactory.getLog(MetaTableAccessor.class);
121 
122   static final byte [] META_REGION_PREFIX;
123   static {
124     // Copy the prefix from FIRST_META_REGIONINFO into META_REGION_PREFIX.
125     // FIRST_META_REGIONINFO == 'hbase:meta,,1'.  META_REGION_PREFIX == 'hbase:meta,'
126     int len = HRegionInfo.FIRST_META_REGIONINFO.getRegionName().length - 2;
127     META_REGION_PREFIX = new byte [len];
128     System.arraycopy(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), 0,
129       META_REGION_PREFIX, 0, len);
130   }
131 
132 
133   @InterfaceAudience.Private
134   public enum QueryType {
135     ALL(HConstants.TABLE_FAMILY, HConstants.CATALOG_FAMILY),
136     REGION(HConstants.CATALOG_FAMILY),
137     TABLE(HConstants.TABLE_FAMILY);
138 
139     private final byte[][] families;
140 
141     QueryType(byte[]... families) {
142       this.families = families;
143     }
144 
145     byte[][] getFamilies() {
146       return this.families;
147     }
148   }
149 
150   /** The delimiter for meta columns for replicaIds > 0 */
151   protected static final char META_REPLICA_ID_DELIMITER = '_';
152 
153   /** A regex for parsing server columns from meta. See above javadoc for meta layout */
154   private static final Pattern SERVER_COLUMN_PATTERN
155     = Pattern.compile("^server(_[0-9a-fA-F]{4})?$");
156 
157   ////////////////////////
158   // Reading operations //
159   ////////////////////////
160 
161   /**
162    * Performs a full scan of <code>hbase:meta</code> for regions.
163    * @param connection connection we're using
164    * @param visitor Visitor invoked against each row in regions family.
165    * @throws IOException
166    */
167   public static void fullScanRegions(Connection connection,
168       final Visitor visitor)
169       throws IOException {
170     fullScan(connection, visitor, null, QueryType.REGION);
171   }
172 
173   /**
174    * Performs a full scan of <code>hbase:meta</code> for regions.
175    * @param connection connection we're using
176    * @throws IOException
177    */
178   public static List<Result> fullScanRegions(Connection connection)
179       throws IOException {
180     return fullScan(connection, QueryType.REGION);
181   }
182 
183   /**
184    * Performs a full scan of <code>hbase:meta</code> for tables.
185    * @param connection connection we're using
186    * @param visitor Visitor invoked against each row in tables family.
187    * @throws IOException
188    */
189   public static void fullScanTables(Connection connection,
190       final Visitor visitor)
191       throws IOException {
192     fullScan(connection, visitor, null, QueryType.TABLE);
193   }
194 
195   /**
196    * Performs a full scan of <code>hbase:meta</code>.
197    * @param connection connection we're using
198    * @param visitor Visitor invoked against each row.
199    * @param type scanned part of meta
200    * @throws IOException
201    */
202   public static void fullScan(Connection connection,
203       final Visitor visitor, QueryType type)
204   throws IOException {
205     fullScan(connection, visitor, null, type);
206   }
207 
208   /**
209    * Performs a full scan of <code>hbase:meta</code>.
210    * @param connection connection we're using
211    * @param type scanned part of meta
212    * @return List of {@link Result}
213    * @throws IOException
214    */
215   public static List<Result> fullScan(Connection connection, QueryType type)
216     throws IOException {
217     CollectAllVisitor v = new CollectAllVisitor();
218     fullScan(connection, v, null, type);
219     return v.getResults();
220   }
221 
222   /**
223    * Callers should call close on the returned {@link Table} instance.
224    * @param connection connection we're using to access Meta
225    * @return An {@link Table} for <code>hbase:meta</code>
226    * @throws IOException
227    */
228   static Table getMetaHTable(final Connection connection)
229   throws IOException {
230     // We used to pass whole CatalogTracker in here, now we just pass in Connection
231     if (connection == null || connection.isClosed()) {
232       throw new NullPointerException("No connection");
233     }
234     // If the passed in 'connection' is 'managed' -- i.e. every second test uses
235     // a Table or an HBaseAdmin with managed connections -- then doing
236     // connection.getTable will throw an exception saying you are NOT to use
237     // managed connections getting tables.  Leaving this as it is for now. Will
238     // revisit when inclined to change all tests.  User code probaby makes use of
239     // managed connections too so don't change it till post hbase 1.0.
240     //
241     // There should still be a way to use this method with an unmanaged connection.
242     if (connection instanceof ClusterConnection) {
243       if (((ClusterConnection) connection).isManaged()) {
244         return new HTable(TableName.META_TABLE_NAME, connection);
245       }
246     }
247     return connection.getTable(TableName.META_TABLE_NAME);
248   }
249 
250   /**
251    * @param t Table to use (will be closed when done).
252    * @param g Get to run
253    * @throws IOException
254    */
255   private static Result get(final Table t, final Get g) throws IOException {
256     if (t == null) return null;
257     try {
258       return t.get(g);
259     } finally {
260       t.close();
261     }
262   }
263 
264   /**
265    * Gets the region info and assignment for the specified region.
266    * @param connection connection we're using
267    * @param regionName Region to lookup.
268    * @return Location and HRegionInfo for <code>regionName</code>
269    * @throws IOException
270    * @deprecated use {@link #getRegionLocation(Connection, byte[])} instead
271    */
272   @Deprecated
273   public static Pair<HRegionInfo, ServerName> getRegion(Connection connection, byte [] regionName)
274     throws IOException {
275     HRegionLocation location = getRegionLocation(connection, regionName);
276     return location == null
277       ? null
278       : new Pair<HRegionInfo, ServerName>(location.getRegionInfo(), location.getServerName());
279   }
280 
281   /**
282    * Returns the HRegionLocation from meta for the given region
283    * @param connection connection we're using
284    * @param regionName region we're looking for
285    * @return HRegionLocation for the given region
286    * @throws IOException
287    */
288   public static HRegionLocation getRegionLocation(Connection connection,
289                                                   byte[] regionName) throws IOException {
290     byte[] row = regionName;
291     HRegionInfo parsedInfo = null;
292     try {
293       parsedInfo = parseRegionInfoFromRegionName(regionName);
294       row = getMetaKeyForRegion(parsedInfo);
295     } catch (Exception parseEx) {
296       // Ignore. This is used with tableName passed as regionName.
297     }
298     Get get = new Get(row);
299     get.addFamily(HConstants.CATALOG_FAMILY);
300     Result r = get(getMetaHTable(connection), get);
301     RegionLocations locations = getRegionLocations(r);
302     return locations == null
303       ? null
304       : locations.getRegionLocation(parsedInfo == null ? 0 : parsedInfo.getReplicaId());
305   }
306 
307   /**
308    * Returns the HRegionLocation from meta for the given region
309    * @param connection connection we're using
310    * @param regionInfo region information
311    * @return HRegionLocation for the given region
312    * @throws IOException
313    */
314   public static HRegionLocation getRegionLocation(Connection connection,
315                                                   HRegionInfo regionInfo) throws IOException {
316     byte[] row = getMetaKeyForRegion(regionInfo);
317     Get get = new Get(row);
318     get.addFamily(HConstants.CATALOG_FAMILY);
319     Result r = get(getMetaHTable(connection), get);
320     return getRegionLocation(r, regionInfo, regionInfo.getReplicaId());
321   }
322 
323   /** Returns the row key to use for this regionInfo */
324   public static byte[] getMetaKeyForRegion(HRegionInfo regionInfo) {
325     return RegionReplicaUtil.getRegionInfoForDefaultReplica(regionInfo).getRegionName();
326   }
327 
328   /** Returns an HRI parsed from this regionName. Not all the fields of the HRI
329    * is stored in the name, so the returned object should only be used for the fields
330    * in the regionName.
331    */
332   protected static HRegionInfo parseRegionInfoFromRegionName(byte[] regionName)
333     throws IOException {
334     byte[][] fields = HRegionInfo.parseRegionName(regionName);
335     long regionId =  Long.parseLong(Bytes.toString(fields[2]));
336     int replicaId = fields.length > 3 ? Integer.parseInt(Bytes.toString(fields[3]), 16) : 0;
337     return new HRegionInfo(
338       TableName.valueOf(fields[0]), fields[1], fields[1], false, regionId, replicaId);
339   }
340 
341   /**
342    * Gets the result in hbase:meta for the specified region.
343    * @param connection connection we're using
344    * @param regionName region we're looking for
345    * @return result of the specified region
346    * @throws IOException
347    */
348   public static Result getRegionResult(Connection connection,
349       byte[] regionName) throws IOException {
350     Get get = new Get(regionName);
351     get.addFamily(HConstants.CATALOG_FAMILY);
352     return get(getMetaHTable(connection), get);
353   }
354 
355   /**
356    * Get regions from the merge qualifier of the specified merged region
357    * @return null if it doesn't contain merge qualifier, else two merge regions
358    * @throws IOException
359    */
360   @Nullable
361   public static Pair<HRegionInfo, HRegionInfo> getRegionsFromMergeQualifier(
362       Connection connection, byte[] regionName) throws IOException {
363     Result result = getRegionResult(connection, regionName);
364     HRegionInfo mergeA = getHRegionInfo(result, HConstants.MERGEA_QUALIFIER);
365     HRegionInfo mergeB = getHRegionInfo(result, HConstants.MERGEB_QUALIFIER);
366     if (mergeA == null && mergeB == null) {
367       return null;
368     }
369     return new Pair<HRegionInfo, HRegionInfo>(mergeA, mergeB);
370  }
371 
372   /**
373    * Checks if the specified table exists.  Looks at the hbase:meta table hosted on
374    * the specified server.
375    * @param connection connection we're using
376    * @param tableName table to check
377    * @return true if the table exists in meta, false if not
378    * @throws IOException
379    */
380   public static boolean tableExists(Connection connection,
381       final TableName tableName)
382   throws IOException {
383     // Catalog tables always exist.
384     return tableName.equals(TableName.META_TABLE_NAME)
385         || getTableState(connection, tableName) != null;
386   }
387 
388   /**
389    * Gets all of the regions of the specified table. Do not use this method
390    * to get meta table regions, use methods in MetaTableLocator instead.
391    * @param connection connection we're using
392    * @param tableName table we're looking for
393    * @return Ordered list of {@link HRegionInfo}.
394    * @throws IOException
395    */
396   public static List<HRegionInfo> getTableRegions(Connection connection, TableName tableName)
397   throws IOException {
398     return getTableRegions(connection, tableName, false);
399   }
400 
401   /**
402    * Gets all of the regions of the specified table. Do not use this method
403    * to get meta table regions, use methods in MetaTableLocator instead.
404    * @param connection connection we're using
405    * @param tableName table we're looking for
406    * @param excludeOfflinedSplitParents If true, do not include offlined split
407    * parents in the return.
408    * @return Ordered list of {@link HRegionInfo}.
409    * @throws IOException
410    */
411   public static List<HRegionInfo> getTableRegions(Connection connection,
412       TableName tableName, final boolean excludeOfflinedSplitParents)
413       throws IOException {
414     List<Pair<HRegionInfo, ServerName>> result;
415 
416     result = getTableRegionsAndLocations(connection, tableName,
417       excludeOfflinedSplitParents);
418 
419     return getListOfHRegionInfos(result);
420   }
421 
422   @Nullable
423   static List<HRegionInfo> getListOfHRegionInfos(final List<Pair<HRegionInfo, ServerName>> pairs) {
424     if (pairs == null || pairs.isEmpty()) return null;
425     List<HRegionInfo> result = new ArrayList<HRegionInfo>(pairs.size());
426     for (Pair<HRegionInfo, ServerName> pair: pairs) {
427       result.add(pair.getFirst());
428     }
429     return result;
430   }
431 
432   /**
433    * @param current region of current table we're working with
434    * @param tableName table we're checking against
435    * @return True if <code>current</code> tablename is equal to
436    * <code>tableName</code>
437    */
438   static boolean isInsideTable(final HRegionInfo current, final TableName tableName) {
439     return tableName.equals(current.getTable());
440   }
441 
442   /**
443    * @param tableName table we're working with
444    * @return Place to start Scan in <code>hbase:meta</code> when passed a
445    * <code>tableName</code>; returns &lt;tableName&rt; &lt;,&rt; &lt;,&rt;
446    */
447   static byte [] getTableStartRowForMeta(TableName tableName) {
448     byte [] startRow = new byte[tableName.getName().length + 2];
449     System.arraycopy(tableName.getName(), 0, startRow, 0, tableName.getName().length);
450     startRow[startRow.length - 2] = HConstants.DELIMITER;
451     startRow[startRow.length - 1] = HConstants.DELIMITER;
452     return startRow;
453   }
454 
455   /**
456    * This method creates a Scan object that will only scan catalog rows that
457    * belong to the specified table. It doesn't specify any columns.
458    * This is a better alternative to just using a start row and scan until
459    * it hits a new table since that requires parsing the HRI to get the table
460    * name.
461    * @param tableName bytes of table's name
462    * @return configured Scan object
463    */
464   public static Scan getScanForTableName(TableName tableName) {
465     String strName = tableName.getNameAsString();
466     // Start key is just the table name with delimiters
467     byte[] startKey = Bytes.toBytes(strName + ",,");
468     // Stop key appends the smallest possible char to the table name
469     byte[] stopKey = Bytes.toBytes(strName + " ,,");
470 
471     Scan scan = new Scan(startKey);
472     scan.setStopRow(stopKey);
473     return scan;
474   }
475 
476   /**
477    * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
478    * @param connection connection we're using
479    * @param tableName table we're looking for
480    * @return Return list of regioninfos and server.
481    * @throws IOException
482    */
483   public static List<Pair<HRegionInfo, ServerName>>
484     getTableRegionsAndLocations(Connection connection, TableName tableName)
485       throws IOException {
486     return getTableRegionsAndLocations(connection, tableName, true);
487   }
488 
489   /**
490    * Do not use this method to get meta table regions, use methods in MetaTableLocator instead.
491    * @param connection connection we're using
492    * @param tableName table to work with
493    * @param excludeOfflinedSplitParents don't return split parents
494    * @return Return list of regioninfos and server addresses.
495    * @throws IOException
496    */
497   public static List<Pair<HRegionInfo, ServerName>> getTableRegionsAndLocations(
498         Connection connection, final TableName tableName,
499       final boolean excludeOfflinedSplitParents) throws IOException {
500     if (tableName.equals(TableName.META_TABLE_NAME)) {
501       throw new IOException("This method can't be used to locate meta regions;"
502         + " use MetaTableLocator instead");
503     }
504     // Make a version of CollectingVisitor that collects HRegionInfo and ServerAddress
505     CollectingVisitor<Pair<HRegionInfo, ServerName>> visitor =
506       new CollectingVisitor<Pair<HRegionInfo, ServerName>>() {
507         private RegionLocations current = null;
508 
509         @Override
510         public boolean visit(Result r) throws IOException {
511           current = getRegionLocations(r);
512           if (current == null || current.getRegionLocation().getRegionInfo() == null) {
513             LOG.warn("No serialized HRegionInfo in " + r);
514             return true;
515           }
516           HRegionInfo hri = current.getRegionLocation().getRegionInfo();
517           if (!isInsideTable(hri, tableName)) return false;
518           if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
519           // Else call super and add this Result to the collection.
520           return super.visit(r);
521         }
522 
523         @Override
524         void add(Result r) {
525           if (current == null) {
526             return;
527           }
528           for (HRegionLocation loc : current.getRegionLocations()) {
529             if (loc != null) {
530               this.results.add(new Pair<HRegionInfo, ServerName>(
531                 loc.getRegionInfo(), loc.getServerName()));
532             }
533           }
534         }
535       };
536     fullScan(connection, visitor, getTableStartRowForMeta(tableName), QueryType.REGION);
537     return visitor.getResults();
538   }
539 
540   /**
541    * @param connection connection we're using
542    * @param serverName server whose regions we're interested in
543    * @return List of user regions installed on this server (does not include
544    * catalog regions).
545    * @throws IOException
546    */
547   public static NavigableMap<HRegionInfo, Result>
548   getServerUserRegions(Connection connection, final ServerName serverName)
549     throws IOException {
550     final NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
551     // Fill the above hris map with entries from hbase:meta that have the passed
552     // servername.
553     CollectingVisitor<Result> v = new CollectingVisitor<Result>() {
554       @Override
555       void add(Result r) {
556         if (r == null || r.isEmpty()) return;
557         RegionLocations locations = getRegionLocations(r);
558         if (locations == null) return;
559         for (HRegionLocation loc : locations.getRegionLocations()) {
560           if (loc != null) {
561             if (loc.getServerName() != null && loc.getServerName().equals(serverName)) {
562               hris.put(loc.getRegionInfo(), r);
563             }
564           }
565         }
566       }
567     };
568     fullScan(connection, v, QueryType.REGION);
569     return hris;
570   }
571 
572   public static void fullScanMetaAndPrint(Connection connection)
573     throws IOException {
574     Visitor v = new Visitor() {
575       @Override
576       public boolean visit(Result r) throws IOException {
577         if (r ==  null || r.isEmpty()) return true;
578         LOG.info("fullScanMetaAndPrint.Current Meta Row: " + r);
579         TableState state = getTableState(r);
580         if (state != null) {
581           LOG.info("Table State: " + state);
582         } else {
583           RegionLocations locations = getRegionLocations(r);
584           if (locations == null) return true;
585           for (HRegionLocation loc : locations.getRegionLocations()) {
586             if (loc != null) {
587               LOG.info("fullScanMetaAndPrint.HRI Print= " + loc.getRegionInfo());
588             }
589           }
590         }
591         return true;
592       }
593     };
594     fullScan(connection, v, QueryType.ALL);
595   }
596 
597   /**
598    * Performs a full scan of a catalog table.
599    * @param connection connection we're using
600    * @param visitor Visitor invoked against each row.
601    * @param startrow Where to start the scan. Pass null if want to begin scan
602    * at first row.
603    * @param type scanned part of meta
604    * <code>hbase:meta</code>, the default (pass false to scan hbase:meta)
605    * @throws IOException
606    */
607   public static void fullScan(Connection connection,
608       final Visitor visitor, @Nullable final byte[] startrow, QueryType type) throws IOException {
609     fullScan(connection, visitor, startrow, type, false);
610   }
611 
612   /**
613    * Performs a full scan of a catalog table.
614    * @param connection connection we're using
615    * @param visitor Visitor invoked against each row.
616    * @param startrow Where to start the scan. Pass null if want to begin scan
617    * at first row.
618    * @param type scanned part of meta
619    * @param raw read raw data including Delete tumbstones
620    * <code>hbase:meta</code>, the default (pass false to scan hbase:meta)
621    * @throws IOException
622    */
623   public static void fullScan(Connection connection,
624       final Visitor visitor, @Nullable final byte[] startrow, QueryType type, boolean raw)
625   throws IOException {
626     Scan scan = new Scan();
627     scan.setRaw(raw);
628     if (startrow != null) scan.setStartRow(startrow);
629     if (startrow == null) {
630       int caching = connection.getConfiguration()
631           .getInt(HConstants.HBASE_META_SCANNER_CACHING, 100);
632       scan.setCaching(caching);
633     }
634     for (byte[] family : type.getFamilies()) {
635       scan.addFamily(family);
636     }
637     Table metaTable = getMetaHTable(connection);
638     ResultScanner scanner = null;
639     try {
640       scanner = metaTable.getScanner(scan);
641       Result data;
642       while((data = scanner.next()) != null) {
643         if (data.isEmpty()) continue;
644         // Break if visit returns false.
645         if (!visitor.visit(data)) break;
646       }
647     } finally {
648       if (scanner != null) scanner.close();
649       metaTable.close();
650     }
651   }
652 
653   /**
654    * Returns the column family used for meta columns.
655    * @return HConstants.CATALOG_FAMILY.
656    */
657   protected static byte[] getCatalogFamily() {
658     return HConstants.CATALOG_FAMILY;
659   }
660 
661   /**
662    * Returns the column family used for table columns.
663    * @return HConstants.TABLE_FAMILY.
664    */
665   protected static byte[] getTableFamily() {
666     return HConstants.TABLE_FAMILY;
667   }
668 
669   /**
670    * Returns the column qualifier for serialized region info
671    * @return HConstants.REGIONINFO_QUALIFIER
672    */
673   protected static byte[] getRegionInfoColumn() {
674     return HConstants.REGIONINFO_QUALIFIER;
675   }
676 
677   /**
678    * Returns the column qualifier for serialized table state
679    *
680    * @return HConstants.TABLE_STATE_QUALIFIER
681    */
682   protected static byte[] getStateColumn() {
683     return HConstants.TABLE_STATE_QUALIFIER;
684   }
685 
686   /**
687    * Returns the column qualifier for server column for replicaId
688    * @param replicaId the replicaId of the region
689    * @return a byte[] for server column qualifier
690    */
691   @VisibleForTesting
692   public static byte[] getServerColumn(int replicaId) {
693     return replicaId == 0
694       ? HConstants.SERVER_QUALIFIER
695       : Bytes.toBytes(HConstants.SERVER_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
696       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
697   }
698 
699   /**
700    * Returns the column qualifier for server start code column for replicaId
701    * @param replicaId the replicaId of the region
702    * @return a byte[] for server start code column qualifier
703    */
704   @VisibleForTesting
705   public static byte[] getStartCodeColumn(int replicaId) {
706     return replicaId == 0
707       ? HConstants.STARTCODE_QUALIFIER
708       : Bytes.toBytes(HConstants.STARTCODE_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
709       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
710   }
711 
712   /**
713    * Returns the column qualifier for seqNum column for replicaId
714    * @param replicaId the replicaId of the region
715    * @return a byte[] for seqNum column qualifier
716    */
717   @VisibleForTesting
718   public static byte[] getSeqNumColumn(int replicaId) {
719     return replicaId == 0
720       ? HConstants.SEQNUM_QUALIFIER
721       : Bytes.toBytes(HConstants.SEQNUM_QUALIFIER_STR + META_REPLICA_ID_DELIMITER
722       + String.format(HRegionInfo.REPLICA_ID_FORMAT, replicaId));
723   }
724 
725   /**
726    * Parses the replicaId from the server column qualifier. See top of the class javadoc
727    * for the actual meta layout
728    * @param serverColumn the column qualifier
729    * @return an int for the replicaId
730    */
731   @VisibleForTesting
732   static int parseReplicaIdFromServerColumn(byte[] serverColumn) {
733     String serverStr = Bytes.toString(serverColumn);
734 
735     Matcher matcher = SERVER_COLUMN_PATTERN.matcher(serverStr);
736     if (matcher.matches() && matcher.groupCount() > 0) {
737       String group = matcher.group(1);
738       if (group != null && group.length() > 0) {
739         return Integer.parseInt(group.substring(1), 16);
740       } else {
741         return 0;
742       }
743     }
744     return -1;
745   }
746 
747   /**
748    * Returns a {@link ServerName} from catalog table {@link Result}.
749    * @param r Result to pull from
750    * @return A ServerName instance or null if necessary fields not found or empty.
751    */
752   @Nullable
753   private static ServerName getServerName(final Result r, final int replicaId) {
754     byte[] serverColumn = getServerColumn(replicaId);
755     Cell cell = r.getColumnLatestCell(getCatalogFamily(), serverColumn);
756     if (cell == null || cell.getValueLength() == 0) return null;
757     String hostAndPort = Bytes.toString(
758       cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
759     byte[] startcodeColumn = getStartCodeColumn(replicaId);
760     cell = r.getColumnLatestCell(getCatalogFamily(), startcodeColumn);
761     if (cell == null || cell.getValueLength() == 0) return null;
762     return ServerName.valueOf(hostAndPort,
763       Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength()));
764   }
765 
766   /**
767    * The latest seqnum that the server writing to meta observed when opening the region.
768    * E.g. the seqNum when the result of {@link #getServerName(Result, int)} was written.
769    * @param r Result to pull the seqNum from
770    * @return SeqNum, or HConstants.NO_SEQNUM if there's no value written.
771    */
772   private static long getSeqNumDuringOpen(final Result r, final int replicaId) {
773     Cell cell = r.getColumnLatestCell(getCatalogFamily(), getSeqNumColumn(replicaId));
774     if (cell == null || cell.getValueLength() == 0) return HConstants.NO_SEQNUM;
775     return Bytes.toLong(cell.getValueArray(), cell.getValueOffset(), cell.getValueLength());
776   }
777 
778   /**
779    * Returns an HRegionLocationList extracted from the result.
780    * @return an HRegionLocationList containing all locations for the region range or null if
781    *  we can't deserialize the result.
782    */
783   @Nullable
784   public static RegionLocations getRegionLocations(final Result r) {
785     if (r == null) return null;
786     HRegionInfo regionInfo = getHRegionInfo(r, getRegionInfoColumn());
787     if (regionInfo == null) return null;
788 
789     List<HRegionLocation> locations = new ArrayList<HRegionLocation>(1);
790     NavigableMap<byte[],NavigableMap<byte[],byte[]>> familyMap = r.getNoVersionMap();
791 
792     locations.add(getRegionLocation(r, regionInfo, 0));
793 
794     NavigableMap<byte[], byte[]> infoMap = familyMap.get(getCatalogFamily());
795     if (infoMap == null) return new RegionLocations(locations);
796 
797     // iterate until all serverName columns are seen
798     int replicaId = 0;
799     byte[] serverColumn = getServerColumn(replicaId);
800     SortedMap<byte[], byte[]> serverMap = infoMap.tailMap(serverColumn, false);
801     if (serverMap.isEmpty()) return new RegionLocations(locations);
802 
803     for (Map.Entry<byte[], byte[]> entry : serverMap.entrySet()) {
804       replicaId = parseReplicaIdFromServerColumn(entry.getKey());
805       if (replicaId < 0) {
806         break;
807       }
808       HRegionLocation location = getRegionLocation(r, regionInfo, replicaId);
809       // In case the region replica is newly created, it's location might be null. We usually do not
810       // have HRL's in RegionLocations object with null ServerName. They are handled as null HRLs.
811       if (location == null || location.getServerName() == null) {
812         locations.add(null);
813       } else {
814         locations.add(location);
815       }
816     }
817 
818     return new RegionLocations(locations);
819   }
820 
821   /**
822    * Returns the HRegionLocation parsed from the given meta row Result
823    * for the given regionInfo and replicaId. The regionInfo can be the default region info
824    * for the replica.
825    * @param r the meta row result
826    * @param regionInfo RegionInfo for default replica
827    * @param replicaId the replicaId for the HRegionLocation
828    * @return HRegionLocation parsed from the given meta row Result for the given replicaId
829    */
830   private static HRegionLocation getRegionLocation(final Result r, final HRegionInfo regionInfo,
831                                                    final int replicaId) {
832     ServerName serverName = getServerName(r, replicaId);
833     long seqNum = getSeqNumDuringOpen(r, replicaId);
834     HRegionInfo replicaInfo = RegionReplicaUtil.getRegionInfoForReplica(regionInfo, replicaId);
835     return new HRegionLocation(replicaInfo, serverName, seqNum);
836   }
837 
838   /**
839    * Returns HRegionInfo object from the column
840    * HConstants.CATALOG_FAMILY:HConstants.REGIONINFO_QUALIFIER of the catalog
841    * table Result.
842    * @param data a Result object from the catalog table scan
843    * @return HRegionInfo or null
844    */
845   public static HRegionInfo getHRegionInfo(Result data) {
846     return getHRegionInfo(data, HConstants.REGIONINFO_QUALIFIER);
847   }
848 
849   /**
850    * Returns the HRegionInfo object from the column {@link HConstants#CATALOG_FAMILY} and
851    * <code>qualifier</code> of the catalog table result.
852    * @param r a Result object from the catalog table scan
853    * @param qualifier Column family qualifier
854    * @return An HRegionInfo instance or null.
855    */
856   @Nullable
857   private static HRegionInfo getHRegionInfo(final Result r, byte [] qualifier) {
858     Cell cell = r.getColumnLatestCell(getCatalogFamily(), qualifier);
859     if (cell == null) return null;
860     return HRegionInfo.parseFromOrNull(cell.getValueArray(),
861       cell.getValueOffset(), cell.getValueLength());
862   }
863 
864   /**
865    * Returns the daughter regions by reading the corresponding columns of the catalog table
866    * Result.
867    * @param data a Result object from the catalog table scan
868    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
869    * parent
870    */
871   public static PairOfSameType<HRegionInfo> getDaughterRegions(Result data) {
872     HRegionInfo splitA = getHRegionInfo(data, HConstants.SPLITA_QUALIFIER);
873     HRegionInfo splitB = getHRegionInfo(data, HConstants.SPLITB_QUALIFIER);
874 
875     return new PairOfSameType<HRegionInfo>(splitA, splitB);
876   }
877 
878   /**
879    * Returns the merge regions by reading the corresponding columns of the catalog table
880    * Result.
881    * @param data a Result object from the catalog table scan
882    * @return a pair of HRegionInfo or PairOfSameType(null, null) if the region is not a split
883    * parent
884    */
885   public static PairOfSameType<HRegionInfo> getMergeRegions(Result data) {
886     HRegionInfo mergeA = getHRegionInfo(data, HConstants.MERGEA_QUALIFIER);
887     HRegionInfo mergeB = getHRegionInfo(data, HConstants.MERGEB_QUALIFIER);
888 
889     return new PairOfSameType<HRegionInfo>(mergeA, mergeB);
890   }
891 
892   /**
893    * Fetch table state for given table from META table
894    * @param conn connection to use
895    * @param tableName table to fetch state for
896    * @return state
897    * @throws IOException
898    */
899   @Nullable
900   public static TableState getTableState(Connection conn, TableName tableName)
901       throws IOException {
902     Table metaHTable = getMetaHTable(conn);
903     Get get = new Get(tableName.getName()).addColumn(getTableFamily(), getStateColumn());
904     long time = EnvironmentEdgeManager.currentTime();
905     get.setTimeRange(0, time);
906     Result result =
907         metaHTable.get(get);
908     return getTableState(result);
909   }
910 
911   /**
912    * Fetch table states from META table
913    * @param conn connection to use
914    * @return map {tableName -> state}
915    * @throws IOException
916    */
917   public static Map<TableName, TableState> getTableStates(Connection conn)
918       throws IOException {
919     final Map<TableName, TableState> states = new LinkedHashMap<>();
920     Visitor collector = new Visitor() {
921       @Override
922       public boolean visit(Result r) throws IOException {
923         TableState state = getTableState(r);
924         if (state != null)
925           states.put(state.getTableName(), state);
926         return true;
927       }
928     };
929     fullScanTables(conn, collector);
930     return states;
931   }
932 
933   /**
934    * Updates state in META
935    * @param conn connection to use
936    * @param tableName table to look for
937    * @throws IOException
938    */
939   public static void updateTableState(Connection conn, TableName tableName,
940       TableState.State actual) throws IOException {
941     updateTableState(conn, new TableState(tableName, actual));
942   }
943 
944   /**
945    * Decode table state from META Result.
946    * Should contain cell from HConstants.TABLE_FAMILY
947    * @param r result
948    * @return null if not found
949    * @throws IOException
950    */
951   @Nullable
952   public static TableState getTableState(Result r)
953       throws IOException {
954     Cell cell = r.getColumnLatestCell(getTableFamily(), getStateColumn());
955     if (cell == null) return null;
956     try {
957       return TableState.parseFrom(TableName.valueOf(r.getRow()),
958           Arrays.copyOfRange(cell.getValueArray(),
959           cell.getValueOffset(), cell.getValueOffset() + cell.getValueLength()));
960     } catch (DeserializationException e) {
961       throw new IOException(e);
962     }
963 
964   }
965 
966   /**
967    * Implementations 'visit' a catalog table row.
968    */
969   public interface Visitor {
970     /**
971      * Visit the catalog table row.
972      * @param r A row from catalog table
973      * @return True if we are to proceed scanning the table, else false if
974      * we are to stop now.
975      */
976     boolean visit(final Result r) throws IOException;
977   }
978 
979   /**
980    * A {@link Visitor} that collects content out of passed {@link Result}.
981    */
982   static abstract class CollectingVisitor<T> implements Visitor {
983     final List<T> results = new ArrayList<T>();
984     @Override
985     public boolean visit(Result r) throws IOException {
986       if (r ==  null || r.isEmpty()) return true;
987       add(r);
988       return true;
989     }
990 
991     abstract void add(Result r);
992 
993     /**
994      * @return Collected results; wait till visits complete to collect all
995      * possible results
996      */
997     List<T> getResults() {
998       return this.results;
999     }
1000   }
1001 
1002   /**
1003    * Collects all returned.
1004    */
1005   static class CollectAllVisitor extends CollectingVisitor<Result> {
1006     @Override
1007     void add(Result r) {
1008       this.results.add(r);
1009     }
1010   }
1011 
1012   /**
1013    * Count regions in <code>hbase:meta</code> for passed table.
1014    * @param c Configuration object
1015    * @param tableName table name to count regions for
1016    * @return Count or regions in table <code>tableName</code>
1017    * @throws IOException
1018    */
1019   @Deprecated
1020   public static int getRegionCount(final Configuration c, final String tableName)
1021       throws IOException {
1022     return getRegionCount(c, TableName.valueOf(tableName));
1023   }
1024 
1025   /**
1026    * Count regions in <code>hbase:meta</code> for passed table.
1027    * @param c Configuration object
1028    * @param tableName table name to count regions for
1029    * @return Count or regions in table <code>tableName</code>
1030    * @throws IOException
1031    */
1032   public static int getRegionCount(final Configuration c, final TableName tableName)
1033   throws IOException {
1034     try (Connection connection = ConnectionFactory.createConnection(c)) {
1035       return getRegionCount(connection, tableName);
1036     }
1037   }
1038 
1039   /**
1040    * Count regions in <code>hbase:meta</code> for passed table.
1041    * @param connection Connection object
1042    * @param tableName table name to count regions for
1043    * @return Count or regions in table <code>tableName</code>
1044    * @throws IOException
1045    */
1046   public static int getRegionCount(final Connection connection, final TableName tableName)
1047   throws IOException {
1048     try (RegionLocator locator = connection.getRegionLocator(tableName)) {
1049       List<HRegionLocation> locations = locator.getAllRegionLocations();
1050       return locations == null? 0: locations.size();
1051     }
1052   }
1053 
1054   ////////////////////////
1055   // Editing operations //
1056   ////////////////////////
1057 
1058   /**
1059    * Generates and returns a Put containing the region into for the catalog table
1060    */
1061   public static Put makePutFromRegionInfo(HRegionInfo regionInfo)
1062     throws IOException {
1063     long now = EnvironmentEdgeManager.currentTime();
1064     Put put = new Put(regionInfo.getRegionName(), now);
1065     addRegionInfo(put, regionInfo);
1066     return put;
1067   }
1068 
1069   /**
1070    * Generates and returns a Delete containing the region info for the catalog
1071    * table
1072    */
1073   public static Delete makeDeleteFromRegionInfo(HRegionInfo regionInfo) {
1074     if (regionInfo == null) {
1075       throw new IllegalArgumentException("Can't make a delete for null region");
1076     }
1077     long now = EnvironmentEdgeManager.currentTime();
1078     Delete delete = new Delete(regionInfo.getRegionName());
1079     delete.addFamily(getCatalogFamily(), now);
1080     return delete;
1081   }
1082 
1083   /**
1084    * Adds split daughters to the Put
1085    */
1086   public static Put addDaughtersToPut(Put put, HRegionInfo splitA, HRegionInfo splitB) {
1087     if (splitA != null) {
1088       put.addImmutable(
1089         HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, splitA.toByteArray());
1090     }
1091     if (splitB != null) {
1092       put.addImmutable(
1093         HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, splitB.toByteArray());
1094     }
1095     return put;
1096   }
1097 
1098   /**
1099    * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
1100    * @param connection connection we're using
1101    * @param p Put to add to hbase:meta
1102    * @throws IOException
1103    */
1104   static void putToMetaTable(final Connection connection, final Put p)
1105     throws IOException {
1106     put(getMetaHTable(connection), p);
1107   }
1108 
1109   /**
1110    * @param t Table to use (will be closed when done).
1111    * @param p put to make
1112    * @throws IOException
1113    */
1114   private static void put(final Table t, final Put p) throws IOException {
1115     try {
1116       t.put(p);
1117     } finally {
1118       t.close();
1119     }
1120   }
1121 
1122   /**
1123    * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
1124    * @param connection connection we're using
1125    * @param ps Put to add to hbase:meta
1126    * @throws IOException
1127    */
1128   public static void putsToMetaTable(final Connection connection, final List<Put> ps)
1129     throws IOException {
1130     Table t = getMetaHTable(connection);
1131     try {
1132       t.put(ps);
1133     } finally {
1134       t.close();
1135     }
1136   }
1137 
1138   /**
1139    * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
1140    * @param connection connection we're using
1141    * @param d Delete to add to hbase:meta
1142    * @throws IOException
1143    */
1144   static void deleteFromMetaTable(final Connection connection, final Delete d)
1145     throws IOException {
1146     List<Delete> dels = new ArrayList<Delete>(1);
1147     dels.add(d);
1148     deleteFromMetaTable(connection, dels);
1149   }
1150 
1151   /**
1152    * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
1153    * @param connection connection we're using
1154    * @param deletes Deletes to add to hbase:meta  This list should support #remove.
1155    * @throws IOException
1156    */
1157   public static void deleteFromMetaTable(final Connection connection, final List<Delete> deletes)
1158     throws IOException {
1159     Table t = getMetaHTable(connection);
1160     try {
1161       t.delete(deletes);
1162     } finally {
1163       t.close();
1164     }
1165   }
1166 
1167   /**
1168    * Deletes some replica columns corresponding to replicas for the passed rows
1169    * @param metaRows rows in hbase:meta
1170    * @param replicaIndexToDeleteFrom the replica ID we would start deleting from
1171    * @param numReplicasToRemove how many replicas to remove
1172    * @param connection connection we're using to access meta table
1173    * @throws IOException
1174    */
1175   public static void removeRegionReplicasFromMeta(Set<byte[]> metaRows,
1176     int replicaIndexToDeleteFrom, int numReplicasToRemove, Connection connection)
1177       throws IOException {
1178     int absoluteIndex = replicaIndexToDeleteFrom + numReplicasToRemove;
1179     for (byte[] row : metaRows) {
1180       long now = EnvironmentEdgeManager.currentTime();
1181       Delete deleteReplicaLocations = new Delete(row);
1182       for (int i = replicaIndexToDeleteFrom; i < absoluteIndex; i++) {
1183         deleteReplicaLocations.addColumns(getCatalogFamily(),
1184           getServerColumn(i), now);
1185         deleteReplicaLocations.addColumns(getCatalogFamily(),
1186           getSeqNumColumn(i), now);
1187         deleteReplicaLocations.addColumns(getCatalogFamily(),
1188           getStartCodeColumn(i), now);
1189       }
1190       deleteFromMetaTable(connection, deleteReplicaLocations);
1191     }
1192   }
1193 
1194   /**
1195    * Execute the passed <code>mutations</code> against <code>hbase:meta</code> table.
1196    * @param connection connection we're using
1197    * @param mutations Puts and Deletes to execute on hbase:meta
1198    * @throws IOException
1199    */
1200   public static void mutateMetaTable(final Connection connection,
1201                                      final List<Mutation> mutations)
1202     throws IOException {
1203     Table t = getMetaHTable(connection);
1204     try {
1205       t.batch(mutations);
1206     } catch (InterruptedException e) {
1207       InterruptedIOException ie = new InterruptedIOException(e.getMessage());
1208       ie.initCause(e);
1209       throw ie;
1210     } finally {
1211       t.close();
1212     }
1213   }
1214 
1215   /**
1216    * Adds a hbase:meta row for the specified new region.
1217    * @param connection connection we're using
1218    * @param regionInfo region information
1219    * @throws IOException if problem connecting or updating meta
1220    */
1221   public static void addRegionToMeta(Connection connection,
1222                                      HRegionInfo regionInfo)
1223     throws IOException {
1224     putToMetaTable(connection, makePutFromRegionInfo(regionInfo));
1225     LOG.info("Added " + regionInfo.getRegionNameAsString());
1226   }
1227 
1228   /**
1229    * Adds a hbase:meta row for the specified new region to the given catalog table. The
1230    * Table is not flushed or closed.
1231    * @param meta the Table for META
1232    * @param regionInfo region information
1233    * @throws IOException if problem connecting or updating meta
1234    */
1235   public static void addRegionToMeta(Table meta, HRegionInfo regionInfo) throws IOException {
1236     addRegionToMeta(meta, regionInfo, null, null);
1237   }
1238 
1239   /**
1240    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1241    * does not add its daughter's as different rows, but adds information about the daughters
1242    * in the same row as the parent. Use
1243    * {@link #splitRegion(Connection, HRegionInfo, HRegionInfo, HRegionInfo, ServerName, int)}
1244    * if you want to do that.
1245    * @param meta the Table for META
1246    * @param regionInfo region information
1247    * @param splitA first split daughter of the parent regionInfo
1248    * @param splitB second split daughter of the parent regionInfo
1249    * @throws IOException if problem connecting or updating meta
1250    */
1251   public static void addRegionToMeta(Table meta, HRegionInfo regionInfo,
1252                                      HRegionInfo splitA, HRegionInfo splitB) throws IOException {
1253     Put put = makePutFromRegionInfo(regionInfo);
1254     addDaughtersToPut(put, splitA, splitB);
1255     meta.put(put);
1256     if (LOG.isDebugEnabled()) {
1257       LOG.debug("Added " + regionInfo.getRegionNameAsString());
1258     }
1259   }
1260 
1261   /**
1262    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this
1263    * does not add its daughter's as different rows, but adds information about the daughters
1264    * in the same row as the parent. Use
1265    * {@link #splitRegion(Connection, HRegionInfo, HRegionInfo, HRegionInfo, ServerName, int)}
1266    * if you want to do that.
1267    * @param connection connection we're using
1268    * @param regionInfo region information
1269    * @param splitA first split daughter of the parent regionInfo
1270    * @param splitB second split daughter of the parent regionInfo
1271    * @throws IOException if problem connecting or updating meta
1272    */
1273   public static void addRegionToMeta(Connection connection, HRegionInfo regionInfo,
1274                                      HRegionInfo splitA, HRegionInfo splitB) throws IOException {
1275     Table meta = getMetaHTable(connection);
1276     try {
1277       addRegionToMeta(meta, regionInfo, splitA, splitB);
1278     } finally {
1279       meta.close();
1280     }
1281   }
1282 
1283   /**
1284    * Adds a hbase:meta row for each of the specified new regions.
1285    * @param connection connection we're using
1286    * @param regionInfos region information list
1287    * @throws IOException if problem connecting or updating meta
1288    */
1289   public static void addRegionsToMeta(Connection connection,
1290                                       List<HRegionInfo> regionInfos, int regionReplication)
1291     throws IOException {
1292     List<Put> puts = new ArrayList<Put>();
1293     for (HRegionInfo regionInfo : regionInfos) {
1294       if (RegionReplicaUtil.isDefaultReplica(regionInfo)) {
1295         puts.add(makePutFromRegionInfo(regionInfo));
1296         Put put = makePutFromRegionInfo(regionInfo);
1297         // Add empty locations for region replicas so that number of replicas can be cached
1298         // whenever the primary region is looked up from meta
1299         for (int i = 1; i < regionReplication; i++) {
1300           addEmptyLocation(put, i);
1301         }
1302         puts.add(put);
1303       }
1304     }
1305     putsToMetaTable(connection, puts);
1306     LOG.info("Added " + puts.size());
1307   }
1308 
1309   /**
1310    * Adds a daughter region entry to meta.
1311    * @param regionInfo the region to put
1312    * @param sn the location of the region
1313    * @param openSeqNum the latest sequence number obtained when the region was open
1314    */
1315   public static void addDaughter(final Connection connection,
1316       final HRegionInfo regionInfo, final ServerName sn, final long openSeqNum)
1317       throws NotAllMetaRegionsOnlineException, IOException {
1318     long now = EnvironmentEdgeManager.currentTime();
1319     Put put = new Put(regionInfo.getRegionName(), now);
1320     addRegionInfo(put, regionInfo);
1321     if (sn != null) {
1322       addLocation(put, sn, openSeqNum, regionInfo.getReplicaId());
1323     }
1324     putToMetaTable(connection, put);
1325     LOG.info("Added daughter " + regionInfo.getEncodedName() +
1326       (sn == null? ", serverName=null": ", serverName=" + sn.toString()));
1327   }
1328 
1329   /**
1330    * Merge the two regions into one in an atomic operation. Deletes the two
1331    * merging regions in hbase:meta and adds the merged region with the information of
1332    * two merging regions.
1333    * @param connection connection we're using
1334    * @param mergedRegion the merged region
1335    * @param regionA
1336    * @param regionB
1337    * @param sn the location of the region
1338    * @throws IOException
1339    */
1340   public static void mergeRegions(final Connection connection, HRegionInfo mergedRegion,
1341       HRegionInfo regionA, HRegionInfo regionB, ServerName sn, int regionReplication)
1342           throws IOException {
1343     Table meta = getMetaHTable(connection);
1344     try {
1345       HRegionInfo copyOfMerged = new HRegionInfo(mergedRegion);
1346 
1347       // Put for parent
1348       Put putOfMerged = makePutFromRegionInfo(copyOfMerged);
1349       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER,
1350         regionA.toByteArray());
1351       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEB_QUALIFIER,
1352         regionB.toByteArray());
1353 
1354       // Deletes for merging regions
1355       Delete deleteA = makeDeleteFromRegionInfo(regionA);
1356       Delete deleteB = makeDeleteFromRegionInfo(regionB);
1357 
1358       // The merged is a new region, openSeqNum = 1 is fine.
1359       addLocation(putOfMerged, sn, 1, mergedRegion.getReplicaId());
1360 
1361       // Add empty locations for region replicas of the merged region so that number of replicas can
1362       // be cached whenever the primary region is looked up from meta
1363       for (int i = 1; i < regionReplication; i++) {
1364         addEmptyLocation(putOfMerged, i);
1365       }
1366 
1367       byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString()
1368         + HConstants.DELIMITER);
1369       multiMutate(meta, tableRow, putOfMerged, deleteA, deleteB);
1370     } finally {
1371       meta.close();
1372     }
1373   }
1374 
1375   /**
1376    * Splits the region into two in an atomic operation. Offlines the parent
1377    * region with the information that it is split into two, and also adds
1378    * the daughter regions. Does not add the location information to the daughter
1379    * regions since they are not open yet.
1380    * @param connection connection we're using
1381    * @param parent the parent region which is split
1382    * @param splitA Split daughter region A
1383    * @param splitB Split daughter region A
1384    * @param sn the location of the region
1385    */
1386   public static void splitRegion(final Connection connection,
1387                                  HRegionInfo parent, HRegionInfo splitA, HRegionInfo splitB,
1388                                  ServerName sn, int regionReplication) throws IOException {
1389     Table meta = getMetaHTable(connection);
1390     try {
1391       HRegionInfo copyOfParent = new HRegionInfo(parent);
1392       copyOfParent.setOffline(true);
1393       copyOfParent.setSplit(true);
1394 
1395       //Put for parent
1396       Put putParent = makePutFromRegionInfo(copyOfParent);
1397       addDaughtersToPut(putParent, splitA, splitB);
1398 
1399       //Puts for daughters
1400       Put putA = makePutFromRegionInfo(splitA);
1401       Put putB = makePutFromRegionInfo(splitB);
1402 
1403       addLocation(putA, sn, 1, splitA.getReplicaId()); //new regions, openSeqNum = 1 is fine.
1404       addLocation(putB, sn, 1, splitB.getReplicaId());
1405 
1406       // Add empty locations for region replicas of daughters so that number of replicas can be
1407       // cached whenever the primary region is looked up from meta
1408       for (int i = 1; i < regionReplication; i++) {
1409         addEmptyLocation(putA, i);
1410         addEmptyLocation(putB, i);
1411       }
1412 
1413       byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER);
1414       multiMutate(meta, tableRow, putParent, putA, putB);
1415     } finally {
1416       meta.close();
1417     }
1418   }
1419 
1420   /**
1421    * Update state of the table in meta.
1422    * @param connection what we use for update
1423    * @param state new state
1424    * @throws IOException
1425    */
1426   public static void updateTableState(Connection connection, TableState state)
1427       throws IOException {
1428     Put put = makePutFromTableState(state);
1429     putToMetaTable(connection, put);
1430     LOG.info(
1431         "Updated table " + state.getTableName() + " state to " + state.getState() + " in META");
1432   }
1433 
1434   /**
1435    * Construct PUT for given state
1436    * @param state new state
1437    */
1438   public static Put makePutFromTableState(TableState state) {
1439     long time = EnvironmentEdgeManager.currentTime();
1440     Put put = new Put(state.getTableName().getName(), time);
1441     put.add(getTableFamily(), getStateColumn(), state.convert().toByteArray());
1442     return put;
1443   }
1444 
1445   /**
1446    * Remove state for table from meta
1447    * @param connection to use for deletion
1448    * @param table to delete state for
1449    */
1450   public static void deleteTableState(Connection connection, TableName table)
1451       throws IOException {
1452     long time = EnvironmentEdgeManager.currentTime();
1453     Delete delete = new Delete(table.getName());
1454     delete.addColumns(getTableFamily(), getStateColumn(), time);
1455     deleteFromMetaTable(connection, delete);
1456     LOG.info("Deleted table " + table + " state from META");
1457   }
1458 
1459   /**
1460    * Performs an atomic multi-Mutate operation against the given table.
1461    */
1462   private static void multiMutate(Table table, byte[] row, Mutation... mutations)
1463       throws IOException {
1464     CoprocessorRpcChannel channel = table.coprocessorService(row);
1465     MultiRowMutationProtos.MutateRowsRequest.Builder mmrBuilder
1466       = MultiRowMutationProtos.MutateRowsRequest.newBuilder();
1467     for (Mutation mutation : mutations) {
1468       if (mutation instanceof Put) {
1469         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
1470           ClientProtos.MutationProto.MutationType.PUT, mutation));
1471       } else if (mutation instanceof Delete) {
1472         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(
1473           ClientProtos.MutationProto.MutationType.DELETE, mutation));
1474       } else {
1475         throw new DoNotRetryIOException("multi in MetaEditor doesn't support "
1476           + mutation.getClass().getName());
1477       }
1478     }
1479 
1480     MultiRowMutationProtos.MultiRowMutationService.BlockingInterface service =
1481       MultiRowMutationProtos.MultiRowMutationService.newBlockingStub(channel);
1482     try {
1483       service.mutateRows(null, mmrBuilder.build());
1484     } catch (ServiceException ex) {
1485       ProtobufUtil.toIOException(ex);
1486     }
1487   }
1488 
1489   /**
1490    * Updates the location of the specified region in hbase:meta to be the specified
1491    * server hostname and startcode.
1492    * <p>
1493    * Uses passed catalog tracker to get a connection to the server hosting
1494    * hbase:meta and makes edits to that region.
1495    *
1496    * @param connection connection we're using
1497    * @param regionInfo region to update location of
1498    * @param sn Server name
1499    * @throws IOException
1500    */
1501   public static void updateRegionLocation(Connection connection,
1502                                           HRegionInfo regionInfo, ServerName sn, long updateSeqNum)
1503     throws IOException {
1504     updateLocation(connection, regionInfo, sn, updateSeqNum);
1505   }
1506 
1507   /**
1508    * Updates the location of the specified region to be the specified server.
1509    * <p>
1510    * Connects to the specified server which should be hosting the specified
1511    * catalog region name to perform the edit.
1512    *
1513    * @param connection connection we're using
1514    * @param regionInfo region to update location of
1515    * @param sn Server name
1516    * @param openSeqNum the latest sequence number obtained when the region was open
1517    * @throws IOException In particular could throw {@link java.net.ConnectException}
1518    * if the server is down on other end.
1519    */
1520   private static void updateLocation(final Connection connection,
1521                                      HRegionInfo regionInfo, ServerName sn, long openSeqNum)
1522     throws IOException {
1523     // region replicas are kept in the primary region's row
1524     long time = EnvironmentEdgeManager.currentTime();
1525     Put put = new Put(getMetaKeyForRegion(regionInfo), time);
1526     addLocation(put, sn, openSeqNum, regionInfo.getReplicaId());
1527     putToMetaTable(connection, put);
1528     LOG.info("Updated row " + regionInfo.getRegionNameAsString() +
1529       " with server=" + sn);
1530   }
1531 
1532   /**
1533    * Deletes the specified region from META.
1534    * @param connection connection we're using
1535    * @param regionInfo region to be deleted from META
1536    * @throws IOException
1537    */
1538   public static void deleteRegion(Connection connection,
1539                                   HRegionInfo regionInfo)
1540     throws IOException {
1541     long time = EnvironmentEdgeManager.currentTime();
1542     Delete delete = new Delete(regionInfo.getRegionName());
1543     delete.addFamily(getCatalogFamily(), time);
1544     deleteFromMetaTable(connection, delete);
1545     LOG.info("Deleted " + regionInfo.getRegionNameAsString());
1546   }
1547 
1548   /**
1549    * Deletes the specified regions from META.
1550    * @param connection connection we're using
1551    * @param regionsInfo list of regions to be deleted from META
1552    * @throws IOException
1553    */
1554   public static void deleteRegions(Connection connection,
1555                                    List<HRegionInfo> regionsInfo) throws IOException {
1556     List<Delete> deletes = new ArrayList<Delete>(regionsInfo.size());
1557     long time = EnvironmentEdgeManager.currentTime();
1558     for (HRegionInfo hri: regionsInfo) {
1559       Delete e = new Delete(hri.getRegionName());
1560       e.addFamily(getCatalogFamily(), time);
1561       deletes.add(e);
1562     }
1563     deleteFromMetaTable(connection, deletes);
1564     LOG.info("Deleted " + regionsInfo);
1565   }
1566 
1567   /**
1568    * Adds and Removes the specified regions from hbase:meta
1569    * @param connection connection we're using
1570    * @param regionsToRemove list of regions to be deleted from META
1571    * @param regionsToAdd list of regions to be added to META
1572    * @throws IOException
1573    */
1574   public static void mutateRegions(Connection connection,
1575                                    final List<HRegionInfo> regionsToRemove,
1576                                    final List<HRegionInfo> regionsToAdd)
1577     throws IOException {
1578     List<Mutation> mutation = new ArrayList<Mutation>();
1579     if (regionsToRemove != null) {
1580       for (HRegionInfo hri: regionsToRemove) {
1581         mutation.add(makeDeleteFromRegionInfo(hri));
1582       }
1583     }
1584     if (regionsToAdd != null) {
1585       for (HRegionInfo hri: regionsToAdd) {
1586         mutation.add(makePutFromRegionInfo(hri));
1587       }
1588     }
1589     mutateMetaTable(connection, mutation);
1590     if (regionsToRemove != null && regionsToRemove.size() > 0) {
1591       LOG.debug("Deleted " + regionsToRemove);
1592     }
1593     if (regionsToAdd != null && regionsToAdd.size() > 0) {
1594       LOG.debug("Added " + regionsToAdd);
1595     }
1596   }
1597 
1598   /**
1599    * Overwrites the specified regions from hbase:meta
1600    * @param connection connection we're using
1601    * @param regionInfos list of regions to be added to META
1602    * @throws IOException
1603    */
1604   public static void overwriteRegions(Connection connection,
1605       List<HRegionInfo> regionInfos, int regionReplication) throws IOException {
1606     deleteRegions(connection, regionInfos);
1607     // Why sleep? This is the easiest way to ensure that the previous deletes does not
1608     // eclipse the following puts, that might happen in the same ts from the server.
1609     // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed,
1610     // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep.
1611     Threads.sleep(20);
1612     addRegionsToMeta(connection, regionInfos, regionReplication);
1613     LOG.info("Overwritten " + regionInfos);
1614   }
1615 
1616   /**
1617    * Deletes merge qualifiers for the specified merged region.
1618    * @param connection connection we're using
1619    * @param mergedRegion
1620    * @throws IOException
1621    */
1622   public static void deleteMergeQualifiers(Connection connection,
1623                                            final HRegionInfo mergedRegion) throws IOException {
1624     long time = EnvironmentEdgeManager.currentTime();
1625     Delete delete = new Delete(mergedRegion.getRegionName());
1626     delete.addColumns(getCatalogFamily(), HConstants.MERGEA_QUALIFIER, time);
1627     delete.addColumns(getCatalogFamily(), HConstants.MERGEB_QUALIFIER, time);
1628     deleteFromMetaTable(connection, delete);
1629     LOG.info("Deleted references in merged region "
1630       + mergedRegion.getRegionNameAsString() + ", qualifier="
1631       + Bytes.toStringBinary(HConstants.MERGEA_QUALIFIER) + " and qualifier="
1632       + Bytes.toStringBinary(HConstants.MERGEB_QUALIFIER));
1633   }
1634 
1635   private static Put addRegionInfo(final Put p, final HRegionInfo hri)
1636     throws IOException {
1637     p.addImmutable(getCatalogFamily(), HConstants.REGIONINFO_QUALIFIER,
1638       hri.toByteArray());
1639     return p;
1640   }
1641 
1642   public static Put addLocation(final Put p, final ServerName sn, long openSeqNum, int replicaId){
1643     // using regionserver's local time as the timestamp of Put.
1644     // See: HBASE-11536
1645     long now = EnvironmentEdgeManager.currentTime();
1646     p.addImmutable(getCatalogFamily(), getServerColumn(replicaId), now,
1647       Bytes.toBytes(sn.getHostAndPort()));
1648     p.addImmutable(getCatalogFamily(), getStartCodeColumn(replicaId), now,
1649       Bytes.toBytes(sn.getStartcode()));
1650     p.addImmutable(getCatalogFamily(), getSeqNumColumn(replicaId), now,
1651       Bytes.toBytes(openSeqNum));
1652     return p;
1653   }
1654 
1655   public static Put addEmptyLocation(final Put p, int replicaId) {
1656     long now = EnvironmentEdgeManager.currentTime();
1657     p.addImmutable(getCatalogFamily(), getServerColumn(replicaId), now, null);
1658     p.addImmutable(getCatalogFamily(), getStartCodeColumn(replicaId), now, null);
1659     p.addImmutable(getCatalogFamily(), getSeqNumColumn(replicaId), now, null);
1660     return p;
1661   }
1662 }