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.catalog;
19  
20  import java.io.IOException;
21  import java.io.InterruptedIOException;
22  import java.net.ConnectException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.classification.InterfaceAudience;
29  import org.apache.hadoop.hbase.DoNotRetryIOException;
30  import org.apache.hadoop.hbase.HConstants;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.NotAllMetaRegionsOnlineException;
33  import org.apache.hadoop.hbase.ServerName;
34  import org.apache.hadoop.hbase.client.Delete;
35  import org.apache.hadoop.hbase.client.HTable;
36  import org.apache.hadoop.hbase.client.Mutation;
37  import org.apache.hadoop.hbase.client.Put;
38  import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
39  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
40  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto.MutationType;
41  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationService;
42  import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.apache.hadoop.hbase.util.Threads;
45  
46  import com.google.protobuf.ServiceException;
47  
48  /**
49   * Writes region and assignment information to <code>hbase:meta</code>.
50   * TODO: Put MetaReader and MetaEditor together; doesn't make sense having
51   * them distinct. see HBASE-3475.
52   */
53  @InterfaceAudience.Private
54  public class MetaEditor {
55    // TODO: Strip CatalogTracker from this class.  Its all over and in the end
56    // its only used to get its Configuration so we can get associated
57    // Connection.
58    private static final Log LOG = LogFactory.getLog(MetaEditor.class);
59  
60    /**
61     * Generates and returns a Put containing the region into for the catalog table
62     */
63    public static Put makePutFromRegionInfo(HRegionInfo regionInfo)
64    throws IOException {
65      Put put = new Put(regionInfo.getRegionName());
66      addRegionInfo(put, regionInfo);
67      return put;
68    }
69  
70    /**
71     * Generates and returns a Delete containing the region info for the catalog
72     * table
73     */
74    public static Delete makeDeleteFromRegionInfo(HRegionInfo regionInfo) {
75      if (regionInfo == null) {
76        throw new IllegalArgumentException("Can't make a delete for null region");
77      }
78      Delete delete = new Delete(regionInfo.getRegionName());
79      return delete;
80    }
81  
82    /**
83     * Adds split daughters to the Put
84     */
85    public static Put addDaughtersToPut(Put put, HRegionInfo splitA, HRegionInfo splitB) {
86      if (splitA != null) {
87        put.addImmutable(
88            HConstants.CATALOG_FAMILY, HConstants.SPLITA_QUALIFIER, splitA.toByteArray());
89      }
90      if (splitB != null) {
91        put.addImmutable(
92            HConstants.CATALOG_FAMILY, HConstants.SPLITB_QUALIFIER, splitB.toByteArray());
93      }
94      return put;
95    }
96  
97    /**
98     * Put the passed <code>p</code> to the <code>hbase:meta</code> table.
99     * @param ct CatalogTracker on whose back we will ride the edit.
100    * @param p Put to add to hbase:meta
101    * @throws IOException
102    */
103   static void putToMetaTable(final CatalogTracker ct, final Put p)
104   throws IOException {
105     put(MetaReader.getMetaHTable(ct), p);
106   }
107 
108   /**
109    * Put the passed <code>p</code> to a catalog table.
110    * @param ct CatalogTracker on whose back we will ride the edit.
111    * @param p Put to add
112    * @throws IOException
113    */
114   static void putToCatalogTable(final CatalogTracker ct, final Put p)
115   throws IOException {
116     put(MetaReader.getCatalogHTable(ct), p);
117   }
118 
119   /**
120    * @param t Table to use (will be closed when done).
121    * @param p
122    * @throws IOException
123    */
124   private static void put(final HTable t, final Put p) throws IOException {
125     try {
126       t.put(p);
127     } finally {
128       t.close();
129     }
130   }
131 
132   /**
133    * Put the passed <code>ps</code> to the <code>hbase:meta</code> table.
134    * @param ct CatalogTracker on whose back we will ride the edit.
135    * @param ps Put to add to hbase:meta
136    * @throws IOException
137    */
138   public static void putsToMetaTable(final CatalogTracker ct, final List<Put> ps)
139   throws IOException {
140     HTable t = MetaReader.getMetaHTable(ct);
141     try {
142       t.put(ps);
143     } finally {
144       t.close();
145     }
146   }
147 
148   /**
149    * Delete the passed <code>d</code> from the <code>hbase:meta</code> table.
150    * @param ct CatalogTracker on whose back we will ride the edit.
151    * @param d Delete to add to hbase:meta
152    * @throws IOException
153    */
154   static void deleteFromMetaTable(final CatalogTracker ct, final Delete d)
155       throws IOException {
156     List<Delete> dels = new ArrayList<Delete>(1);
157     dels.add(d);
158     deleteFromMetaTable(ct, dels);
159   }
160 
161   /**
162    * Delete the passed <code>deletes</code> from the <code>hbase:meta</code> table.
163    * @param ct CatalogTracker on whose back we will ride the edit.
164    * @param deletes Deletes to add to hbase:meta  This list should support #remove.
165    * @throws IOException
166    */
167   public static void deleteFromMetaTable(final CatalogTracker ct, final List<Delete> deletes)
168       throws IOException {
169     HTable t = MetaReader.getMetaHTable(ct);
170     try {
171       t.delete(deletes);
172     } finally {
173       t.close();
174     }
175   }
176 
177   /**
178    * Execute the passed <code>mutations</code> against <code>hbase:meta</code> table.
179    * @param ct CatalogTracker on whose back we will ride the edit.
180    * @param mutations Puts and Deletes to execute on hbase:meta
181    * @throws IOException
182    */
183   public static void mutateMetaTable(final CatalogTracker ct, final List<Mutation> mutations)
184       throws IOException {
185     HTable t = MetaReader.getMetaHTable(ct);
186     try {
187       t.batch(mutations);
188     } catch (InterruptedException e) {
189       InterruptedIOException ie = new InterruptedIOException(e.getMessage());
190       ie.initCause(e);
191       throw ie;
192     } finally {
193       t.close();
194     }
195   }
196 
197   /**
198    * Adds a hbase:meta row for the specified new region.
199    * @param regionInfo region information
200    * @throws IOException if problem connecting or updating meta
201    */
202   public static void addRegionToMeta(CatalogTracker catalogTracker,
203       HRegionInfo regionInfo)
204   throws IOException {
205     putToMetaTable(catalogTracker, makePutFromRegionInfo(regionInfo));
206     LOG.info("Added " + regionInfo.getRegionNameAsString());
207   }
208 
209   /**
210    * Adds a hbase:meta row for the specified new region to the given catalog table. The
211    * HTable is not flushed or closed.
212    * @param meta the HTable for META
213    * @param regionInfo region information
214    * @throws IOException if problem connecting or updating meta
215    */
216   public static void addRegionToMeta(HTable meta, HRegionInfo regionInfo) throws IOException {
217     addRegionToMeta(meta, regionInfo, null, null);
218   }
219 
220   /**
221    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this does
222    * not add its daughter's as different rows, but adds information about the daughters
223    * in the same row as the parent. Use
224    * {@link #splitRegion(CatalogTracker, HRegionInfo, HRegionInfo, HRegionInfo, ServerName)}
225    * if you want to do that.
226    * @param meta the HTable for META
227    * @param regionInfo region information
228    * @param splitA first split daughter of the parent regionInfo
229    * @param splitB second split daughter of the parent regionInfo
230    * @throws IOException if problem connecting or updating meta
231    */
232   public static void addRegionToMeta(HTable meta, HRegionInfo regionInfo,
233       HRegionInfo splitA, HRegionInfo splitB) throws IOException {
234     Put put = makePutFromRegionInfo(regionInfo);
235     addDaughtersToPut(put, splitA, splitB);
236     meta.put(put);
237     if (LOG.isDebugEnabled()) {
238       LOG.debug("Added " + regionInfo.getRegionNameAsString());
239     }
240   }
241 
242   /**
243    * Adds a (single) hbase:meta row for the specified new region and its daughters. Note that this does
244    * not add its daughter's as different rows, but adds information about the daughters
245    * in the same row as the parent. Use
246    * {@link #splitRegion(CatalogTracker, HRegionInfo, HRegionInfo, HRegionInfo, ServerName)}
247    * if you want to do that.
248    * @param catalogTracker CatalogTracker on whose back we will ride the edit.
249    * @param regionInfo region information
250    * @param splitA first split daughter of the parent regionInfo
251    * @param splitB second split daughter of the parent regionInfo
252    * @throws IOException if problem connecting or updating meta
253    */
254   public static void addRegionToMeta(CatalogTracker catalogTracker, HRegionInfo regionInfo,
255       HRegionInfo splitA, HRegionInfo splitB) throws IOException {
256     HTable meta = MetaReader.getMetaHTable(catalogTracker);
257     try {
258       addRegionToMeta(meta, regionInfo, splitA, splitB);
259     } finally {
260       meta.close();
261     }
262   }
263 
264   /**
265    * Adds a hbase:meta row for each of the specified new regions.
266    * @param catalogTracker CatalogTracker
267    * @param regionInfos region information list
268    * @throws IOException if problem connecting or updating meta
269    */
270   public static void addRegionsToMeta(CatalogTracker catalogTracker,
271       List<HRegionInfo> regionInfos)
272   throws IOException {
273     List<Put> puts = new ArrayList<Put>();
274     for (HRegionInfo regionInfo : regionInfos) {
275       puts.add(makePutFromRegionInfo(regionInfo));
276     }
277     putsToMetaTable(catalogTracker, puts);
278     LOG.info("Added " + puts.size());
279   }
280 
281   /**
282    * Adds a daughter region entry to meta.
283    * @param regionInfo the region to put
284    * @param sn the location of the region
285    * @param openSeqNum the latest sequence number obtained when the region was open
286    */
287   public static void addDaughter(final CatalogTracker catalogTracker,
288       final HRegionInfo regionInfo, final ServerName sn, final long openSeqNum)
289   throws NotAllMetaRegionsOnlineException, IOException {
290     Put put = new Put(regionInfo.getRegionName());
291     addRegionInfo(put, regionInfo);
292     if (sn != null) {
293       addLocation(put, sn, openSeqNum);
294     }
295     putToMetaTable(catalogTracker, put);
296     LOG.info("Added daughter " + regionInfo.getEncodedName() +
297       (sn == null? ", serverName=null": ", serverName=" + sn.toString()));
298   }
299 
300   /**
301    * Merge the two regions into one in an atomic operation. Deletes the two
302    * merging regions in hbase:meta and adds the merged region with the information of
303    * two merging regions.
304    * @param catalogTracker the catalog tracker
305    * @param mergedRegion the merged region
306    * @param regionA
307    * @param regionB
308    * @param sn the location of the region
309    * @throws IOException
310    */
311   public static void mergeRegions(final CatalogTracker catalogTracker,
312       HRegionInfo mergedRegion, HRegionInfo regionA, HRegionInfo regionB,
313       ServerName sn) throws IOException {
314     HTable meta = MetaReader.getMetaHTable(catalogTracker);
315     try {
316       HRegionInfo copyOfMerged = new HRegionInfo(mergedRegion);
317 
318       // Put for parent
319       Put putOfMerged = makePutFromRegionInfo(copyOfMerged);
320       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER,
321           regionA.toByteArray());
322       putOfMerged.addImmutable(HConstants.CATALOG_FAMILY, HConstants.MERGEB_QUALIFIER,
323           regionB.toByteArray());
324 
325       // Deletes for merging regions
326       Delete deleteA = makeDeleteFromRegionInfo(regionA);
327       Delete deleteB = makeDeleteFromRegionInfo(regionB);
328 
329       // The merged is a new region, openSeqNum = 1 is fine.
330       addLocation(putOfMerged, sn, 1);
331 
332       byte[] tableRow = Bytes.toBytes(mergedRegion.getRegionNameAsString()
333           + HConstants.DELIMITER);
334       multiMutate(meta, tableRow, putOfMerged, deleteA, deleteB);
335     } finally {
336       meta.close();
337     }
338   }
339 
340   /**
341    * Splits the region into two in an atomic operation. Offlines the parent
342    * region with the information that it is split into two, and also adds
343    * the daughter regions. Does not add the location information to the daughter
344    * regions since they are not open yet.
345    * @param catalogTracker the catalog tracker
346    * @param parent the parent region which is split
347    * @param splitA Split daughter region A
348    * @param splitB Split daughter region A
349    * @param sn the location of the region
350    */
351   public static void splitRegion(final CatalogTracker catalogTracker,
352       HRegionInfo parent, HRegionInfo splitA, HRegionInfo splitB,
353       ServerName sn) throws IOException {
354     HTable meta = MetaReader.getMetaHTable(catalogTracker);
355     try {
356       HRegionInfo copyOfParent = new HRegionInfo(parent);
357       copyOfParent.setOffline(true);
358       copyOfParent.setSplit(true);
359 
360       //Put for parent
361       Put putParent = makePutFromRegionInfo(copyOfParent);
362       addDaughtersToPut(putParent, splitA, splitB);
363 
364       //Puts for daughters
365       Put putA = makePutFromRegionInfo(splitA);
366       Put putB = makePutFromRegionInfo(splitB);
367 
368       addLocation(putA, sn, 1); //these are new regions, openSeqNum = 1 is fine.
369       addLocation(putB, sn, 1);
370 
371       byte[] tableRow = Bytes.toBytes(parent.getRegionNameAsString() + HConstants.DELIMITER);
372       multiMutate(meta, tableRow, putParent, putA, putB);
373     } finally {
374       meta.close();
375     }
376   }
377 
378   /**
379    * Performs an atomic multi-Mutate operation against the given table.
380    */
381   private static void multiMutate(HTable table, byte[] row, Mutation... mutations) throws IOException {
382     CoprocessorRpcChannel channel = table.coprocessorService(row);
383     MutateRowsRequest.Builder mmrBuilder = MutateRowsRequest.newBuilder();
384     for (Mutation mutation : mutations) {
385       if (mutation instanceof Put) {
386         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(MutationType.PUT, mutation));
387       } else if (mutation instanceof Delete) {
388         mmrBuilder.addMutationRequest(ProtobufUtil.toMutation(MutationType.DELETE, mutation));
389       } else {
390         throw new DoNotRetryIOException("multi in MetaEditor doesn't support "
391             + mutation.getClass().getName());
392       }
393     }
394 
395     MultiRowMutationService.BlockingInterface service =
396         MultiRowMutationService.newBlockingStub(channel);
397     try {
398       service.mutateRows(null, mmrBuilder.build());
399     } catch (ServiceException ex) {
400       ProtobufUtil.toIOException(ex);
401     }
402   }
403 
404 
405   /**
406    * Updates the location of the specified hbase:meta region in ROOT to be the
407    * specified server hostname and startcode.
408    * <p>
409    * Uses passed catalog tracker to get a connection to the server hosting
410    * ROOT and makes edits to that region.
411    *
412    * @param catalogTracker catalog tracker
413    * @param regionInfo region to update location of
414    * @param sn Server name
415    * @param openSeqNum the latest sequence number obtained when the region was open
416    * @throws IOException
417    * @throws ConnectException Usually because the regionserver carrying hbase:meta
418    * is down.
419    * @throws NullPointerException Because no -ROOT- server connection
420    */
421   public static void updateMetaLocation(CatalogTracker catalogTracker,
422       HRegionInfo regionInfo, ServerName sn, long openSeqNum)
423   throws IOException, ConnectException {
424     updateLocation(catalogTracker, regionInfo, sn, openSeqNum);
425   }
426 
427   /**
428    * Updates the location of the specified region in hbase:meta to be the specified
429    * server hostname and startcode.
430    * <p>
431    * Uses passed catalog tracker to get a connection to the server hosting
432    * hbase:meta and makes edits to that region.
433    *
434    * @param catalogTracker catalog tracker
435    * @param regionInfo region to update location of
436    * @param sn Server name
437    * @throws IOException
438    */
439   public static void updateRegionLocation(CatalogTracker catalogTracker,
440       HRegionInfo regionInfo, ServerName sn, long updateSeqNum)
441   throws IOException {
442     updateLocation(catalogTracker, regionInfo, sn, updateSeqNum);
443   }
444 
445   /**
446    * Updates the location of the specified region to be the specified server.
447    * <p>
448    * Connects to the specified server which should be hosting the specified
449    * catalog region name to perform the edit.
450    *
451    * @param catalogTracker
452    * @param regionInfo region to update location of
453    * @param sn Server name
454    * @param openSeqNum the latest sequence number obtained when the region was open
455    * @throws IOException In particular could throw {@link java.net.ConnectException}
456    * if the server is down on other end.
457    */
458   private static void updateLocation(final CatalogTracker catalogTracker,
459       HRegionInfo regionInfo, ServerName sn, long openSeqNum)
460   throws IOException {
461     Put put = new Put(regionInfo.getRegionName());
462     addLocation(put, sn, openSeqNum);
463     putToCatalogTable(catalogTracker, put);
464     LOG.info("Updated row " + regionInfo.getRegionNameAsString() +
465       " with server=" + sn);
466   }
467 
468   /**
469    * Deletes the specified region from META.
470    * @param catalogTracker
471    * @param regionInfo region to be deleted from META
472    * @throws IOException
473    */
474   public static void deleteRegion(CatalogTracker catalogTracker,
475       HRegionInfo regionInfo)
476   throws IOException {
477     Delete delete = new Delete(regionInfo.getRegionName());
478     deleteFromMetaTable(catalogTracker, delete);
479     LOG.info("Deleted " + regionInfo.getRegionNameAsString());
480   }
481 
482   /**
483    * Deletes the specified regions from META.
484    * @param catalogTracker
485    * @param regionsInfo list of regions to be deleted from META
486    * @throws IOException
487    */
488   public static void deleteRegions(CatalogTracker catalogTracker,
489       List<HRegionInfo> regionsInfo) throws IOException {
490     List<Delete> deletes = new ArrayList<Delete>(regionsInfo.size());
491     for (HRegionInfo hri: regionsInfo) {
492       deletes.add(new Delete(hri.getRegionName()));
493     }
494     deleteFromMetaTable(catalogTracker, deletes);
495     LOG.info("Deleted " + regionsInfo);
496   }
497 
498   /**
499    * Adds and Removes the specified regions from hbase:meta
500    * @param catalogTracker
501    * @param regionsToRemove list of regions to be deleted from META
502    * @param regionsToAdd list of regions to be added to META
503    * @throws IOException
504    */
505   public static void mutateRegions(CatalogTracker catalogTracker,
506       final List<HRegionInfo> regionsToRemove, final List<HRegionInfo> regionsToAdd)
507       throws IOException {
508     List<Mutation> mutation = new ArrayList<Mutation>();
509     if (regionsToRemove != null) {
510       for (HRegionInfo hri: regionsToRemove) {
511         mutation.add(new Delete(hri.getRegionName()));
512       }
513     }
514     if (regionsToAdd != null) {
515       for (HRegionInfo hri: regionsToAdd) {
516         mutation.add(makePutFromRegionInfo(hri));
517       }
518     }
519     mutateMetaTable(catalogTracker, mutation);
520     if (regionsToRemove != null && regionsToRemove.size() > 0) {
521       LOG.debug("Deleted " + regionsToRemove);
522     }
523     if (regionsToAdd != null && regionsToAdd.size() > 0) {
524       LOG.debug("Added " + regionsToAdd);
525     }
526   }
527 
528   /**
529    * Overwrites the specified regions from hbase:meta
530    * @param catalogTracker
531    * @param regionInfos list of regions to be added to META
532    * @throws IOException
533    */
534   public static void overwriteRegions(CatalogTracker catalogTracker,
535       List<HRegionInfo> regionInfos) throws IOException {
536     deleteRegions(catalogTracker, regionInfos);
537     // Why sleep? This is the easiest way to ensure that the previous deletes does not
538     // eclipse the following puts, that might happen in the same ts from the server.
539     // See HBASE-9906, and HBASE-9879. Once either HBASE-9879, HBASE-8770 is fixed,
540     // or HBASE-9905 is fixed and meta uses seqIds, we do not need the sleep.
541     Threads.sleep(20);
542     addRegionsToMeta(catalogTracker, regionInfos);
543     LOG.info("Overwritten " + regionInfos);
544   }
545 
546   /**
547    * Deletes merge qualifiers for the specified merged region.
548    * @param catalogTracker
549    * @param mergedRegion
550    * @throws IOException
551    */
552   public static void deleteMergeQualifiers(CatalogTracker catalogTracker,
553       final HRegionInfo mergedRegion) throws IOException {
554     Delete delete = new Delete(mergedRegion.getRegionName());
555     delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.MERGEA_QUALIFIER);
556     delete.deleteColumns(HConstants.CATALOG_FAMILY, HConstants.MERGEB_QUALIFIER);
557     deleteFromMetaTable(catalogTracker, delete);
558     LOG.info("Deleted references in merged region "
559         + mergedRegion.getRegionNameAsString() + ", qualifier="
560         + Bytes.toStringBinary(HConstants.MERGEA_QUALIFIER) + " and qualifier="
561         + Bytes.toStringBinary(HConstants.MERGEB_QUALIFIER));
562   }
563 
564   private static Put addRegionInfo(final Put p, final HRegionInfo hri)
565   throws IOException {
566     p.addImmutable(HConstants.CATALOG_FAMILY, HConstants.REGIONINFO_QUALIFIER,
567         hri.toByteArray());
568     return p;
569   }
570 
571   private static Put addLocation(final Put p, final ServerName sn, long openSeqNum) {
572     p.addImmutable(HConstants.CATALOG_FAMILY, HConstants.SERVER_QUALIFIER,
573       Bytes.toBytes(sn.getHostAndPort()));
574     p.addImmutable(HConstants.CATALOG_FAMILY, HConstants.STARTCODE_QUALIFIER,
575       Bytes.toBytes(sn.getStartcode()));
576     p.addImmutable(HConstants.CATALOG_FAMILY, HConstants.SEQNUM_QUALIFIER,
577         Bytes.toBytes(openSeqNum));
578     return p;
579   }
580 }