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.CoordinatedStateException;
29  import org.apache.hadoop.hbase.TableName;
30  import org.apache.hadoop.hbase.HRegionInfo;
31  import org.apache.hadoop.hbase.Server;
32  import org.apache.hadoop.hbase.TableNotEnabledException;
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.constraint.ConstraintException;
37  import org.apache.hadoop.hbase.executor.EventHandler;
38  import org.apache.hadoop.hbase.executor.EventType;
39  import org.apache.hadoop.hbase.master.AssignmentManager;
40  import org.apache.hadoop.hbase.master.BulkAssigner;
41  import org.apache.hadoop.hbase.master.HMaster;
42  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
43  import org.apache.hadoop.hbase.master.RegionStates;
44  import org.apache.hadoop.hbase.master.TableLockManager;
45  import org.apache.hadoop.hbase.master.RegionState.State;
46  import org.apache.hadoop.hbase.master.TableLockManager.TableLock;
47  import org.apache.hadoop.hbase.protobuf.generated.ZooKeeperProtos;
48  import org.htrace.Trace;
49  
50  /**
51   * Handler to run disable of a table.
52   */
53  @InterfaceAudience.Private
54  public class DisableTableHandler extends EventHandler {
55    private static final Log LOG = LogFactory.getLog(DisableTableHandler.class);
56    private final TableName tableName;
57    private final AssignmentManager assignmentManager;
58    private final TableLockManager tableLockManager;
59    private final CatalogTracker catalogTracker;
60    private final boolean skipTableStateCheck;
61    private TableLock tableLock;
62  
63    public DisableTableHandler(Server server, TableName tableName,
64        CatalogTracker catalogTracker, AssignmentManager assignmentManager,
65        TableLockManager tableLockManager, boolean skipTableStateCheck) {
66      super(server, EventType.C_M_DISABLE_TABLE);
67      this.tableName = tableName;
68      this.assignmentManager = assignmentManager;
69      this.catalogTracker = catalogTracker;
70      this.tableLockManager = tableLockManager;
71      this.skipTableStateCheck = skipTableStateCheck;
72    }
73  
74    public DisableTableHandler prepare()
75        throws TableNotFoundException, TableNotEnabledException, IOException {
76      if(tableName.equals(TableName.META_TABLE_NAME)) {
77        throw new ConstraintException("Cannot disable catalog table");
78      }
79      //acquire the table write lock, blocking
80      this.tableLock = this.tableLockManager.writeLock(tableName,
81          EventType.C_M_DISABLE_TABLE.toString());
82      this.tableLock.acquire();
83  
84      boolean success = false;
85      try {
86        // Check if table exists
87        if (!MetaReader.tableExists(catalogTracker, tableName)) {
88          throw new TableNotFoundException(tableName);
89        }
90  
91        // There could be multiple client requests trying to disable or enable
92        // the table at the same time. Ensure only the first request is honored
93        // After that, no other requests can be accepted until the table reaches
94        // DISABLED or ENABLED.
95        //TODO: reevaluate this since we have table locks now
96        if (!skipTableStateCheck) {
97          try {
98            if (!this.assignmentManager.getTableStateManager().setTableStateIfInStates(
99              this.tableName, ZooKeeperProtos.Table.State.DISABLING,
100             ZooKeeperProtos.Table.State.ENABLED)) {
101             LOG.info("Table " + tableName + " isn't enabled; skipping disable");
102             throw new TableNotEnabledException(this.tableName);
103           }
104         } catch (CoordinatedStateException e) {
105           throw new IOException("Unable to ensure that the table will be" +
106             " disabling because of a coordination engine issue", e);
107         }
108       }
109       success = true;
110     } finally {
111       if (!success) {
112         releaseTableLock();
113       }
114     }
115 
116     return this;
117   }
118 
119   @Override
120   public String toString() {
121     String name = "UnknownServerName";
122     if(server != null && server.getServerName() != null) {
123       name = server.getServerName().toString();
124     }
125     return getClass().getSimpleName() + "-" + name + "-" + getSeqid() + "-" +
126         tableName;
127   }
128 
129   @Override
130   public void process() {
131     try {
132       LOG.info("Attempting to disable table " + this.tableName);
133       MasterCoprocessorHost cpHost = ((HMaster) this.server)
134           .getMasterCoprocessorHost();
135       if (cpHost != null) {
136         cpHost.preDisableTableHandler(this.tableName);
137       }
138       handleDisableTable();
139       if (cpHost != null) {
140         cpHost.postDisableTableHandler(this.tableName);
141       }
142     } catch (IOException e) {
143       LOG.error("Error trying to disable table " + this.tableName, e);
144     } catch (CoordinatedStateException e) {
145       LOG.error("Error trying to disable table " + this.tableName, e);
146     } finally {
147       releaseTableLock();
148     }
149   }
150 
151   private void releaseTableLock() {
152     if (this.tableLock != null) {
153       try {
154         this.tableLock.release();
155       } catch (IOException ex) {
156         LOG.warn("Could not release the table lock", ex);
157       }
158     }
159   }
160 
161   private void handleDisableTable() throws IOException, CoordinatedStateException {
162     // Set table disabling flag up in zk.
163     this.assignmentManager.getTableStateManager().setTableState(this.tableName,
164       ZooKeeperProtos.Table.State.DISABLING);
165     boolean done = false;
166     while (true) {
167       // Get list of online regions that are of this table.  Regions that are
168       // already closed will not be included in this list; i.e. the returned
169       // list is not ALL regions in a table, its all online regions according
170       // to the in-memory state on this master.
171       final List<HRegionInfo> regions = this.assignmentManager
172         .getRegionStates().getRegionsOfTable(tableName);
173       if (regions.size() == 0) {
174         done = true;
175         break;
176       }
177       LOG.info("Offlining " + regions.size() + " regions.");
178       BulkDisabler bd = new BulkDisabler(this.server, regions);
179       try {
180         if (bd.bulkAssign()) {
181           done = true;
182           break;
183         }
184       } catch (InterruptedException e) {
185         LOG.warn("Disable was interrupted");
186         // Preserve the interrupt.
187         Thread.currentThread().interrupt();
188         break;
189       }
190     }
191     // Flip the table to disabled if success.
192     if (done) this.assignmentManager.getTableStateManager().setTableState(this.tableName,
193       ZooKeeperProtos.Table.State.DISABLED);
194     LOG.info("Disabled table, " + this.tableName + ", is done=" + done);
195   }
196 
197   /**
198    * Run bulk disable.
199    */
200   class BulkDisabler extends BulkAssigner {
201     private final List<HRegionInfo> regions;
202 
203     BulkDisabler(final Server server, final List<HRegionInfo> regions) {
204       super(server);
205       this.regions = regions;
206     }
207 
208     @Override
209     protected void populatePool(ExecutorService pool) {
210       RegionStates regionStates = assignmentManager.getRegionStates();
211       for (HRegionInfo region: regions) {
212         if (regionStates.isRegionInTransition(region)
213             && !regionStates.isRegionInState(region, State.FAILED_CLOSE)) {
214           continue;
215         }
216         final HRegionInfo hri = region;
217         pool.execute(Trace.wrap("DisableTableHandler.BulkDisabler",new Runnable() {
218           public void run() {
219             assignmentManager.unassign(hri, true);
220           }
221         }));
222       }
223     }
224 
225     @Override
226     protected boolean waitUntilDone(long timeout)
227     throws InterruptedException {
228       long startTime = System.currentTimeMillis();
229       long remaining = timeout;
230       List<HRegionInfo> regions = null;
231       while (!server.isStopped() && remaining > 0) {
232         Thread.sleep(waitingTimeForEvents);
233         regions = assignmentManager.getRegionStates().getRegionsOfTable(tableName);
234         LOG.debug("Disable waiting until done; " + remaining + " ms remaining; " + regions);
235         if (regions.isEmpty()) break;
236         remaining = timeout - (System.currentTimeMillis() - startTime);
237       }
238       return regions != null && regions.isEmpty();
239     }
240   }
241 }