View Javadoc

1   /**
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  package org.apache.hadoop.hbase.master.handler;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.concurrent.ExecutorService;
24  
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.hadoop.classification.InterfaceAudience;
28  import org.apache.hadoop.hbase.HRegionInfo;
29  import org.apache.hadoop.hbase.Server;
30  import org.apache.hadoop.hbase.exceptions.TableNotEnabledException;
31  import org.apache.hadoop.hbase.exceptions.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.executor.EventType;
36  import org.apache.hadoop.hbase.master.AssignmentManager;
37  import org.apache.hadoop.hbase.master.BulkAssigner;
38  import org.apache.hadoop.hbase.master.HMaster;
39  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
40  import org.apache.hadoop.hbase.master.RegionStates;
41  import org.apache.hadoop.hbase.master.TableLockManager;
42  import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
43  import org.apache.hadoop.hbase.util.Bytes;
44  import org.apache.zookeeper.KeeperException;
45  import org.cloudera.htrace.Trace;
46  
47  /**
48   * Handler to run disable of a table.
49   */
50  @InterfaceAudience.Private
51  public class DisableTableHandler extends EventHandler {
52    private static final Log LOG = LogFactory.getLog(DisableTableHandler.class);
53    private final byte [] tableName;
54    private final String tableNameStr;
55    private final AssignmentManager assignmentManager;
56    private final TableLockManager tableLockManager;
57    private final CatalogTracker catalogTracker;
58    private final boolean skipTableStateCheck;
59    private TableLock tableLock;
60  
61    public DisableTableHandler(Server server, byte [] tableName,
62        CatalogTracker catalogTracker, AssignmentManager assignmentManager,
63        TableLockManager tableLockManager, boolean skipTableStateCheck) {
64      super(server, EventType.C_M_DISABLE_TABLE);
65      this.tableName = tableName;
66      this.tableNameStr = Bytes.toString(this.tableName);
67      this.assignmentManager = assignmentManager;
68      this.catalogTracker = catalogTracker;
69      this.tableLockManager = tableLockManager;
70      this.skipTableStateCheck = skipTableStateCheck;
71    }
72  
73    public DisableTableHandler prepare()
74        throws TableNotFoundException, TableNotEnabledException, IOException {
75      //acquire the table write lock, blocking
76      this.tableLock = this.tableLockManager.writeLock(tableName,
77          EventType.C_M_DISABLE_TABLE.toString());
78      this.tableLock.acquire();
79  
80      boolean success = false;
81      try {
82        // Check if table exists
83        if (!MetaReader.tableExists(catalogTracker, this.tableNameStr)) {
84          throw new TableNotFoundException(this.tableNameStr);
85        }
86  
87        // There could be multiple client requests trying to disable or enable
88        // the table at the same time. Ensure only the first request is honored
89        // After that, no other requests can be accepted until the table reaches
90        // DISABLED or ENABLED.
91        //TODO: reevaluate this since we have table locks now
92        if (!skipTableStateCheck) {
93          try {
94            if (!this.assignmentManager.getZKTable().checkEnabledAndSetDisablingTable
95              (this.tableNameStr)) {
96              LOG.info("Table " + tableNameStr + " isn't enabled; skipping disable");
97              throw new TableNotEnabledException(this.tableNameStr);
98            }
99          } catch (KeeperException e) {
100           throw new IOException("Unable to ensure that the table will be" +
101             " disabling because of a ZooKeeper issue", e);
102         }
103       }
104       success = true;
105     } finally {
106       if (!success) {
107         releaseTableLock();
108       }
109     }
110 
111     return this;
112   }
113 
114   @Override
115   public String toString() {
116     String name = "UnknownServerName";
117     if(server != null && server.getServerName() != null) {
118       name = server.getServerName().toString();
119     }
120     return getClass().getSimpleName() + "-" + name + "-" + getSeqid() + "-" +
121       tableNameStr;
122   }
123 
124   @Override
125   public void process() {
126     try {
127       LOG.info("Attempting to disable table " + this.tableNameStr);
128       MasterCoprocessorHost cpHost = ((HMaster) this.server)
129           .getCoprocessorHost();
130       if (cpHost != null) {
131         cpHost.preDisableTableHandler(this.tableName);
132       }
133       handleDisableTable();
134       if (cpHost != null) {
135         cpHost.postDisableTableHandler(this.tableName);
136       }
137     } catch (IOException e) {
138       LOG.error("Error trying to disable table " + this.tableNameStr, e);
139     } catch (KeeperException e) {
140       LOG.error("Error trying to disable table " + this.tableNameStr, e);
141     } finally {
142       releaseTableLock();
143     }
144   }
145 
146   private void releaseTableLock() {
147     if (this.tableLock != null) {
148       try {
149         this.tableLock.release();
150       } catch (IOException ex) {
151         LOG.warn("Could not release the table lock", ex);
152       }
153     }
154   }
155 
156   private void handleDisableTable() throws IOException, KeeperException {
157     // Set table disabling flag up in zk.
158     this.assignmentManager.getZKTable().setDisablingTable(this.tableNameStr);
159     boolean done = false;
160     while (true) {
161       // Get list of online regions that are of this table.  Regions that are
162       // already closed will not be included in this list; i.e. the returned
163       // list is not ALL regions in a table, its all online regions according
164       // to the in-memory state on this master.
165       final List<HRegionInfo> regions = this.assignmentManager
166         .getRegionStates().getRegionsOfTable(tableName);
167       if (regions.size() == 0) {
168         done = true;
169         break;
170       }
171       LOG.info("Offlining " + regions.size() + " regions.");
172       BulkDisabler bd = new BulkDisabler(this.server, regions);
173       try {
174         if (bd.bulkAssign()) {
175           done = true;
176           break;
177         }
178       } catch (InterruptedException e) {
179         LOG.warn("Disable was interrupted");
180         // Preserve the interrupt.
181         Thread.currentThread().interrupt();
182         break;
183       }
184     }
185     // Flip the table to disabled if success.
186     if (done) this.assignmentManager.getZKTable().setDisabledTable(this.tableNameStr);
187     LOG.info("Disabled table is done=" + done);
188   }
189 
190   /**
191    * Run bulk disable.
192    */
193   class BulkDisabler extends BulkAssigner {
194     private final List<HRegionInfo> regions;
195 
196     BulkDisabler(final Server server, final List<HRegionInfo> regions) {
197       super(server);
198       this.regions = regions;
199     }
200 
201     @Override
202     protected void populatePool(ExecutorService pool) {
203       RegionStates regionStates = assignmentManager.getRegionStates();
204       for (HRegionInfo region: regions) {
205         if (regionStates.isRegionInTransition(region)
206             && !regionStates.isRegionFailedToClose(region)) continue;
207         final HRegionInfo hri = region;
208         pool.execute(Trace.wrap(new Runnable() {
209           public void run() {
210             assignmentManager.unassign(hri, true);
211           }
212         }));
213       }
214     }
215 
216     @Override
217     protected boolean waitUntilDone(long timeout)
218     throws InterruptedException {
219       long startTime = System.currentTimeMillis();
220       long remaining = timeout;
221       List<HRegionInfo> regions = null;
222       while (!server.isStopped() && remaining > 0) {
223         Thread.sleep(waitingTimeForEvents);
224         regions = assignmentManager.getRegionStates().getRegionsOfTable(tableName);
225         LOG.debug("Disable waiting until done; " + remaining + " ms remaining; " + regions);
226         if (regions.isEmpty()) break;
227         remaining = timeout - (System.currentTimeMillis() - startTime);
228       }
229       return regions != null && regions.isEmpty();
230     }
231   }
232 }