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.io.InterruptedIOException;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.apache.hadoop.fs.FileSystem;
29  import org.apache.hadoop.fs.Path;
30  import org.apache.hadoop.hbase.CoordinatedStateException;
31  import org.apache.hadoop.hbase.HRegionInfo;
32  import org.apache.hadoop.hbase.HTableDescriptor;
33  import org.apache.hadoop.hbase.MetaTableAccessor;
34  import org.apache.hadoop.hbase.Server;
35  import org.apache.hadoop.hbase.TableName;
36  import org.apache.hadoop.hbase.backup.HFileArchiver;
37  import org.apache.hadoop.hbase.classification.InterfaceAudience;
38  import org.apache.hadoop.hbase.client.ClusterConnection;
39  import org.apache.hadoop.hbase.client.Delete;
40  import org.apache.hadoop.hbase.client.Result;
41  import org.apache.hadoop.hbase.client.ResultScanner;
42  import org.apache.hadoop.hbase.client.Scan;
43  import org.apache.hadoop.hbase.client.Table;
44  import org.apache.hadoop.hbase.executor.EventType;
45  import org.apache.hadoop.hbase.master.AssignmentManager;
46  import org.apache.hadoop.hbase.master.HMaster;
47  import org.apache.hadoop.hbase.master.MasterCoprocessorHost;
48  import org.apache.hadoop.hbase.master.MasterFileSystem;
49  import org.apache.hadoop.hbase.master.MasterServices;
50  import org.apache.hadoop.hbase.master.RegionState.State;
51  import org.apache.hadoop.hbase.master.RegionStates;
52  import org.apache.hadoop.hbase.regionserver.HRegion;
53  
54  @InterfaceAudience.Private
55  public class DeleteTableHandler extends TableEventHandler {
56    private static final Log LOG = LogFactory.getLog(DeleteTableHandler.class);
57  
58    protected HTableDescriptor hTableDescriptor = null;
59  
60    public DeleteTableHandler(TableName tableName, Server server,
61        final MasterServices masterServices) {
62      super(EventType.C_M_DELETE_TABLE, tableName, server, masterServices);
63    }
64  
65    @Override
66    protected void prepareWithTableLock() throws IOException {
67      // The next call fails if no such table.
68      hTableDescriptor = getTableDescriptor().getHTableDescriptor();
69    }
70  
71    protected void waitRegionInTransition(final List<HRegionInfo> regions)
72        throws IOException, CoordinatedStateException {
73      AssignmentManager am = this.masterServices.getAssignmentManager();
74      RegionStates states = am.getRegionStates();
75      long waitTime = server.getConfiguration().
76        getLong("hbase.master.wait.on.region", 5 * 60 * 1000);
77      for (HRegionInfo region : regions) {
78        long done = System.currentTimeMillis() + waitTime;
79        while (System.currentTimeMillis() < done) {
80          if (states.isRegionInState(region, State.FAILED_OPEN)) {
81            am.regionOffline(region);
82          }
83          if (!states.isRegionInTransition(region)) break;
84          try {
85            Thread.sleep(waitingTimeForEvents);
86          } catch (InterruptedException e) {
87            LOG.warn("Interrupted while sleeping");
88            throw (InterruptedIOException)new InterruptedIOException().initCause(e);
89          }
90          LOG.debug("Waiting on region to clear regions in transition; "
91            + am.getRegionStates().getRegionTransitionState(region));
92        }
93        if (states.isRegionInTransition(region)) {
94          throw new IOException("Waited hbase.master.wait.on.region (" +
95            waitTime + "ms) for region to leave region " +
96            region.getRegionNameAsString() + " in transitions");
97        }
98      }
99    }
100 
101   @Override
102   protected void handleTableOperation(List<HRegionInfo> regions)
103       throws IOException, CoordinatedStateException {
104     MasterCoprocessorHost cpHost = ((HMaster) this.server).getMasterCoprocessorHost();
105     if (cpHost != null) {
106       cpHost.preDeleteTableHandler(this.tableName);
107     }
108 
109     // 1. Wait because of region in transition
110     waitRegionInTransition(regions);
111 
112       // 2. Remove table from hbase:meta and HDFS
113     removeTableData(regions);
114 
115     if (cpHost != null) {
116       cpHost.postDeleteTableHandler(this.tableName);
117     }
118     ((HMaster) this.server).getMasterQuotaManager().removeTableFromNamespaceQuota(tableName);
119   }
120 
121   private void cleanupTableState() throws IOException {
122     // 3. Update table descriptor cache
123     LOG.debug("Removing '" + tableName + "' descriptor.");
124     this.masterServices.getTableDescriptors().remove(tableName);
125 
126     AssignmentManager am = this.masterServices.getAssignmentManager();
127 
128     // 4. Clean up regions of the table in RegionStates.
129     LOG.debug("Removing '" + tableName + "' from region states.");
130     am.getRegionStates().tableDeleted(tableName);
131 
132 
133     // 5.Clean any remaining rows for this table.
134     cleanAnyRemainingRows();
135 
136     // 6. If entry for this table states, remove it.
137     LOG.debug("Marking '" + tableName + "' as deleted.");
138     am.getTableStateManager().setDeletedTable(tableName);
139   }
140 
141   /**
142    * There may be items for this table still up in hbase:meta in the case where the
143    * info:regioninfo column was empty because of some write error. Remove ALL rows from hbase:meta
144    * that have to do with this table. See HBASE-12980.
145    * @throws IOException
146    */
147   private void cleanAnyRemainingRows() throws IOException {
148     ClusterConnection connection = this.masterServices.getConnection();
149     Scan tableScan = MetaTableAccessor.getScanForTableName(connection, tableName);
150     try (Table metaTable =
151         connection.getTable(TableName.META_TABLE_NAME)) {
152       List<Delete> deletes = new ArrayList<Delete>();
153       try (ResultScanner resScanner = metaTable.getScanner(tableScan)) {
154         for (Result result : resScanner) {
155           deletes.add(new Delete(result.getRow()));
156         }
157       }
158       if (!deletes.isEmpty()) {
159         LOG.warn("Deleting some vestigal " + deletes.size() + " rows of " + this.tableName +
160           " from " + TableName.META_TABLE_NAME);
161         if (LOG.isDebugEnabled()) {
162           for (Delete d: deletes) LOG.debug("Purging " + d);
163         }
164         metaTable.delete(deletes);
165       }
166     }
167   }
168 
169   /**
170    * Removes the table from hbase:meta and archives the HDFS files.
171    */
172   protected void removeTableData(final List<HRegionInfo> regions)
173       throws IOException, CoordinatedStateException {
174     try {
175       // 1. Remove regions from META
176       LOG.debug("Deleting regions from META");
177       MetaTableAccessor.deleteRegions(this.server.getConnection(), regions);
178 
179       // -----------------------------------------------------------------------
180       // NOTE: At this point we still have data on disk, but nothing in hbase:meta
181       //       if the rename below fails, hbck will report an inconsistency.
182       // -----------------------------------------------------------------------
183 
184       // 2. Move the table in /hbase/.tmp
185       MasterFileSystem mfs = this.masterServices.getMasterFileSystem();
186       Path tempTableDir = mfs.moveTableToTemp(tableName);
187 
188       // 3. Archive regions from FS (temp directory)
189       FileSystem fs = mfs.getFileSystem();
190       for (HRegionInfo hri : regions) {
191         LOG.debug("Archiving region " + hri.getRegionNameAsString() + " from FS");
192         HFileArchiver.archiveRegion(fs, mfs.getRootDir(),
193             tempTableDir, HRegion.getRegionDir(tempTableDir, hri.getEncodedName()));
194       }
195 
196       // 4. Delete table directory from FS (temp directory)
197       if (!fs.delete(tempTableDir, true)) {
198         LOG.error("Couldn't delete " + tempTableDir);
199       }
200 
201       LOG.debug("Table '" + tableName + "' archived!");
202     } finally {
203       cleanupTableState();
204     }
205   }
206 
207   @Override
208   protected void releaseTableLock() {
209     super.releaseTableLock();
210     try {
211       masterServices.getTableLockManager().tableDeleted(tableName);
212     } catch (IOException ex) {
213       LOG.warn("Received exception from TableLockManager.tableDeleted:", ex); //not critical
214     }
215   }
216 
217   @Override
218   public String toString() {
219     String name = "UnknownServerName";
220     if(server != null && server.getServerName() != null) {
221       name = server.getServerName().toString();
222     }
223     return getClass().getSimpleName() + "-" + name + "-" + getSeqid() + "-" + tableName;
224   }
225 }