View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.catalog;
21  
22  import java.io.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.NavigableMap;
27  import java.util.Set;
28  import java.util.TreeMap;
29  import java.util.TreeSet;
30  
31  import org.apache.hadoop.hbase.HConstants;
32  import org.apache.hadoop.hbase.HRegionInfo;
33  import org.apache.hadoop.hbase.HServerAddress;
34  import org.apache.hadoop.hbase.HServerInfo;
35  import org.apache.hadoop.hbase.HTableDescriptor;
36  import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
37  import org.apache.hadoop.hbase.NotServingRegionException;
38  import org.apache.hadoop.hbase.client.Get;
39  import org.apache.hadoop.hbase.client.Result;
40  import org.apache.hadoop.hbase.client.Scan;
41  import org.apache.hadoop.hbase.ipc.HRegionInterface;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.apache.hadoop.hbase.util.Pair;
44  import org.apache.hadoop.hbase.util.Writables;
45  import org.apache.hadoop.ipc.RemoteException;
46  
47  /**
48   * Reads region and assignment information from <code>.META.</code>.
49   * <p>
50   * Uses the {@link CatalogTracker} to obtain locations and connections to
51   * catalogs.
52   */
53  public class MetaReader {
54    public static final byte [] META_REGION_PREFIX;
55    static {
56      // Copy the prefix from FIRST_META_REGIONINFO into META_REGION_PREFIX.
57      // FIRST_META_REGIONINFO == '.META.,,1'.  META_REGION_PREFIX == '.META.,'
58      int len = HRegionInfo.FIRST_META_REGIONINFO.getRegionName().length - 2;
59      META_REGION_PREFIX = new byte [len];
60      System.arraycopy(HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), 0,
61        META_REGION_PREFIX, 0, len);
62    }
63  
64    /**
65     * @param ct
66     * @param tableName A user tablename or a .META. table name.
67     * @return Interface on to server hosting the <code>-ROOT-</code> or
68     * <code>.META.</code> regions.
69     * @throws NotAllMetaRegionsOnlineException
70     * @throws IOException
71     */
72    private static HRegionInterface getCatalogRegionInterface(final CatalogTracker ct,
73        final byte [] tableName)
74    throws NotAllMetaRegionsOnlineException, IOException {
75      return Bytes.equals(HConstants.META_TABLE_NAME, tableName)?
76        ct.waitForRootServerConnectionDefault():
77        ct.waitForMetaServerConnectionDefault();
78    }
79  
80    /**
81     * @param tableName
82     * @return Returns region name to look in for regions for <code>tableName</code>;
83     * e.g. if we are looking for <code>.META.</code> regions, we need to look
84     * in the <code>-ROOT-</code> region, else if a user table, we need to look
85     * in the <code>.META.</code> region.
86     */
87    private static byte [] getCatalogRegionNameForTable(final byte [] tableName) {
88      return Bytes.equals(HConstants.META_TABLE_NAME, tableName)?
89        HRegionInfo.ROOT_REGIONINFO.getRegionName():
90        HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
91    }
92  
93    /**
94     * @param regionName
95     * @return Returns region name to look in for <code>regionName</code>;
96     * e.g. if we are looking for <code>.META.,,1</code> region, we need to look
97     * in <code>-ROOT-</code> region, else if a user region, we need to look
98     * in the <code>.META.,,1</code> region.
99     */
100   private static byte [] getCatalogRegionNameForRegion(final byte [] regionName) {
101     return isMetaRegion(regionName)?
102       HRegionInfo.ROOT_REGIONINFO.getRegionName():
103       HRegionInfo.FIRST_META_REGIONINFO.getRegionName();
104   }
105 
106   /**
107    * @param regionName
108    * @return True if <code>regionName</code> is from <code>.META.</code> table.
109    */
110   private static boolean isMetaRegion(final byte [] regionName) {
111     if (regionName.length < META_REGION_PREFIX.length + 2 /* ',', + '1' */) {
112       // Can't be meta table region.
113       return false;
114     }
115     // Compare the prefix of regionName.  If it matches META_REGION_PREFIX prefix,
116     // then this is region from .META. table.
117     return Bytes.compareTo(regionName, 0, META_REGION_PREFIX.length,
118       META_REGION_PREFIX, 0, META_REGION_PREFIX.length) == 0;
119   }
120 
121   /**
122    * Performs a full scan of <code>.META.</code>.
123    * <p>
124    * Returns a map of every region to it's currently assigned server, according
125    * to META.  If the region does not have an assignment it will have a null
126    * value in the map.
127    *
128    * @return map of regions to their currently assigned server
129    * @throws IOException
130    */
131   public static Map<HRegionInfo,HServerAddress> fullScan(
132       CatalogTracker catalogTracker)
133   throws IOException {
134     return fullScan(catalogTracker, new TreeSet<String>());
135   }
136 
137   /**
138    * Performs a full scan of <code>.META.</code>, skipping regions from any
139    * tables in the specified set of disabled tables.
140    * <p>
141    * Returns a map of every region to it's currently assigned server, according
142    * to META.  If the region does not have an assignment it will have a null
143    * value in the map.
144    *
145    * @param catalogTracker
146    * @param disabledTables set of disabled tables that will not be returned
147    * @return map of regions to their currently assigned server
148    * @throws IOException
149    */
150   public static Map<HRegionInfo,HServerAddress> fullScan(
151       CatalogTracker catalogTracker, final Set<String> disabledTables)
152   throws IOException {
153     return fullScan(catalogTracker, disabledTables, false);
154   }
155 
156   /**
157    * Performs a full scan of <code>.META.</code>, skipping regions from any
158    * tables in the specified set of disabled tables.
159    * <p>
160    * Returns a map of every region to it's currently assigned server, according
161    * to META.  If the region does not have an assignment it will have a null
162    * value in the map.
163    *
164    * @param catalogTracker
165    * @param disabledTables set of disabled tables that will not be returned
166    * @param excludeOfflinedSplitParents If true, do not include offlined split
167    * parents in the return.
168    * @return map of regions to their currently assigned server
169    * @throws IOException
170    */
171   public static Map<HRegionInfo,HServerAddress> fullScan(
172       CatalogTracker catalogTracker, final Set<String> disabledTables,
173       final boolean excludeOfflinedSplitParents)
174   throws IOException {
175     final Map<HRegionInfo,HServerAddress> regions =
176       new TreeMap<HRegionInfo,HServerAddress>();
177     Visitor v = new Visitor() {
178       @Override
179       public boolean visit(Result r) throws IOException {
180         if (r ==  null || r.isEmpty()) return true;
181         Pair<HRegionInfo,HServerAddress> region = metaRowToRegionPair(r);
182         if (region == null) return true;
183         HRegionInfo hri = region.getFirst();
184         if (disabledTables.contains(
185             hri.getTableDesc().getNameAsString())) return true;
186         // Are we to include split parents in the list?
187         if (excludeOfflinedSplitParents && hri.isSplitParent()) return true;
188         regions.put(hri, region.getSecond());
189         return true;
190       }
191     };
192     fullScan(catalogTracker, v);
193     return regions;
194   }
195 
196   /**
197    * Performs a full scan of <code>.META.</code>.
198    * <p>
199    * Returns a map of every region to it's currently assigned server, according
200    * to META.  If the region does not have an assignment it will have a null
201    * value in the map.
202    * <p>
203    * Returns HServerInfo which includes server startcode.
204    *
205    * @return map of regions to their currently assigned server
206    * @throws IOException
207    */
208   public static List<Result> fullScanOfResults(
209       CatalogTracker catalogTracker)
210   throws IOException {
211     final List<Result> regions = new ArrayList<Result>();
212     Visitor v = new Visitor() {
213       @Override
214       public boolean visit(Result r) throws IOException {
215         if (r ==  null || r.isEmpty()) return true;
216         regions.add(r);
217         return true;
218       }
219     };
220     fullScan(catalogTracker, v);
221     return regions;
222   }
223 
224   /**
225    * Performs a full scan of <code>.META.</code>.
226    * <p>
227    * Returns a map of every region to it's currently assigned server, according
228    * to META.  If the region does not have an assignment it will have a null
229    * value in the map.
230    * @param catalogTracker
231    * @param visitor
232    * @throws IOException
233    */
234   public static void fullScan(CatalogTracker catalogTracker,
235       final Visitor visitor)
236   throws IOException {
237     fullScan(catalogTracker, visitor, null);
238   }
239 
240   /**
241    * Performs a full scan of <code>.META.</code>.
242    * <p>
243    * Returns a map of every region to it's currently assigned server, according
244    * to META.  If the region does not have an assignment it will have a null
245    * value in the map.
246    * @param catalogTracker
247    * @param visitor
248    * @param startrow Where to start the scan. Pass null if want to begin scan
249    * at first row.
250    * @throws IOException
251    */
252   public static void fullScan(CatalogTracker catalogTracker,
253       final Visitor visitor, final byte [] startrow)
254   throws IOException {
255     HRegionInterface metaServer =
256       catalogTracker.waitForMetaServerConnectionDefault();
257     Scan scan = new Scan();
258     if (startrow != null) scan.setStartRow(startrow);
259     scan.addFamily(HConstants.CATALOG_FAMILY);
260     long scannerid = metaServer.openScanner(
261         HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
262     try {
263       Result data;
264       while((data = metaServer.next(scannerid)) != null) {
265         if (!data.isEmpty()) visitor.visit(data);
266       }
267     } finally {
268       metaServer.close(scannerid);
269     }
270     return;
271   }
272 
273   /**
274    * Reads the location of META from ROOT.
275    * @param metaServer connection to server hosting ROOT
276    * @return location of META in ROOT, null if not available
277    * @throws IOException
278    */
279   public static HServerAddress readMetaLocation(HRegionInterface metaServer)
280   throws IOException {
281     return readLocation(metaServer, CatalogTracker.ROOT_REGION,
282         CatalogTracker.META_REGION);
283   }
284 
285   /**
286    * Reads the location of the specified region from META.
287    * @param catalogTracker
288    * @param regionName region to read location of
289    * @return location of region in META, null if not available
290    * @throws IOException
291    */
292   public static HServerAddress readRegionLocation(CatalogTracker catalogTracker,
293       byte [] regionName)
294   throws IOException {
295     if (isMetaRegion(regionName)) throw new IllegalArgumentException("See readMetaLocation");
296     return readLocation(catalogTracker.waitForMetaServerConnectionDefault(),
297         CatalogTracker.META_REGION, regionName);
298   }
299 
300   private static HServerAddress readLocation(HRegionInterface metaServer,
301       byte [] catalogRegionName, byte [] regionName)
302   throws IOException {
303     Result r = null;
304     try {
305       r = metaServer.get(catalogRegionName,
306         new Get(regionName).addColumn(HConstants.CATALOG_FAMILY,
307         HConstants.SERVER_QUALIFIER));
308     } catch (java.net.SocketTimeoutException e) {
309       // Treat this exception + message as unavailable catalog table. Catch it
310       // and fall through to return a null
311     } catch (java.net.ConnectException e) {
312       if (e.getMessage() != null &&
313           e.getMessage().contains("Connection refused")) {
314         // Treat this exception + message as unavailable catalog table. Catch it
315         // and fall through to return a null
316       } else {
317         throw e;
318       }
319     } catch (RemoteException re) {
320       IOException ioe = re.unwrapRemoteException();
321       if (ioe instanceof NotServingRegionException) {
322         // Treat this NSRE as unavailable table.  Catch and fall through to
323         // return null below
324       } else if (ioe.getMessage().contains("Server not running")) {
325         // Treat as unavailable table.
326       } else {
327         throw re;
328       }
329     } catch (IOException e) {
330       if (e.getCause() != null && e.getCause() instanceof IOException &&
331           e.getCause().getMessage() != null &&
332           e.getCause().getMessage().contains("Connection reset by peer")) {
333         // Treat this exception + message as unavailable catalog table. Catch it
334         // and fall through to return a null
335       } else {
336         throw e;
337       }
338     }
339     if (r == null || r.isEmpty()) {
340       return null;
341     }
342     byte [] value = r.getValue(HConstants.CATALOG_FAMILY,
343       HConstants.SERVER_QUALIFIER);
344     return new HServerAddress(Bytes.toString(value));
345   }
346 
347   /**
348    * Gets the region info and assignment for the specified region from META.
349    * @param catalogTracker
350    * @param regionName
351    * @return region info and assignment from META, null if not available
352    * @throws IOException
353    */
354   public static Pair<HRegionInfo, HServerAddress> getRegion(
355       CatalogTracker catalogTracker, byte [] regionName)
356   throws IOException {
357     Get get = new Get(regionName);
358     get.addFamily(HConstants.CATALOG_FAMILY);
359     byte [] meta = getCatalogRegionNameForRegion(regionName);
360     Result r = catalogTracker.waitForMetaServerConnectionDefault().get(meta, get);
361     if(r == null || r.isEmpty()) {
362       return null;
363     }
364     return metaRowToRegionPair(r);
365   }
366 
367   /**
368    * @param data A .META. table row.
369    * @return A pair of the regioninfo and the server address from <code>data</code>
370    * or null for server address if no address set in .META. or null for a result
371    * if no HRegionInfo found.
372    * @throws IOException
373    */
374   public static Pair<HRegionInfo, HServerAddress> metaRowToRegionPair(
375       Result data) throws IOException {
376     byte [] bytes =
377       data.getValue(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
378     if (bytes == null) return null;
379     HRegionInfo info = Writables.getHRegionInfo(bytes);
380     final byte[] value = data.getValue(HConstants.CATALOG_FAMILY,
381       HConstants.SERVER_QUALIFIER);
382     if (value != null && value.length > 0) {
383       HServerAddress server = new HServerAddress(Bytes.toString(value));
384       return new Pair<HRegionInfo,HServerAddress>(info, server);
385     } else {
386       return new Pair<HRegionInfo, HServerAddress>(info, null);
387     }
388   }
389 
390   /**
391    * @param data A .META. table row.
392    * @return A pair of the regioninfo and the server info from <code>data</code>
393    * (or null for server address if no address set in .META.).
394    * @throws IOException
395    */
396   public static Pair<HRegionInfo, HServerInfo> metaRowToRegionPairWithInfo(
397       Result data) throws IOException {
398     byte [] bytes = data.getValue(HConstants.CATALOG_FAMILY,
399       HConstants.REGIONINFO_QUALIFIER);
400     if (bytes == null) return null;
401     HRegionInfo info = Writables.getHRegionInfo(bytes);
402     final byte[] value = data.getValue(HConstants.CATALOG_FAMILY,
403       HConstants.SERVER_QUALIFIER);
404     if (value != null && value.length > 0) {
405       final long startCode = Bytes.toLong(data.getValue(HConstants.CATALOG_FAMILY,
406           HConstants.STARTCODE_QUALIFIER));
407       HServerAddress server = new HServerAddress(Bytes.toString(value));
408       HServerInfo hsi = new HServerInfo(server, startCode, 0,
409           server.getHostname());
410       return new Pair<HRegionInfo,HServerInfo>(info, hsi);
411     } else {
412       return new Pair<HRegionInfo, HServerInfo>(info, null);
413     }
414   }
415 
416   /**
417    * Checks if the specified table exists.  Looks at the META table hosted on
418    * the specified server.
419    * @param catalogTracker
420    * @param tableName table to check
421    * @return true if the table exists in meta, false if not
422    * @throws IOException
423    */
424   public static boolean tableExists(CatalogTracker catalogTracker,
425       String tableName)
426   throws IOException {
427     if (tableName.equals(HTableDescriptor.ROOT_TABLEDESC.getNameAsString()) ||
428         tableName.equals(HTableDescriptor.META_TABLEDESC.getNameAsString())) {
429       // Catalog tables always exist.
430       return true;
431     }
432     HRegionInterface metaServer =
433       catalogTracker.waitForMetaServerConnectionDefault();
434     byte[] firstRowInTable = Bytes.toBytes(tableName + ",,");
435     Scan scan = new Scan(firstRowInTable);
436     scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
437     long scannerid = metaServer.openScanner(
438         HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
439     try {
440       Result data = metaServer.next(scannerid);
441       if (data != null && data.size() > 0) {
442         HRegionInfo info = Writables.getHRegionInfo(
443           data.getValue(HConstants.CATALOG_FAMILY,
444               HConstants.REGIONINFO_QUALIFIER));
445         if (info.getTableDesc().getNameAsString().equals(tableName)) {
446           // A region for this table already exists. Ergo table exists.
447           return true;
448         }
449       }
450       return false;
451     } finally {
452       metaServer.close(scannerid);
453     }
454   }
455 
456   /**
457    * Gets all of the regions of the specified table.
458    * @param catalogTracker
459    * @param tableName
460    * @return Ordered list of {@link HRegionInfo}.
461    * @throws IOException
462    */
463   public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
464       byte [] tableName)
465   throws IOException {
466     return getTableRegions(catalogTracker, tableName, false);
467   }
468 
469   /**
470    * Gets all of the regions of the specified table.
471    * @param catalogTracker
472    * @param tableName
473    * @param excludeOfflinedSplitParents If true, do not include offlined split
474    * parents in the return.
475    * @return Ordered list of {@link HRegionInfo}.
476    * @throws IOException
477    */
478   public static List<HRegionInfo> getTableRegions(CatalogTracker catalogTracker,
479       byte [] tableName, final boolean excludeOfflinedSplitParents)
480   throws IOException {
481     if (Bytes.equals(tableName, HConstants.ROOT_TABLE_NAME)) {
482       // If root, do a bit of special handling.
483       List<HRegionInfo> list = new ArrayList<HRegionInfo>();
484       list.add(HRegionInfo.ROOT_REGIONINFO);
485       return list;
486     } else if (Bytes.equals(tableName, HConstants.META_TABLE_NAME)) {
487       // Same for .META. table
488       List<HRegionInfo> list = new ArrayList<HRegionInfo>();
489       list.add(HRegionInfo.FIRST_META_REGIONINFO);
490       return list;
491     }
492 
493     // Its a user table.
494     HRegionInterface metaServer =
495       getCatalogRegionInterface(catalogTracker, tableName);
496     List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
497     String tableString = Bytes.toString(tableName);
498     byte[] firstRowInTable = Bytes.toBytes(tableString + ",,");
499     Scan scan = new Scan(firstRowInTable);
500     scan.addColumn(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER);
501     long scannerid =
502       metaServer.openScanner(getCatalogRegionNameForTable(tableName), scan);
503     try {
504       Result data;
505       while((data = metaServer.next(scannerid)) != null) {
506         if (data != null && data.size() > 0) {
507           HRegionInfo info = Writables.getHRegionInfo(
508               data.getValue(HConstants.CATALOG_FAMILY,
509                   HConstants.REGIONINFO_QUALIFIER));
510           if (info.getTableDesc().getNameAsString().equals(tableString)) {
511             // Are we to include split parents in the list?
512             if (excludeOfflinedSplitParents && info.isSplitParent()) continue;
513             regions.add(info);
514           } else {
515             break;
516           }
517         }
518       }
519       return regions;
520     } finally {
521       metaServer.close(scannerid);
522     }
523   }
524 
525   /**
526    * @param catalogTracker
527    * @param tableName
528    * @return Return list of regioninfos and server addresses.
529    * @throws IOException
530    * @throws InterruptedException
531    */
532   public static List<Pair<HRegionInfo, HServerAddress>>
533   getTableRegionsAndLocations(CatalogTracker catalogTracker, String tableName)
534   throws IOException, InterruptedException {
535     byte [] tableNameBytes = Bytes.toBytes(tableName);
536     if (Bytes.equals(tableNameBytes, HConstants.ROOT_TABLE_NAME)) {
537       // If root, do a bit of special handling.
538       HServerAddress hsa = catalogTracker.getRootLocation();
539       List<Pair<HRegionInfo, HServerAddress>> list =
540         new ArrayList<Pair<HRegionInfo, HServerAddress>>();
541       list.add(new Pair<HRegionInfo, HServerAddress>(HRegionInfo.ROOT_REGIONINFO, hsa));
542       return list;
543     }
544     HRegionInterface metaServer =
545       getCatalogRegionInterface(catalogTracker, tableNameBytes);
546     List<Pair<HRegionInfo, HServerAddress>> regions =
547       new ArrayList<Pair<HRegionInfo, HServerAddress>>();
548     byte[] firstRowInTable = Bytes.toBytes(tableName + ",,");
549     Scan scan = new Scan(firstRowInTable);
550     scan.addFamily(HConstants.CATALOG_FAMILY);
551     long scannerid =
552       metaServer.openScanner(getCatalogRegionNameForTable(tableNameBytes), scan);
553     try {
554       Result data;
555       while((data = metaServer.next(scannerid)) != null) {
556         if (data != null && data.size() > 0) {
557           Pair<HRegionInfo, HServerAddress> region = metaRowToRegionPair(data);
558           if (region == null) continue;
559           if (region.getFirst().getTableDesc().getNameAsString().equals(
560               tableName)) {
561             regions.add(region);
562           } else {
563             break;
564           }
565         }
566       }
567       return regions;
568     } finally {
569       metaServer.close(scannerid);
570     }
571   }
572 
573   /**
574    * @param catalogTracker
575    * @param hsi Server specification
576    * @return List of user regions installed on this server (does not include
577    * catalog regions).
578    * @throws IOException
579    */
580   public static NavigableMap<HRegionInfo, Result>
581   getServerUserRegions(CatalogTracker catalogTracker, final HServerInfo hsi)
582   throws IOException {
583     HRegionInterface metaServer =
584       catalogTracker.waitForMetaServerConnectionDefault();
585     NavigableMap<HRegionInfo, Result> hris = new TreeMap<HRegionInfo, Result>();
586     Scan scan = new Scan();
587     scan.addFamily(HConstants.CATALOG_FAMILY);
588     long scannerid = metaServer.openScanner(
589         HRegionInfo.FIRST_META_REGIONINFO.getRegionName(), scan);
590     try {
591       Result result;
592       while((result = metaServer.next(scannerid)) != null) {
593         if (result != null && result.size() > 0) {
594           Pair<HRegionInfo, HServerInfo> pair =
595             metaRowToRegionPairWithInfo(result);
596           if (pair == null) continue;
597           if (pair.getSecond() == null || !pair.getSecond().equals(hsi)) {
598             continue;
599           }
600           hris.put(pair.getFirst(), result);
601         }
602       }
603       return hris;
604     } finally {
605       metaServer.close(scannerid);
606     }
607   }
608 
609   /**
610    * Implementations 'visit' a catalog table row.
611    */
612   public interface Visitor {
613     /**
614      * Visit the catalog table row.
615      * @param r A row from catalog table
616      * @return True if we are to proceed scanning the table, else false if
617      * we are to stop now.
618      */
619     public boolean visit(final Result r) throws IOException;
620   }
621 }