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.List;
24  import java.util.concurrent.ExecutorService;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.hbase.HRegionInfo;
29  import org.apache.hadoop.hbase.Server;
30  import org.apache.hadoop.hbase.TableNotDisabledException;
31  import org.apache.hadoop.hbase.TableNotFoundException;
32  import org.apache.hadoop.hbase.catalog.CatalogTracker;
33  import org.apache.hadoop.hbase.catalog.MetaReader;
34  import org.apache.hadoop.hbase.executor.EventHandler;
35  import org.apache.hadoop.hbase.master.AssignmentManager;
36  import org.apache.hadoop.hbase.master.BulkAssigner;
37  import org.apache.hadoop.hbase.util.Bytes;
38  import org.apache.zookeeper.KeeperException;
39  
40  /**
41   * Handler to run enable of a table.
42   */
43  public class EnableTableHandler extends EventHandler {
44    private static final Log LOG = LogFactory.getLog(EnableTableHandler.class);
45    private final byte [] tableName;
46    private final String tableNameStr;
47    private final AssignmentManager assignmentManager;
48    private final CatalogTracker ct;
49  
50    public EnableTableHandler(Server server, byte [] tableName,
51        CatalogTracker catalogTracker, AssignmentManager assignmentManager,
52        boolean skipTableStateCheck)
53    throws TableNotFoundException, TableNotDisabledException, IOException {
54      super(server, EventType.C_M_ENABLE_TABLE);
55      this.tableName = tableName;
56      this.tableNameStr = Bytes.toString(tableName);
57      this.ct = catalogTracker;
58      this.assignmentManager = assignmentManager;
59      // Check if table exists
60      if (!MetaReader.tableExists(catalogTracker, this.tableNameStr)) {
61        throw new TableNotFoundException(Bytes.toString(tableName));
62      }
63  
64      // There could be multiple client requests trying to disable or enable
65      // the table at the same time. Ensure only the first request is honored
66      // After that, no other requests can be accepted until the table reaches
67      // DISABLED or ENABLED.
68      if (!skipTableStateCheck)
69      {
70        try {
71          if (!this.assignmentManager.getZKTable().checkDisabledAndSetEnablingTable
72            (this.tableNameStr)) {
73            LOG.info("Table " + tableNameStr + " isn't disabled; skipping enable");
74            throw new TableNotDisabledException(this.tableNameStr);
75          }
76        } catch (KeeperException e) {
77          throw new IOException("Unable to ensure that the table will be" +
78            " enabling because of a ZooKeeper issue", e);
79        }
80      }
81    }
82  
83    @Override
84    public String toString() {
85      String name = "UnknownServerName";
86      if(server != null && server.getServerName() != null) {
87        name = server.getServerName().toString();
88      }
89      return getClass().getSimpleName() + "-" + name + "-" + getSeqid() + "-" +
90        tableNameStr;
91    }
92  
93    @Override
94    public void process() {
95      try {
96        LOG.info("Attemping to enable the table " + this.tableNameStr);
97        handleEnableTable();
98      } catch (IOException e) {
99        LOG.error("Error trying to enable the table " + this.tableNameStr, e);
100     } catch (KeeperException e) {
101       LOG.error("Error trying to enable the table " + this.tableNameStr, e);
102     }
103   }
104 
105   private void handleEnableTable() throws IOException, KeeperException {
106     // I could check table is disabling and if so, not enable but require
107     // that user first finish disabling but that might be obnoxious.
108 
109     // Set table enabling flag up in zk.
110     this.assignmentManager.getZKTable().setEnablingTable(this.tableNameStr);
111     boolean done = false;
112     // Get the regions of this table. We're done when all listed
113     // tables are onlined.
114     List<HRegionInfo> regionsInMeta;
115     regionsInMeta = MetaReader.getTableRegions(this.ct, tableName, true);
116     int countOfRegionsInTable = regionsInMeta.size();
117     List<HRegionInfo> regions = regionsToAssign(regionsInMeta);
118     int regionsCount = regions.size();
119     if (regionsCount == 0) {
120       done = true;
121     }
122     LOG.info("Table has " + countOfRegionsInTable + " regions of which " +
123       regionsCount + " are offline.");
124     BulkEnabler bd = new BulkEnabler(this.server, regions,
125       countOfRegionsInTable);
126     try {
127       if (bd.bulkAssign()) {
128         done = true;
129       }
130     } catch (InterruptedException e) {
131       LOG.warn("Enable was interrupted");
132       // Preserve the interrupt.
133       Thread.currentThread().interrupt();
134     }
135     // Flip the table to enabled.
136     if (done) this.assignmentManager.getZKTable().setEnabledTable(
137       this.tableNameStr);
138     LOG.info("Enabled table is done=" + done);
139   }
140 
141   /**
142    * @param regionsInMeta This datastructure is edited by this method.
143    * @return The <code>regionsInMeta</code> list minus the regions that have
144    * been onlined; i.e. List of regions that need onlining.
145    * @throws IOException
146    */
147   private List<HRegionInfo> regionsToAssign(
148     final List<HRegionInfo> regionsInMeta)
149   throws IOException {
150     final List<HRegionInfo> onlineRegions =
151       this.assignmentManager.getRegionsOfTable(tableName);
152     regionsInMeta.removeAll(onlineRegions);
153     return regionsInMeta;
154   }
155 
156   /**
157    * Run bulk enable.
158    */
159   class BulkEnabler extends BulkAssigner {
160     private final List<HRegionInfo> regions;
161     // Count of regions in table at time this assign was launched.
162     private final int countOfRegionsInTable;
163 
164     BulkEnabler(final Server server, final List<HRegionInfo> regions,
165         final int countOfRegionsInTable) {
166       super(server);
167       this.regions = regions;
168       this.countOfRegionsInTable = countOfRegionsInTable;
169     }
170 
171     @Override
172     protected void populatePool(ExecutorService pool) {
173       for (HRegionInfo region: regions) {
174         if (assignmentManager.isRegionInTransition(region) != null) continue;
175         final HRegionInfo hri = region;
176         pool.execute(new Runnable() {
177           public void run() {
178             assignmentManager.assign(hri, true);
179           }
180         });
181       }
182     }
183 
184     @Override
185     protected boolean waitUntilDone(long timeout)
186     throws InterruptedException {
187       long startTime = System.currentTimeMillis();
188       long remaining = timeout;
189       List<HRegionInfo> regions = null;
190       int lastNumberOfRegions = this.countOfRegionsInTable;
191       while (!server.isStopped() && remaining > 0) {
192         Thread.sleep(waitingTimeForEvents);
193         regions = assignmentManager.getRegionsOfTable(tableName);
194         if (isDone(regions)) break;
195 
196         // Punt on the timeout as long we make progress
197         if (regions.size() > lastNumberOfRegions) {
198           lastNumberOfRegions = regions.size();
199           timeout += waitingTimeForEvents;
200         }
201         remaining = timeout - (System.currentTimeMillis() - startTime);
202       }
203       return isDone(regions);
204     }
205 
206     private boolean isDone(final List<HRegionInfo> regions) {
207       return regions != null && regions.size() >= this.countOfRegionsInTable;
208     }
209   }
210 }