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