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.master.handler;
21  
22  import java.io.FileNotFoundException;
23  import java.io.IOException;
24  import java.util.ArrayList;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.NavigableMap;
28  import java.util.TreeMap;
29  
30  import org.apache.commons.logging.Log;
31  import org.apache.commons.logging.LogFactory;
32  import org.apache.hadoop.hbase.HRegionInfo;
33  import org.apache.hadoop.hbase.HTableDescriptor;
34  import org.apache.hadoop.hbase.InvalidFamilyOperationException;
35  import org.apache.hadoop.hbase.Server;
36  import org.apache.hadoop.hbase.ServerName;
37  import org.apache.hadoop.hbase.TableExistsException;
38  import org.apache.hadoop.hbase.TableNotDisabledException;
39  import org.apache.hadoop.hbase.catalog.MetaReader;
40  import org.apache.hadoop.hbase.client.HTable;
41  import org.apache.hadoop.hbase.executor.EventHandler;
42  import org.apache.hadoop.hbase.master.BulkReOpen;
43  import org.apache.hadoop.hbase.master.MasterServices;
44  import org.apache.hadoop.hbase.util.Bytes;
45  import org.apache.zookeeper.KeeperException;
46  
47  import com.google.common.collect.Lists;
48  import com.google.common.collect.Maps;
49  
50  /**
51   * Base class for performing operations against tables.
52   * Checks on whether the process can go forward are done in constructor rather
53   * than later on in {@link #process()}.  The idea is to fail fast rather than
54   * later down in an async invocation of {@link #process()} (which currently has
55   * no means of reporting back issues once started).
56   */
57  public abstract class TableEventHandler extends EventHandler {
58    private static final Log LOG = LogFactory.getLog(TableEventHandler.class);
59    protected final MasterServices masterServices;
60    protected final byte [] tableName;
61    protected final String tableNameStr;
62  
63    public TableEventHandler(EventType eventType, byte [] tableName, Server server,
64        MasterServices masterServices)
65    throws IOException {
66      super(server, eventType);
67      this.masterServices = masterServices;
68      this.tableName = tableName;
69      try {
70        this.masterServices.checkTableModifiable(tableName);
71      } catch (TableNotDisabledException ex)  {
72        if (isOnlineSchemaChangeAllowed()
73            && eventType.isOnlineSchemaChangeSupported()) {
74          LOG.debug("Ignoring table not disabled exception " +
75              "for supporting online schema changes.");
76        }	else {
77          throw ex;
78        }
79      }
80      this.tableNameStr = Bytes.toString(this.tableName);
81    }
82  
83    private boolean isOnlineSchemaChangeAllowed() {
84      return this.server.getConfiguration().getBoolean(
85          "hbase.online.schema.update.enable", false);
86    }
87  
88    @Override
89    public void process() {
90      try {
91        LOG.info("Handling table operation " + eventType + " on table " +
92            Bytes.toString(tableName));
93        List<HRegionInfo> hris =
94          MetaReader.getTableRegions(this.server.getCatalogTracker(),
95            tableName);
96        handleTableOperation(hris);
97        if (eventType.isOnlineSchemaChangeSupported() && this.masterServices.
98            getAssignmentManager().getZKTable().
99            isEnabledTable(Bytes.toString(tableName))) {
100         if (reOpenAllRegions(hris)) {
101           LOG.info("Completed table operation " + eventType + " on table " +
102               Bytes.toString(tableName));
103         } else {
104           LOG.warn("Error on reopening the regions");
105         }
106       }
107     } catch (IOException e) {
108       LOG.error("Error manipulating table " + Bytes.toString(tableName), e);
109     } catch (KeeperException e) {
110       LOG.error("Error manipulating table " + Bytes.toString(tableName), e);
111     }
112   }
113 
114   public boolean reOpenAllRegions(List<HRegionInfo> regions) throws IOException {
115     boolean done = false;
116     LOG.info("Bucketing regions by region server...");
117     HTable table = new HTable(masterServices.getConfiguration(), tableName);
118     TreeMap<ServerName, List<HRegionInfo>> serverToRegions = Maps
119         .newTreeMap();
120     NavigableMap<HRegionInfo, ServerName> hriHserverMapping = table.getRegionLocations();
121     List<HRegionInfo> reRegions = new ArrayList<HRegionInfo>();
122     for (HRegionInfo hri : regions) {
123       ServerName rsLocation = hriHserverMapping.get(hri);
124 
125       // Skip the offlined split parent region
126       // See HBASE-4578 for more information.
127       if (null == rsLocation) {
128         LOG.info("Skip " + hri);
129         continue;
130       }
131       if (!serverToRegions.containsKey(rsLocation)) {
132         LinkedList<HRegionInfo> hriList = Lists.newLinkedList();
133         serverToRegions.put(rsLocation, hriList);
134       }
135       reRegions.add(hri);
136       serverToRegions.get(rsLocation).add(hri);
137     }
138     
139     LOG.info("Reopening " + reRegions.size() + " regions on "
140         + serverToRegions.size() + " region servers.");
141     this.masterServices.getAssignmentManager().setRegionsToReopen(reRegions);
142     BulkReOpen bulkReopen = new BulkReOpen(this.server, serverToRegions,
143         this.masterServices.getAssignmentManager());
144     while (true) {
145       try {
146         if (bulkReopen.bulkReOpen()) {
147           done = true;
148           break;
149         } else {
150           LOG.warn("Timeout before reopening all regions");
151         }
152       } catch (InterruptedException e) {
153         LOG.warn("Reopen was interrupted");
154         // Preserve the interrupt.
155         Thread.currentThread().interrupt();
156         break;
157       }
158     }
159     return done;
160   }
161 
162   /**
163    * @return Table descriptor for this table
164    * @throws TableExistsException
165    * @throws FileNotFoundException
166    * @throws IOException
167    */
168   HTableDescriptor getTableDescriptor()
169   throws TableExistsException, FileNotFoundException, IOException {
170     final String name = Bytes.toString(tableName);
171     HTableDescriptor htd =
172       this.masterServices.getTableDescriptors().get(name);
173     if (htd == null) {
174       throw new IOException("HTableDescriptor missing for " + name);
175     }
176     return htd;
177   }
178 
179   byte [] hasColumnFamily(final HTableDescriptor htd, final byte [] cf)
180   throws InvalidFamilyOperationException {
181     if (!htd.hasFamily(cf)) {
182       throw new InvalidFamilyOperationException("Column family '" +
183         Bytes.toString(cf) + "' does not exist");
184     }
185     return cf;
186   }
187 
188   protected abstract void handleTableOperation(List<HRegionInfo> regions)
189   throws IOException, KeeperException;
190 }