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