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