View Javadoc

1   /**
2    * Copyright 2011 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.IOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  import java.util.concurrent.ExecutorService;
26  
27  import org.apache.commons.logging.Log;
28  import org.apache.commons.logging.LogFactory;
29  import org.apache.hadoop.hbase.HRegionInfo;
30  import org.apache.hadoop.hbase.Server;
31  import org.apache.hadoop.hbase.ServerName;
32  import org.apache.hadoop.hbase.TableNotDisabledException;
33  import org.apache.hadoop.hbase.TableNotFoundException;
34  import org.apache.hadoop.hbase.catalog.CatalogTracker;
35  import org.apache.hadoop.hbase.catalog.MetaReader;
36  import org.apache.hadoop.hbase.executor.EventHandler;
37  import org.apache.hadoop.hbase.master.AssignmentManager;
38  import org.apache.hadoop.hbase.master.BulkAssigner;
39  import org.apache.hadoop.hbase.master.HMaster;
40  import org.apache.hadoop.hbase.master.RegionPlan;
41  import org.apache.hadoop.hbase.master.ServerManager;
42  import org.apache.hadoop.hbase.util.Bytes;
43  import org.apache.hadoop.hbase.util.Pair;
44  import org.apache.zookeeper.KeeperException;
45  
46  /**
47   * Handler to run enable of a table.
48   */
49  public class EnableTableHandler extends EventHandler {
50    private static final Log LOG = LogFactory.getLog(EnableTableHandler.class);
51    private final byte [] tableName;
52    private final String tableNameStr;
53    private final AssignmentManager assignmentManager;
54    private final CatalogTracker ct;
55    private boolean skipTableStateCheck = false;
56  
57    public EnableTableHandler(Server server, byte [] tableName,
58        CatalogTracker catalogTracker, AssignmentManager assignmentManager,
59        boolean skipTableStateCheck)
60    throws TableNotFoundException, TableNotDisabledException, IOException {
61      super(server, EventType.C_M_ENABLE_TABLE);
62      this.tableName = tableName;
63      this.tableNameStr = Bytes.toString(tableName);
64      this.ct = catalogTracker;
65      this.assignmentManager = assignmentManager;
66      this.skipTableStateCheck = skipTableStateCheck;
67      // Check if table exists
68      if (!MetaReader.tableExists(catalogTracker, this.tableNameStr)) {
69        // skipTableStateCheck is true only during recovery. In normal case it is
70        // false
71        if (!this.skipTableStateCheck) {
72          throw new TableNotFoundException(tableNameStr);
73        }
74        try {
75          this.assignmentManager.getZKTable().removeEnablingTable(tableNameStr, true);
76        } catch (KeeperException e) {
77          // TODO : Use HBCK to clear such nodes
78          LOG.warn("Failed to delete the ENABLING node for the table " + tableNameStr
79              + ".  The table will remain unusable. Run HBCK to manually fix the problem.");
80        }
81      }
82  
83      // There could be multiple client requests trying to disable or enable
84      // the table at the same time. Ensure only the first request is honored
85      // After that, no other requests can be accepted until the table reaches
86      // DISABLED or ENABLED.
87      if (!skipTableStateCheck)
88      {
89        try {
90          if (!this.assignmentManager.getZKTable().checkDisabledAndSetEnablingTable
91            (this.tableNameStr)) {
92            LOG.info("Table " + tableNameStr + " isn't disabled; skipping enable");
93            throw new TableNotDisabledException(this.tableNameStr);
94          }
95        } catch (KeeperException e) {
96          throw new IOException("Unable to ensure that the table will be" +
97            " enabling because of a ZooKeeper issue", e);
98        }
99      }
100   }
101 
102   @Override
103   public String toString() {
104     String name = "UnknownServerName";
105     if(server != null && server.getServerName() != null) {
106       name = server.getServerName().toString();
107     }
108     return getClass().getSimpleName() + "-" + name + "-" + getSeqid() + "-" +
109       tableNameStr;
110   }
111 
112   @Override
113   public void process() {
114     try {
115       LOG.info("Attemping to enable the table " + this.tableNameStr);
116       handleEnableTable();
117     } catch (IOException e) {
118       LOG.error("Error trying to enable the table " + this.tableNameStr, e);
119     } catch (KeeperException e) {
120       LOG.error("Error trying to enable the table " + this.tableNameStr, e);
121     } catch (InterruptedException e) {
122       LOG.error("Error trying to enable the table " + this.tableNameStr, e);
123     }
124   }
125 
126   private void handleEnableTable() throws IOException, KeeperException, InterruptedException {
127     // I could check table is disabling and if so, not enable but require
128     // that user first finish disabling but that might be obnoxious.
129 
130     // Set table enabling flag up in zk.
131     this.assignmentManager.getZKTable().setEnablingTable(this.tableNameStr);
132     boolean done = false;
133     // Get the regions of this table. We're done when all listed
134     // tables are onlined.
135     List<Pair<HRegionInfo, ServerName>> tableRegionsAndLocations = MetaReader
136         .getTableRegionsAndLocations(this.ct, tableName, true);
137     int countOfRegionsInTable = tableRegionsAndLocations.size();
138     List<HRegionInfo> regions = regionsToAssignWithServerName(tableRegionsAndLocations);
139     int regionsCount = regions.size();
140     if (regionsCount == 0) {
141       done = true;
142     }
143     LOG.info("Table has " + countOfRegionsInTable + " regions of which " +
144       regionsCount + " are offline.");
145     BulkEnabler bd = new BulkEnabler(this.server, regions, countOfRegionsInTable, true);
146     try {
147       if (bd.bulkAssign()) {
148         done = true;
149       }
150     } catch (InterruptedException e) {
151       LOG.warn("Enable was interrupted");
152       // Preserve the interrupt.
153       Thread.currentThread().interrupt();
154     }
155     // Flip the table to enabled.
156     if (done) this.assignmentManager.getZKTable().setEnabledTable(
157       this.tableNameStr);
158     LOG.info("Enabled table is done=" + done);
159   }
160 
161   /**
162    * @param regionsInMeta This datastructure is edited by this method.
163    * @return List of regions neither in transition nor assigned.
164    * @throws IOException
165    */
166   private List<HRegionInfo> regionsToAssignWithServerName(
167       final List<Pair<HRegionInfo, ServerName>> regionsInMeta) throws IOException {
168     ServerManager serverManager = ((HMaster) this.server).getServerManager();
169     List<HRegionInfo> regions = new ArrayList<HRegionInfo>();
170     List<HRegionInfo> enablingTableRegions = this.assignmentManager
171         .getEnablingTableRegions(this.tableNameStr);
172     final List<HRegionInfo> onlineRegions = this.assignmentManager.getRegionsOfTable(tableName);
173     for (Pair<HRegionInfo, ServerName> regionLocation : regionsInMeta) {
174       HRegionInfo hri = regionLocation.getFirst();
175       ServerName sn = regionLocation.getSecond();
176       if (this.skipTableStateCheck) {
177         // Region may be available in enablingTableRegions during master startup only.
178         if (enablingTableRegions != null && enablingTableRegions.contains(hri)) {
179           regions.add(hri);
180           if (sn != null && serverManager.isServerOnline(sn)) {
181             this.assignmentManager.addPlan(hri.getEncodedName(), new RegionPlan(hri, null, sn));
182           }
183         }
184       } else if (onlineRegions.contains(hri)) {
185         continue;
186       } else {
187         regions.add(hri);
188         if (sn != null && serverManager.isServerOnline(sn)) {
189           this.assignmentManager.addPlan(hri.getEncodedName(), new RegionPlan(hri, null, sn));
190         }
191       }
192     }
193     return regions;
194   }
195 
196   /**
197    * Run bulk enable.
198    */
199   class BulkEnabler extends BulkAssigner {
200     private final List<HRegionInfo> regions;
201     // Count of regions in table at time this assign was launched.
202     private final int countOfRegionsInTable;
203 
204     BulkEnabler(final Server server, final List<HRegionInfo> regions,
205         final int countOfRegionsInTable,final boolean retainAssignment) {
206       super(server);
207       this.regions = regions;
208       this.countOfRegionsInTable = countOfRegionsInTable;
209     }
210 
211     @Override
212     protected void populatePool(ExecutorService pool) throws IOException {
213       for (HRegionInfo region : regions) {
214         if (assignmentManager.isRegionInTransition(region) != null) {
215           continue;
216         }
217         final HRegionInfo hri = region;
218         pool.execute(new Runnable() {
219           public void run() {
220             assignmentManager.assign(hri, true, false, false);
221           }
222         });
223       }
224     }
225 
226     @Override
227     protected boolean waitUntilDone(long timeout)
228     throws InterruptedException {
229       long startTime = System.currentTimeMillis();
230       long remaining = timeout;
231       List<HRegionInfo> regions = null;
232       int lastNumberOfRegions = 0;
233       while (!server.isStopped() && remaining > 0) {
234         Thread.sleep(waitingTimeForEvents);
235         regions = assignmentManager.getRegionsOfTable(tableName);
236         if (isDone(regions)) break;
237 
238         // Punt on the timeout as long we make progress
239         if (regions.size() > lastNumberOfRegions) {
240           lastNumberOfRegions = regions.size();
241           timeout += waitingTimeForEvents;
242         }
243         remaining = timeout - (System.currentTimeMillis() - startTime);
244       }
245       return isDone(regions);
246     }
247 
248     private boolean isDone(final List<HRegionInfo> regions) {
249       return regions != null && regions.size() >= this.countOfRegionsInTable;
250     }
251   }
252 }