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