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